1. 前言
1.1. 什么是透明鉴权?
开发业务代码的时候,不用关心请求自己接口的用户是否合法。鉴权的工作交给架构中的公共代码,自己仅专心于业务实现。
@Controller
public class ThreadLocalController {
@Autowired
BankAccountService service;
@RequestMapping("/bank/account")
public ModelAndView doSomething(@RequestParam("id") Long id) {
// 省略了userService.getUserById() 的鉴权操作,也不用在方法上增加一个userId参数
service.doSomething();
return new ModelAndView();
}
}
1.2. 有什么好处?
践行 “单一职责” 原则。鉴权与授权都应该是安全模块的内容,当单体架构膨胀到一定程度后,考虑重构为微服务时,安全模块可拔插的特点就有优势了。
1.3. 透明鉴权后,还有什么需要考虑的?
-
细粒度控制依旧在业务层
EG: 删除客户资源,要取登录人的信息确保是本人删除 -
ThreadLocal
的技术抉择
EG:ThreadLocal
用来存储上下文中用户实体,避免User参数层层传递,但是在线程池环境下,ThreadLocal
很容易出问题。Spring Cloud Hystrix
默认Thread
隔离,要修改成Semaphore
才能避免ThreadLocal
污染。 -
授权与鉴权的兼容
EG: 用户获得新的权限,旧的token如何处理(强制下线重新登录还是做兼容)
1.4. 本文的重点是什么?
- 理清
Servlet Filter
与Spring MVC Interceptor
的能力边界 - 构建一个透明鉴权的模型,可迁移到
Spring Cloud Gateway
或Spring Security
的学习中
2. 一个简单的鉴权流程图
2.1. 基本思想
- Filter 能有读取header的能力,根据token是否携带、token是否命中缓存(缓存可以是Session/ConcurrentHashMap/Redis) 转发给对应的url,让Spring的DispatcherServlet接管
- 缺少token => /token/empty => 返回401
- token失效 => /token/invalid => 返回403
- 有效的token会正常进入Spring 的 Controller,这样在Controller层就不用校验用户是否合法了
2.2. 不足与思考
后端接口不需要校验请求的合法性,但是需要请求人的信息时怎么办?
—— 在Filter层访问缓存时,提供一个获取user实体的能力
- 用户登录写入token - user实体
- 用户带着token访问,用token获取user实体
- 在Filter层 把user实体放入threadlocal,让 controller后的代码能获取到user实体
- 用户信息修改,把缓存的token及user实体清除,确保一致性
3. 引入ThreadLocal 的流程图
3.1. 不足与思考
- ThreadLocal 很容易造成内存泄漏
使用Spring mvc 的 Interceptor 进行后置处理,remove掉ThreadLocal
4. 引入Interceptor 的流程图
5. 后记
-
分布式的方案远不止上述的注意点,特别是在用到线程池的时候,Threadlocal要慎用
-
这个模型将Servlet和Spring架构割裂了。Spring Security 会把这两个架构融合在一起