Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
三个核心组件:Subject, SecurityManager 和 Realms
Subject:即“当前操作用户”。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
自定义Shiro注解
- 创建自定义的注解
- 权限处理器,继承
AuthorizingAnnotationHandler
,校验权限 - 资源管理器,继承
AuthorizationAttributeSourceAdvisor
,添加新注解支持 - 方法拦截器,继承
AuthorizingAnnotationMethodInterceptor
- AOP拦截器,继承
AopAllianceAnnotationsAuthorizingMethodInterceptor
- shiroConfig配置
- shiro
UserRealm
- 使用demo
创建注解
1 |
|
权限处理器
1 | public class AuthHandler extends AuthorizingAnnotationHandler { |
资源管理器
1 | //加上该注解可以不用再shiroConfig文件中增加相关代码 |
方法拦截器
1 | public class AuthMethodInterceptor extends AuthorizingAnnotationMethodInterceptor { |
AOP切面拦截器
1 | public class AuthAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor { |
配置shiro
1 |
|
shiro Realm
1 |
|
shiro工具类
1 | public class ShiroUtils { |
使用demo
1 |
|
shiro自定义注解方案二
定义注解
1 |
|
定义切面
1 |
|
权限验证工具类
1 | public class UserUtils { |
使用demo
1 |
|
Session
tomcat禁用session,或者自己控制session。
把spring controller参数里面的HttpServletRequest对象和HttpSession对象打印出来:打印的结果是org.apache.shiro.web.servlet.ShiroHttpServletRequest
和org.apache.shiro.web.servlet.ShiroHttpSession
是如何进行封装的?
查看filter调用链:AbstractShiroFilter
中实现了doFilterInternal()。
1 | protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) { |
这就知道request
怎么变成ShiroHttpServletRequest
的了。
Shiro重写了默认的HttpServletRequestWrapper
1 | public class ShiroHttpServletRequest extends HttpServletRequestWrapper { |
session是如何封装的?
this.getSecurityManager().isHttpSessionMode()
取决于SecurityManager
我项目里ShiroConfig中
1 |
|
使用的是DefaultWebSecurityManager
。
1 | public class DefaultWebSecurityManager extends DefaultSecurityManager implements WebSecurityManager { |
DefaultWebSessionManager
中该方法返回的是false,所以用的是ShiroHttpSession
。这样通过shiro就可以灵活控制session。
也可以模仿shiro自己重写HttpServletRequestWrapper
- 继承HttpServletRequestWrapper 重写getSession()方法
- 配置一个过滤器,在过滤器中配置自己的Request类
还有比较原始的方式:
jsp中<%@ page session="false"%>
然后显示获取session,request.getSession(true)
,request.getSession(false)
不会生成Session。
应用
自定义sessionID生成方案
需要修改的地方比较多重写部分方法,同时可以支持在消息头加token字段或者在cookies中传递sessionID标识。
对于想使用cookeis的客户端可以调用validate接口进行登录,后续就可以自动带上cookies进行操作了。
对于需要调用第三方平台进行校验的可以统一拦截请求判断请求头中是否携带token,如果携带的token没通过校验就去校验中心进行校验,校验通过使用token创建对应session设置cookies。
扩展
Spring Cloud微服务权限验证
OAuth2验证
验证流程:
1 | +--------+ +---------------+ |
所有的Token有效性校验都落在的授权服务器上,服务器压力大。
应用场景:
各大应用内的qq,微信,微博登录等。
a.用户点击qq登录,会先跳转到qq登录页面,这时请求已经跳转到qq服务器了,然后用户输入账号或者扫码登录,这时所有请求都在qq服务器完成
b.用户正确登录后,qq服务器返回用户的code给第三方应用,然后第三方应用再使用code去授权服务器请求获取token。(这一步用户不可见)
c.第三方应用获取到token后,再使用token获取用户的qq名称,头像等信息
优点:用户可以控制自身的一些权限是否给第三方,第三方只能获取到用户临时产生的一个访问的code,安全性。
缺点:认证过程繁琐。
JWT验证
验证流程:
1 | +--------+ +---------------+ |
业务服务器自行校验,降低服务器压力。
适用场景:
一次性的身份认证、api的鉴权等,这些场景能充分发挥jwt无状态以及分布式验证的优势。
不适用的场景:
不要试图用jwt去代替session。这种模式下其实传统的session+cookie机制工作的更好,jwt因为其无状态和分布式,事实上只要在有效期内,是无法作废的,用户的签退更多是一个客户端的签退,服务端token仍然有效,你只要使用这个token,仍然可以登陆系统。另外一个问题是续签问题,使用token,无疑令续签变得十分麻烦,当然你也可以通过redis去记录token状态,并在用户访问后更新这个状态,但这就是硬生生把jwt的无状态搞成有状态了,而这些在传统的session+cookie机制中都是不需要去考虑的。
微服务中Auth2+JWT组合使用
通过JWT生成Token。后端可以通过token获取用户信息。token生成策略除了JWT还有Auth2自己的策略。可以通过配置把Auth2的token存储在内存(单实例)、存储在redis或者存储在其他数据库中。可以通过DefaultTokenServices
进行配置
由于JWT无法手动过期,可以JWT跟网关结合,增加存储模块存储过期的Token,实现过期。
微服务中直接用Redis
生成随机Token与对应用户信息存储在redis中,后续请求经过网关服务器进行校验,校验通过在转发请求时在请求头增加用户信息,后端服务可以获取得到用户信息。
其他方案服务间校验
- 在Zuul Api网关上进行校验,可以只在zuul网关上进行校验,但是后端服务之间调用不安全,所以可以都进行校验
- Service中在Interceptor上进行拦截,去服务鉴权中心获取token校验。这样要频繁访问鉴权中心,可以考虑优化
参考
The OAuth 2.0 Authorization Framework
Oauth2详解-介绍(一)
理解OAuth 2.0
微服务场景使用OAuth2
微服务架构下的统一身份认证和授权