SpringSecurity RememberMe功能

一、前言

如SpringSecurity在用户名密码登录的示例所示:
UsernamePasswordAuthenticationFilter的父类Filter中的 doFilter() 方法,调用用户认证的方法认证用户成功后,会调用一个名为successfulAuthentication 的方法,它内部有几大过程;

  • 1、将认证成功的信息存入SecurityContextHolder中。
  • 2、如果rememberMeServices功能开启了,处理rememberMe的逻辑。
  • 3、调用successHandler成功处理器。
    其中第二点就是今天要说的 "记住我" 的功能。它的实现逻辑如下:
rememberMe.png

二、流程梳理

1、第一次赋值流程(假设已经开启了rememberMe认证流程)

PersistentTokenBasedRememberMeServices类中,先是生成一个PersistentRememberMeToken类型的 token,并通过tokenReposority.createNewToken(token)方法存储这个token,最后将token信息存入cookie中返回前端。


这里的tokenReposority是需要我们自己配置的,SpringSecurity提前提供好了两个可供使用的类

InMemoryTokenRepositoryImpl 
顾名思义,将token存储在内存中,特点是快,但是消耗内存,用户量少的话可以使用。内部原理也很简单,就是将不同的token存储在一个HashMap里面。

@Bean
public PersistentTokenRepository persistentTokenRepository(){
    InMemoryTokenRepositoryImpl tokenRepository = new InMemoryTokenRepositoryImpl ();
    return tokenRepository;
}

JdbcTokenRepositoryImpl
这个是将token存储在数据库中的选择,下面setCreateTableOnStartup选中的ture会自动在数据库中创建一个表来存储数据(第二次启动项目记得改为false,因为表第一次启动已经创建了,第二次还是true会报错)

@Bean
public PersistentTokenRepository persistentTokenRepository(){
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setCreateTableOnStartup(true);  
    tokenRepository.setDataSource(dataSource);
    return tokenRepository;
}

其实,我们还可以自定义这个TokenRepository,只需要去实现上述说的两个类的接口PersistentTokenRepository即可。

public interface PersistentTokenRepository {
    void createNewToken(PersistentRememberMeToken token);
    void updateToken(String series, String tokenValue, Date lastUsed);
    PersistentRememberMeToken getTokenForSeries(String seriesId);
    void removeUserTokens(String username);
}


2、过滤流程

第一次我登录了之后,因为会话的关系,我们可以访问一些资源。但是当我关闭页面,在会话消失后,我们的访问一个后台资源的话,按照以往的逻辑,应该是访问不到且会跳转到登录页面。但是如果我们之前的会话有RememberMe的话,cookie中带有一个名为remember-me的信息,在通过前面几个过滤器之后,到了名为RememberMeAuthenticationFilter的过滤器中的时候,它会从request中拿取cookie信息,并尝试通过token去获取用户信息,成功获取到之后会通过UserDetailService认证,认证通过即可放行。

上面的aotuLogin()方法如下

最后,附上开启记住我功能的配置

   // tokenRepository配置
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setCreateTableOnStartup(false);
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }
    // 默认的密码加密
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    // 这个自定义即可
    @Bean
    public UserDetailsService demoUser() {
        return (username) -> new User(username, passwordEncoder().encode("123456"),
                AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()
                    .loginProcessingUrl("/testToLogin")
                    .loginPage("/needLogin")
                    .successHandler((request, response, authentication) -> {
                        PrintWriter writer = response.getWriter();
                        writer.print("强啊,成了得嘛");
                    }).failureHandler((request, response, exception) -> {
                        response.setContentType("application/json;charset=UTF-8");
                        response.getWriter().write(om.writeValueAsString("login failure"));
                    })
                .and()
                    .rememberMe()            // -----------就是这里了
                    .alwaysRemember(true)      // 最近发现新版本要多配置一个这个,否则也没有开启
                    .tokenValiditySeconds(3600)
                    .tokenRepository(persistentTokenRepository())
                    .userDetailsService(demoUser())
                .and()
                .authorizeRequests()
                .antMatchers("/skip", "/needLogin").permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356