shiro源码篇 - 疑问解答与系列总结,你值得拥有

 

揣摩下此刻男人的内心

  路漫漫其修远兮,吾将上下而求索!

  github:https://github.com/youzhibing

  码云(gitee):https://gitee.com/youzhibing

前情回顾

  上篇中主要讲了两点认证与授权,认证主要FormAuthenticationFilter和AnonymousFilter两个filter来控制,shiro对所有请求都会先生成ProxiedFilterChain,请求会经过ProxiedFilterChain,先执行shiro的filter链,再执行剩下的servlet Filter链,最后来到我们的Controller。

  认证过程是通过filter控制实现的,我们所有的请求由shiro中3个Filter:LogoutFilter、AnonymousFilter、FormAuthenticationFilter分摊了,LogoutFilter负责/logout,AnonymousFilter负责/login和静态资源,FormAuthenticationFilter则负责剩下的(/**),三个filter只会有一个生效(注意filter的配置顺序);当FormAuthenticationFilter生效的时候会进行登录认证,认证过程:先从缓存获取authenticationInfo,没有则通过realm从数据库获取并放入缓存,然后将页面输入的用户信息(UsernamePasswordToken)与authenticationInfo进行匹配验证,认证通过会将subject中的authenticated设置成true,表示当前subject已经被认证过了。关于认证缓存,个人不建议开启,因为当修改用户信息后,不好处理缓存中的authenticationInfo,另外认证频率本来就不高,缓存的意义不大。

  一般情况下授权是通过注解方式实现的,注解配合aop会在我们的业务方法前织入前置权限检查处理,检查过程与认证过程类似:从缓存中获取authorizationInfo,没有则通过realm从数据库获取,然后放入缓存,然后将authorizationInfo与@RequiresPermissions("xxx")中的xxx来进行匹配,完成权限检查,检查通过则进入我们的目标方法,不通过则抛出异常。关于权限缓存,个人建议开启,因为权限的验证还是挺频繁的,如果不开启缓存,那么会给数据库造成一定的压力。

遗留问题解答

  上篇遗留问题:session过期后,我们再请求,shiro是如何处理并跳转到登录页的?回答这个问题之前,我们先看看另外一个问题:

复制代码
上篇博文中讲到了登录认证成功后会将subject的authenticated设置成true,表示当前subject已经被认证过了,但是只是当前subject; 我们可以将subject看成是request,每次请求来的时候都会将request/response对封装成subject,AbstractShiroFilter的方法doFilterInternal中有这样一个调用  final Subject subject = createSubject(request, response);  我们来看看createSubject的方法描述:     Creates a  WebSubject instance to associate with the incoming request/response pair which will be used throughout the request/response execution.     创建一个关联request/response对的WebSubject实例,用于后续request/response的执行
复制代码

  也就是目前我们还只是看到了当前请求有认证状态,当前会话还没有看到认证状态;撇开shiro,如果是我们自己实现,我们会怎么实现,肯定会在subject的authenticated设置成true的时候也将认证状态也设置在session中,至于是存储在自定义session的某个标志字段(类似subject的authenticated)中,还是存储在session的attributes中(setAttribute(Object key, Object value)进行设置),看我们的需求和个人喜好。

  归纳下这个问题:shiro是如何保存当前会话认证状态的,是上述中的某种实现方式,还是shiro有另外的实现方式

  shiro是如何保存会话认证状态的

    每次请求都会生成新的subject,如果我们把认证状态只放到subject中,那么每次请求都需要进行认证,这显然是不合理的,我们需要将认证状态保存到会话(session)中,那么整个会话期间只需要认证一次即可。那么shiro是怎么实现的了,我们来看看源码,从我们Controller的doLogin方法开始

    如果我们继续跟进s.setAttribute(attributeKey, value),会发现认证状态最终存放到了SimpleSession的attributes属性中,

private transient Map<Object, Object> attributes;

    也就是说认证状态会存在session的attributes中,正是我们上面说的方式之一,shiro没有用它特有的方式,最终在session中的存在形式如下图

    看过上篇博客的朋友应该会有印象:FormAuthenticationFilter的isAccessAllowed方法(从AuthenticatingFilter继承)中第一个认证的是subject的authenticated

    为什么获取subject的authenticated,而不是直接获取session的认证状态,我还没弄清楚为什么,难道是为了组件的分工明确? 既然shiro这么做了肯定有它的道理,我们先不纠结这个(知道的朋友可以评论区提示下)。我们知道登录成功后,subject的authenticated会被赋值成true,但是登录成功后的其他请求,比如:upload/201812241131423654.gif" alt="" style="margin: 0px auto; padding: 0px; border: 0px; max-width: 900px; height: auto; display: block;" />

    可以看到,在创建subject的时候,会将session中的认证状态赋值给subject的authenticated。

    小结下:登录时,登录成功会将认证状态(成功)存储到session的attributes中,之后的每一次请求,在创建subject的时候,都会将session中的认证状态赋值给subject的authenticated,那么FormAuthenticationFilter在认证的时候会直接返回true,继续走servlet filter链,最终来到我们的Controller。

    至此,该问题就明了了,会话认证状态还是保存在session中,只是中间处理的时候会将session中的认证状态赋值给subject,由subject传递给FormAuthenticationFilter认证状态。

  session过期后,我们再请求,shiro是如何处理并跳转到登录页的

    如果我们明白了上个问题,那么这个问题就很好理解了。如果session过期,那么通过sessionDAO获取的session为null,subject的authenticated就会被赋值成false,那么在FormAuthenticationFilter中认证不通过,则会重定向到/login,让用户重新进行登录认证。事实是这样吗,我们来跟下源码(假设此时请求是:upload/201812241131428162.gif" alt="" style="margin: 0px auto; padding: 0px; border: 0px; max-width: 900px; height: auto; display: block;" />

    可以看到,请求进过SpringShiroFilter时,subject中authenticated被设置成false,然后生成ProxiedFilterChain

关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信