看了很多大佬的博客,列出自己碰到的问题还有理解,有问题的话欢迎指出讨论。
JWT
原名:json web token
由头部-载荷-签名构成
头部封印着加密算法
载荷是jwt传递的信息,就相当于你工卡上面的你的名字职位等等属性,是可以被看到的所以敏感信息不能放在载荷中。
签名是用来作验证的,放在服务端,服务端就是通过签名验证token是否有效,如果泄漏很危险,这样客户端就可以自己构造jwt绕过安全认证。
网上jwt的分析很多,这里不是重点。
springsecurity
spring的安全框架,包括 认证+授权
大概流程
1、客户端发送post表单请求到服务端时被过滤器UsernamePasswordAuthenticationFilter获取到登录信息(用户名,密码)
2、UsernamePasswordAuthenticationFilter将登录信息初步封装成UsernamePasswordAuthenticationToken(Authentication的子类)交给AuthenticationManager管理
3、AuthenticationManager管理着很多AuthenticationProvider(可以自定义登录成功和失败的回调问题)
4、AuthenticationProvider下又有DaoAuthenticationProvider(可以配置密码加密方式)
5、DaoAuthenticationProvider通过UserDetailsService的loadUserByUsername()方法拿到UserDetails(封装着用户的信息,主要是去拿到权限信息构造上下文),配置上密码的加密方式setPasswordEncoder()
6、将DaoAuthenticationProvider交给AutenticationManager的authenticate()方法实现认证
7、AutenticationManager会自己构造授权信息的上下文SecurityContextHolder.getContext().setAuthentication(authentication);也是最终目的
详细流程可以看这篇博客:https://mp.weixin.qq.com/s/z6GeR5O-vBzY3SHehmccVA
读了源码才知道AuthenticationManager干了什么。
其中的坑点:
1、一开始构造的UsernamePasswordAuthenticationToken(Autentication的子类)是不完善的,目的就是为了去数据库中获取到该用户的权限构造成完整的上下文放到SecurityContext中,我认为这个上下文就是登录的授权凭证了。而你的用户名和密码只是判断准入的条件而已。
2、jwt在启动session时上下文信息才能被其他线程获取到,因为jvm是为每个线程分配了一个ThreadLocal(可以理解成线程私有的空间),所以其他线程无法获取到。在UsernamePasswordAuthenticationFilter过滤器之前还有个SecurityContextPersistenceFilter过滤器。看这篇博客:https://segmentfault.com/a/1190000022767227
博客内的主要内容是:
每一个请求到达服务端的时候,首先从 session 中找出来 SecurityContext ,然后设置到 SecurityContextHolder 中去,方便后续使用,当这个请求离开的时候,SecurityContextHolder 会被清空,SecurityContext 会被放回 session 中,方便下一个请求来的时候获取。
所以要实现不查库体现出jwt的优势就只能打开session,所以jwt的使用场景比较受到限制,一般用来当作外链接或者一次性邮件使用,就像百度网盘的链接分享。可以看这篇博客的优缺点分析:https://zhuanlan.zhihu.com/p/263410154
如果关掉session那么每次都要查库进行角色验证,相当于普通的token了,jwt可以理解为高级点的token。
3、AuthenticationManager是自己清理掉了密码这个敏感信息的,不用纠结自己写来保证密码安全。
4、jwt的签名明明放在了服务端,所以我原来以为只要解密成功就代表能够准入,但是服务端要上下文作为权限判断,这样jwt就成了token的一种形式,这样不用session的话就必须去查库。
5、jwt的载荷是能够被看到的!所以不能存敏感信息,只是用来实现跨域的准入条件,服务端统一签名,通过签名进行解密后能够将解密信息和实际信息匹配上就代表可入。流程是:
(1)拿到jwt的头部(加密方式)
(2)通过签名解密载荷信息
(3)判断是否和载荷的信息匹配
(4)能匹配代表jwt初步有效,因为还得判断是否过期的问题,过期时间这个也是存在载荷当中的。
6、springsecurity整合jwt的话,就是在UsernamePasswordAuthenticationFilter前加个拦截器,这个自己定义,然后jwt去把AuthenticationManager的事情干了,就是拿到对应用户的权限封装成上下文SecurityContext存到session中,不存也可以这样就得每次登录验证在存到上下文且只适用于单线程。
第一次发文,如果有什么问题欢迎评论区补充讨论。