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

Token=MD5(username+分隔符+expiryTime+分隔符+password)
注意: 这种方式不推荐使用, 有严重的安全问题. 就是密码信息在前端浏览器cookie中存放. 如果cookie 被盗取很容易破解.
@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)
代码实现
@Autowired
DataSource dataSource;
/**
* 负责token与数据库之间的操作
* @return
*/
@Bean
public PersistentTokenRepository getPersistentTokenRepository(){
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource); //设置数据源
tokenRepository.setCreateTableOnStartup(true); //启动时帮助我们自动创建一张表,第一次启动设置为true,第二次启动程序的时候设置false或者注释掉;
return tokenRepository;
}
@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窃取伪造登录成功,访问接口获取数据,只需在关键接口处添加如下代码,可解决此问题。如下
/**
* 根据用户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;
}
浙公网安备 33010602011771号