背景
今天讲下若依框架对于登录认证方面的实现,这个方面若依做的不算太好,如果项目中想用的话需要参考其他框架的实现,做的更好一些。
我建议是前后端放在一起来看,单纯看后端会比较无趣。
后端部分
/login 接口
- userName
- password
- code 验证码
前端获取上面三个要素后调用接口,整体改接口做了下面几件事情
- 验证用户身份(账号密码+验证码)
- 生成token
- 保存用户登录态到spring security中
安全配置:定义了基本的配置信息
framework.config.SecurityConfig
UserDetailsServiceImpl 用户验证处理类
登录接口的服务类
framework.web.service.SysLoginService
JWT拦截器,拦截令牌并校验信息
framework.security.filter.JwtAuthenticationTokenFilter
详细过程
- SysLoginService 中调用UserDetailsServiceImpl校验用户的密码是否匹配以及用户账户状态,校验通过后返回UserDetails实例,该实例包含了用户的基本信息和菜单权限信息
- 调用tokenService.createToken(loginUser)生成token
令牌生成的详细过程
- 生成uuid随机数,这个随机数用来做rediskey存储token
- 生成一个token(无时效)
- 拦截到的token如果距离失效在10分钟以内(可配置)就自动刷新有效期
前面提到了token本身无时效,有效期是通过redis控制的,因为jwt本身未提供刷新有效期的方法(可能是我不知道)。
以上用户调用了login接口并且获得了token
jwt令牌校验
/**
* token过滤器 验证token有效性
*
* @author sj
*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter
{
@Autowired
private TokenService tokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException
{
LoginUser loginUser = tokenService.getLoginUser(request);
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication()))
{
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
}
}
代码比较短,所以就直接贴出来,这段代码拦截了所有请求并且完成了令牌的校验和刷新,具体过程如下
- tokenService.getLoginUser(request); 从request中获取token并校验,如果校验通过就返回LoginUser对象
- 校验LoginUser的token,如果再刷限期内就直接刷新
- 将LoginUser封装到SecurityContextHolder中作为全局的用户登录状态
注:第3条有两个好处
- 后续拦截器发现SecurityContextHolder中保存了用户时,就直接通过校验
- 通过SecurityContextHolder可以快速获取当前请求的登录信息。
以上基本上已经说名了JWT校验的基本过程,忽略了很多细节,基础薄弱的同学可以需要先研究其他博客再来看这篇
getInfo 获取用户信息
- 用户的基本信息
- 用户所有的Permissions(菜单树)
- 用户所有的RopePersmission(roleKeys)
getRouters 获取前端页面路由信息
这个接口完全为前端准备,后面会专门讲述前端的权限控制