背景说明
用户权限管理是每个信息系统最基本的需求,对基于Java的项目来说,最常用的权限管理框架就是大名鼎鼎的Apache Shiro。Apache Shiro功能非常强大,使用广泛,几乎成为了权限管理的代名词。但对于普通项目来说,Shiro的设计理念因为追求灵活性,一些概念如Realm,Subject的抽象级别都比较高,显得比较复杂。如果没有对框架细节进行深入了解的话,很难理解其中的准确含义。要将其应用于实际项目,还需要针对项目的实际情况做大量的配置和改造,时间成本较高。
而且Shiro兴起的时代主流应用还是传统的基于Session的Web网站,并没有过多的考虑目前流行的微服务等应用形式的权限管理需求。导致其并没有提供一套无状态微服务的开箱即用的整合方案。需要在项目层面对Shiro进行二次封装和改进,开发难度较大。
我负责的几个项目都使用了Shiro作为权限管理框架,感叹其强大功能的也为每次都需要进行二次开发和封装感到厌烦了,于是在对Shiro的结构有比较深入的了解之后,决定在Shrio的基础上,对一些常用的开发场景进行封装和整合,提高开发效率,降低配置难度,开发一套基于Spring Boot环境,适合于各类无状态微服务应用的,开箱即用的轻量级权限框架。
使用Aceess Token替换Session
所谓的无状态,其实是把原来由后端服务负责维护的,基于Http Session的用户会话信息交由客户端(如果是普通的web应用,客户端即是用户的浏览器)进行维护,这样后端服务的单元测试,负载均衡,横向扩容都要方便很多。
但是用户会话信息关乎数据安全,放到客户端如何确保安全呢?常见的做法是由服务端根据客户端首次提交的认证信息签发一个accessToken,这个accessToken就相当于客户端的身份证,以后每次交互的时候客户端只需要出示这个凭证,服务端就能够识别当前客户端的身份。
实现accessToken的方式有很多,理论上只要确保一个accessToken无法被第三方解码,能唯一标识一个客户端,服务端能够解析出token的创建时间,客户端标识等内容就行了。但是自行设计的实现方法难免存在各种安全隐患,accessToken是要由客户端进行维护的,我们无法确保客户端都一定运行在完全安全的环境中。幸运的是现在有一种专门为此目的而设计的开放标准JWT(JSON Web token),它基于http交互中常见的数据格式JSON,提供紧凑而安全的Token生成处理机制。JWT的详细内容这里就不多介绍了,有兴趣可以自行查阅相关资料。
认证和授权
Shiro默认提供的实现是基于用户Session的权限验证模型,如何让其支持基于accessToken的无状态形式呢?Shiro功能的核心其实主要包含两部分内容:认证(Authentication)和授权(Authorization)。认证就是核实用户身份的过程,比如检查客户端提供的用户名和密码是否是合法的系统用户。而授权的含义则是检查该用户是否能够访问具体的某个资源,也就是访问控制。所以我们需要做的就是扩展Shiro对于认证和授权的默认实现,使其能够支持accessToken的形式。
我们先来看一下Shiro实现认证的流程:
Shiro认证流程图.jpg
从整个流程图上可以看出,最终实现认证逻辑的组件是所谓的Realm,Shiro默认实现了很多不同的Realm,可以从数据库,LADP等各个地方加载用户的认证信息。
授权过程和认证过程差不多,核心也在于Realm的实现:
Shiro授权流程图.jpg
所以,第一步要做的应该是先实现一个自定义的JWTShiroRealm,采用accessToken的方式来实现系统的认证和授权。
public class JWTShiroRealm extends AuthorizingRealm {
/**
* 可供扩展的权限加载器,由应用程序负责实现
*/
private JWT