以前老的项目大多采用的是session进行用户身份的管理,目前很多公司的项目都是前后端分离, 这样使用session 成本就会变高,然后多数项目会采用Jwt进行用户的身份的管理。
JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。有不清楚的同学可以先看看者篇文章:10分钟了解JSON Web令牌(JWT)
今天要做的事就是将Jwt认证和授权的过程封装成一个Springboot 的Stater包,只需要使用EnableJwtSecurity注解,就可以一键开启Jwt认证功能,无需在每个项目都拷贝同样的认证代码,非常的麻烦。代码已经放在github上,链接地址在文末,有兴趣的可以亲自使用一下,欢迎提出您的宝贵意见。
该Starter包含的功能
1、支持白名单过滤功能
2、支持配置token刷新时间
3、支持生成随机盐的方式生成Jwt(默认是使用的固定的盐)
4、最重要的当然是Jwt认证功能啦
接下来我们开始讲解该Starter的认证思路,篇幅有点长,请耐心阅读
Spring Security 核心类简介
AuthenticationManager, 用户认证的管理类,所有的认证请求(比如login)都会通过提交一个token给AuthenticationManager的authenticate()方法来实现。当然事情肯定不是它来做,具体校验动作会由AuthenticationManager将请求转发给具体的实现类来做。根据实现反馈的结果再调用具体的Handler来给用户以反馈。这个类基本等同于shiro的SecurityManager。
AuthenticationProvider, 认证的具体实现类,一个provider是一种认证方式的实现,比如提交的用户名密码我是通过和DB中查出的user记录做比对实现的,那就有一个DaoProvider;如果我是通过CAS请求单点登录系统实现,那就有一个CASProvider。这个是不是和shiro的Realm的定义很像?基本上你可以帮他们当成同一个东西。按照Spring一贯的作风,主流的认证方式它都已经提供了默认实现,比如DAO、LDAP、CAS、OAuth2等。。
UserDetailService, 用户认证通过Provider来做,所以Provider需要拿到系统已经保存的认证信息,获取用户信息的接口spring-security抽象成UserDetailService。虽然叫Service,但是我更愿意把它认为是我们系统里经常有的UserDao。
AuthenticationToken, 所有提交给AuthenticationManager的认证请求都会被封装成一个Token的实现,比如最容易理解的UsernamePasswordAuthenticationToken。这个就不多讲了,连名字都跟Shiro中一样。
SecurityContext,当用户通过认证之后,就会为这个用户生成一个唯一的SecurityContext,里面包含用户的认证信息Authentication。通过SecurityContext我们可以获取到用户的标识Principle和授权信息GrantedAuthrity。在系统的任何地方只要通过SecurityHolder.getSecruityContext()就可以获取到SecurityContext。在Shiro中通过SecurityUtils.getSubject()到达同样的目的。
##Spring Security Filter
Spring Security 的底层是通过一系列的 Filter 来管理的,每个 Filter 都有其自身的功能,而且各个 Filter 在功能上还有关联关系,所以它们的顺序也是非常重要的。
Filter 顺序
1.Spring Security 已经定义了一些 Filter,不管实际应用中你用到了哪些,它们应当保持如下顺序。
2.ChannelProcessingFilter,如果你访问的 channel 错了,那首先就会在 channel 之间进行跳转,如 http 变为 https。
3.SecurityContextPersistenceFilter,这样的话在一开始进行 request 的时候就可以在 SecurityContextHolder 中建立一个 SecurityContext,然后在请求结束的时候,任何对 SecurityContext 的改变都可以被 copy 到 HttpSession。
4.ConcurrentSessionFilter,因为它需要使用 SecurityContextHolder 的功能,而且更新对应 session 的最后更新时间,以及通过 SessionRegistry 获取当前的 SessionInformation 以检查当前的 session 是否已经过期,过期则会调用 LogoutHandler。
认证处理机制,如 UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter 等,以至于 SecurityContextHolder 可以被更新为包含一个有效的 Authentication 请求。
5.SecurityContextHolderAwareRequestFilter,它将会把 HttpServletRequest 封装成一个继承自 HttpServletRequestWrapper 的 SecurityContextHolderAwareRequestWrapper,同时使用 SecurityContext 实现了 HttpServletRequest 中与安全相关的方法。
6.JaasApiIntegrationFilter,如果 SecurityContextHolder 中拥有的 Authentication 是一个 JaasAuthenticationToken,那么该 Filter 将使用包含在 JaasAuthenticationToken 中的 Subject 继续执行 FilterChain。
7.RememberMeAuthenticationFilter,如果之前的认证处理机制没有更新 SecurityContextHolder,并且用户请求包含了一个 Remember-Me 对应的 cookie,那么一个对应的 Authentication 将会设给 SecurityContextHolder。
8.AnonymousAuthenticationFilter,如果之前的认证机制都没有更新 SecurityContextHolder 拥有的 Authentication,那么一个 AnonymousAuthenticationToken 将会设给 SecurityContextHolder。
9.ExceptionTransactionFilter,用于处理在 FilterChain 范围内抛出的 AccessDeniedException 和 AuthenticationException,并把它们转换为对应的 Http 错误码返回或者对应的页面。
10.FilterSecurityInterceptor,保护 Web URI,并且在访问被拒绝时抛出异常。
- 以下是Spring Security 大致的认证流程:
##Jwt 认证流程
看了以上的知识我们要做的无非就是定义一个Filter用于过滤需要认证的请求,