登录校验流程
springSecurity已经为我们默认实现了一个用不着的登录功能,我们需要自己实现个符合我们需求的登录功能,所以我们需要去了解默认登录功能的流程,对其中的部分进行替换
SpringSecurity底层就是过滤器链,包含实现了各种功能的过滤器
- UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请
- 求。入门案例的认证工作主要有它负责。
- ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedException和
- AuthenticationException 。
- FilterSecurityInterceptor:负责权限校验的过滤器。
认证流程详解
- Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。
- AuthenticationManager接口:定义了认证Authentication的方法
- UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。
- UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。
解决问题
登录
- 自定义登录接口
调用ProviderManager的方法进行认证,如果认证通过,生成jwt
把用户信息存入redis
- 自定义UserDetailsService实现类
在这个实现类中完成根据username查询数据库
校验
- 定义Jwt认证过滤器
获取token,解析token中的userid
从redis中获取用户信息
如果能成功获取,将其存入SecurityContextHolder供其他过滤器使用
认证要点记录
登录部分
- 用户想要进行登录,那用户必然处于未登录状态,需要我们配置SecurittyConfig,登录接口只能匿名访问
- 当调用登录接口的时候,由我们自己在Service层调用过滤器的一部分(AuthentionManager的authenticate方法)。
- 我们应该提供自己的UserDetailService的实现类(根据用户名查询用户信息)并注入到spring容器中,以供authenticate调用的部分调用。
- 我们还应该提供PasswordEncoder类(常见的就是BC),注入到spring容器中,以供authenticate调用的部分调用进行密码校验
- authenticate方法的调用,会帮我们完成用户信息查询与密码校验,我们只需要根据返回的结果Authentication是否为空就可以知道登录是否成功
- 如果登录成功,我们从Authentication中获取userid,生成jwt,以键值对形式作为响应体返回给前端
- 同时以"login:"+id作为键,以loginUser作为值存入redis,这样当下一次前端传来token,解析出id,就可以从redis中查到用户信息。
校验部分
- 我们不能让用户每次发起请求都带上用户名密码,所以当用户第一次登录之后,我们可以让用户在一段时间内处于一个登录的状态(可以通过jwt或者redis设置过期时间),处于这个时间段内都可以通过请求头中携带token的方式,直接以此用户的身份访问服务
- 使用此方案一方面方便用户,另一方面也可以解决后端的一些问题,访问鉴权是一个很高频的行为,比如我们可以把对关系型数据库的查询转移到对redis的访问,这能降低关系型数据库的压力,也能让用户获得更快的访问速度
- 那么我们对token的验证的Filter就应该放在使用用户名密码验证的Filter(UsernamePasswordAuthenticationFilter)的前边,这一步需要在SecurityConfig中进行配置。
- 我们在jwtFilter的验证中,会先判断请求是否携带token,不携带token就直接放行(要么是非法的访问,要么是通过用户名密码的访问),交给后边的过滤器处理即可
- 如果拿到token,首先尝试解析token,可能会出现jwt解析失败(伪造失败的token或者过期的token),那么就记录并放行即可,后边的过滤器会驳回请求并返回403
- 如果token解析成功拿到userid,那么我们就去redis中查询是否存在用户信息(用户是否处于登录状态,我们可以通过redis来控制),如果我们没有拿到,那么就记录并放行
- 最后如果顺利拿到了用户信息,说明用户处于登陆状态,根据信息构造authentication,然后存入SecurityContextHolder,其他过滤器能够知道此请求已经经过认证