原理: 用户首次登录发送请求成功通过过滤器后会被生成一个token,这个token和用户名一一对应,且会被放入Cookie与数据库中,用户在之后的登陆请求中携带的Cookiee的token会与数据库中的比对,成功的话会调用UserDetailsService获得用户的信息并返回,至此结束。
仔细想想会发现,这种比对方法在Spring security中十分常见,不去解密而是利用hash的一致性,单向加密对比,防止了由于秘钥泄露造成的安全问题。(个人理解)
使用mysql 所以不讲解内存方法
具体实现:
-
数据库中建表
CREATE TABLE persistent_logins ( username VARCHAR(64) NOT NULL, series VARCHAR(64) NOT NULL PRIMARY KEY, token VARCHAR(64) NOT NULL, last_used TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
-
注入数据源
@Autowired DataSource dataSource;
-
实现UserDetailsService
@Service public class UserSecurityImpl implements UserDetailsService { @Autowired UserService userService; @Autowired PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { com.salty.pojo.User user = userService.findByUsername(username); if(user == null) { throw new UsernameNotFoundException("用户不存在"); } //以后和数据库对接 userService.updateLastLogin(user.getId(), TimeUtil.getFormatTimeForSix()); Collection<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("normal")); User ret = new User(user.getUsername(),user.getPassword(),authorities); return ret; } }
-
注入Bean:
@Bean public PersistentTokenRepository persistentTokenRepository(){ JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl(); tokenRepository.setDataSource(dataSource); // 如果token表不存在,使用下面语句可以初始化该表;若存在,请注释掉这条语句,否则会报错。 //tokenRepository.setCreateTableOnStartup(true); return tokenRepository; }
-
在configure中开启
rememberMe()//记住我功能开启 .tokenRepository(persistentTokenRepository())//这就是之前的Bean .tokenValiditySeconds(60*60)//过期时间 单位看前面是秒 .userDetailsService(userDetailsService)//通过用户名获取登录用户信息 .and()//继续链式调用
最后还有前端的
<input name="remember-me" type="checkbox" value="true"/> 记住我
具体登录form你们自己写写 我就不贴了(国际化写的太乱了),别忘了form登录要在configure中配置
到此可以说已经完成了,登录后应该能在数据库中看到有对应的token进入了。
不用thymeleaf的可以右上角了,下面是我thymeleaf踩的坑
事情经过: 我大部分数据是在登录后的controller跳转中注入到session里面的,之后传给前端的thymeleaf使用,一开始开发的时候使用的非常方便,但是到了这里就会有一个巨大的问题,如果用户收藏了需要登录才能进入的网页,之后直接访问了该url,那么在记住我实现后的情况下不会经过loginController,也就是说前端的thymeleaf没有值注入,导致一大片error null。
解决方法:
通过别的方式注入值,或者干脆放弃thymeleaf,直接用ajax,以后还方便前后端分离
实在不死心就用SecurityContextHolder.getContext().getAuthentication().getName();去获得用户名之后去调信息吧,可以尝试使用before切片注入,也可以写在方法里,随你自己发挥