6.remember me功能

remember me功能

在大多数网站中,都会实现 Remember Me 这个功能,方便用户在下一次登录时直接登录,避免再次输入用户名以及密码去登录,Spring Security针对这个功能已经帮助我们实现,如下图:

 

 

 Token=MD5(username+分隔符+expiryTime+分隔符+password)

注意: 这种方式不推荐使用, 有严重的安全问题. 就是密码信息在前端浏览器cookie中存放. 如果cookie 被盗取很容易破解.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /** http.httpBasic() //开启httpBasic认证
         .and().authorizeRequests().anyRequest().authenticated(); //所有请求都需要认证之后访问
         */
/*        http.formLogin().loginPage("/login.html")//开启表单认证
              //  .and().authorizeRequests() //放行登录页面
            //    .anyRequest().authenticated();
             //   .and().authorizeRequests().antMatchers("/login.html").permitAll() //放行登录页面
                .and().authorizeRequests().antMatchers("/toLoginPage").permitAll() //放行登录页面
                .anyRequest().authenticated();*/
                 http.formLogin() //开启表单认证
                .loginPage("/toLoginPage") // 自定义登陆页面
                .loginProcessingUrl("/login") //表单提交路径
                .usernameParameter("username").passwordParameter("password") //自定义input额name值和password
                .successForwardUrl("/") //登录成功之后跳转的路径
                .and().authorizeRequests().antMatchers("/toLoginPage").permitAll() //放行登录页面
                .anyRequest().authenticated()
                 .and().rememberMe() //开启记住我功能
                  .tokenValiditySeconds(1209600) //token失效时间,默认失效时间是两周
                  .rememberMeParameter("remember-me") // 自定义表单name值
                 .and().headers().frameOptions().sameOrigin() //加载同源域名下iframe页面
                .and().csrf().disable();//关闭csrf防护
    }

login.html

 

 

 测试如下

 

 

 上面所说,密码信息也会存放在cookie中,容易被抓取破解,下面我们可以改造一下,基于持久化的Token生成方法,如下图

存入数据库Token包含:

token: 随机生成策略,每次访问都会重新生成

series: 登录序列号,随机生成策略。用户输入用户名和密码登录时,该值重新生成。使用 remember-me功能,该值保持不变

expiryTime: token过期时间

CookieValue=encode(series+token)

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
@Autowired
DataSource dataSource;
/**
 * 负责token与数据库之间的操作
 * @return
 */
@Bean
public PersistentTokenRepository getPersistentTokenRepository(){
    JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
    tokenRepository.setDataSource(dataSource); //设置数据源
    tokenRepository.setCreateTableOnStartup(true); //启动时帮助我们自动创建一张表,第一次启动设置为true,第二次启动程序的时候设置false或者注释掉;
    return tokenRepository;
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /** http.httpBasic() //开启httpBasic认证
         .and().authorizeRequests().anyRequest().authenticated(); //所有请求都需要认证之后访问
         */
/*        http.formLogin().loginPage("/login.html")//开启表单认证
              //  .and().authorizeRequests() //放行登录页面
            //    .anyRequest().authenticated();
             //   .and().authorizeRequests().antMatchers("/login.html").permitAll() //放行登录页面
                .and().authorizeRequests().antMatchers("/toLoginPage").permitAll() //放行登录页面
                .anyRequest().authenticated();*/
                 http.formLogin() //开启表单认证
                .loginPage("/toLoginPage") // 自定义登陆页面
                .loginProcessingUrl("/login") //表单提交路径
                .usernameParameter("username").passwordParameter("password") //自定义input额name值和password
                .successForwardUrl("/") //登录成功之后跳转的路径
                .and().authorizeRequests().antMatchers("/toLoginPage").permitAll() //放行登录页面
                .anyRequest().authenticated()
                 .and().rememberMe() //开启记住我功能
                  .tokenValiditySeconds(1209600) //token失效时间,默认失效时间是两周
                  .rememberMeParameter("remember-me") // 自定义表单name值
                  .tokenRepository(getPersistentTokenRepository()) //设置PersistentTokenRepository
                 .and().headers().frameOptions().sameOrigin() //加载同源域名下iframe页面
                .and().csrf().disable();//关闭csrf防护
    }

运行后,数据库会生成一张对应的表

 

 登陆成功之后,观察数据库,会插入一条记录.说明持久化token方式已经生效;

 

 cookie窃取伪造登录成功,访问接口获取数据,只需在关键接口处添加如下代码,可解决此问题。如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
 * 根据用户ID查询用户
 *
 * @return
 */
@GetMapping("/{id}")
@ResponseBody
public User getById(@PathVariable Integer id) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    //如果返回true,代表这个登录认证的信息来源于自动登录
    if(RememberMeAuthenticationToken.class.isAssignableFrom(authentication.getClass())){
        throw new RememberMeAuthenticationException("认证来源于remember me");
    }
    User user = userService.getById(id);
    return user;
}

 

posted @   __破  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示