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

前言

  开心一刻html

    小明的朋友骨折了,小明去他家里看他。他老婆很细心的为他换药,敷药,而后出去买菜。小明满脸羡慕地说:你特么真幸福啊,你老婆对你那么好!朋友哭得稀里哗啦的说:兄弟你别说了,我幸福个锤子,就是她把我打骨折的。git

揣摩下此刻男人的心里github

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

  github:https://github.com/youzhibingspring

  码云(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,另外认证频率原本就不高,缓存的意义不大。session

  通常状况下受权是经过注解方式实现的,注解配合aop会在咱们的业务方法前织入前置权限检查处理,检查过程与认证过程相似:从缓存中获取authorizationInfo,没有则经过realm从数据库获取,而后放入缓存,而后将authorizationInfo与@RequiresPermissions("xxx")中的xxx来进行匹配,完成权限检查,检查经过则进入咱们的目标方法,不经过则抛出异常。关于权限缓存,我的建议开启,由于权限的验证仍是挺频繁的,若是不开启缓存,那么会给数据库形成必定的压力。app

遗留问题解答

  上篇遗留问题:session过时后,咱们再请求,shiro是如何处理并跳转到登陆页的?回答这个问题以前,咱们先看看另一个问题:ui

上篇博文中讲到了登陆认证成功后会将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,可是登陆成功后的其余请求,好比:http://localhost:8080/own/index,subject的authenticated是何时在哪被赋值成true的呢?咱们已经知道session中有认证状态,那么确定是某个时候在某个地方将session中的认证状态赋值给了subject,具体是怎么样咱们来跟一下源码,还记得shiro的入口filter:SpringShiroFilter,咱们从SpringShiroFilter的doFilterInternal(从AbstractShiroFilter继承)方法开始

    能够看到,在建立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,让用户从新进行登陆认证。事实是这样吗,咱们来跟下源码(假设此时请求是:http://localhost:8080/own/index)

    能够看到,请求进过SpringShiroFilter时,subject中authenticated被设置成false,而后生成ProxiedFilterChain

    请求会来到FormAuthenticationFilter,认证不经过,重定向到/login,并返回false,表示filter链不用继续往下走了(具体可查看上篇博文)。

    强调下:不少对session的操做,都会同步到缓存(或持久层),包括session刷新(touch())、设置session属性(setAttribute()等等,具体能够看AbstractNativeSessionManager,不少session操做中都会调用onChange方法

protected void onChange(Session session) {
    sessionDAO.update(session);
}

shiro源码系列

  系列地址

  shiro源码篇 - shiro的session建立,你值得拥有

    SessionManager负责session的操做,包括建立、维护、删除、失效、验证等;

    AbstractNativeSessionManager的start是建立session的入口;

    SimpleSession是shiro完彻底全的本身实现,是shiro对session的一种拓展。但SimpleSession不对外暴露,咱们通常操做的是SimpleSession的代理:DelegatingSession,或者是DelegatingSession的代理:StoppingAwareProxiedSession;对session的操做,会经过一层层代理,来到DelegatingSession,DelegatingSession将session的操做转交给sessionMananger,sessionManager经过一些校验后,最后转交给SimpleSession处理。

  shiro源码篇 - shiro的session的查询、刷新、过时与删除,你值得拥有

    通常操做的session是session的代理,代理将session操做委托给sessionManager,sesionManager校验以后再转交给SimpleSession;

    session过时定时任务默认60分钟执行一次,所session已过时或不合法,则抛出对应的异常,上层经过捕获异常从sessionDAO中删除session;

    不仅定时任务作session的校验,session的基本操做都在sessionManager中有作session的校验,例如touch、setAttribute等,具体能够查看AbstractNativeSessionManager,对session的操做都是经过AbstractNativeSessionManager处理后转交给SimpleSession。

  shiro源码篇 - shiro的session共享,你值得拥有

    session共享实现的原理其实都是同样的,都是filter + HttpServletRequestWrapper,只是实现细节会有所区别;

    shiro的session共享实际上是比较简单的,重写CacheManager,将其操做指向咱们的redis,而后定制CachingSessionDAO实现session缓存操做和session持久化;

    若是session须要持久化,推荐自定义sessionDAO继承EnterpriseCacheSessionDAO,若是只是缓存,则推荐自定义sessionDAO继承CachingSessionDAO。

  shiro源码篇 - shiro的filter,你值得拥有

    SpringShiroFilter注册到spring容器,会被包装成FilterRegistrationBean,经过FilterRegistrationBean注册到servlet容器;SpringShiroFilter至关因而整个shiro的入口;

    SpringShiroFilter会建立ProxiedFilterChain,代理servlet FilterChain,让请求先走shiro的filter链,让后再走servlet FilterChain。

  shiro源码篇 - shiro认证与受权,你值得拥有

    认证经过Filter实现,anon表示匿名访问,不须要认证,通常就是针对游客能够访问的资源,而authc则表示须要登陆认证;

    咱们全部的请求通常由shiro中3个Filter:LogoutFilter、AnonymousFilter、FormAuthenticationFilter分摊了,LogoutFilter负责/logout,AnonymousFilter负责/login和静态资源,FormAuthenticationFilter则负责剩下的(/**);

    认证由FormAuthenticationFilter实现,未登陆的请求会由它重定向到/login;认证过程是将界面输入的信息(UsernamePasswordToken)与缓存(或数据库)中的authenticationInfo进行匹对验证;认证信息不建议缓存;

    受权由注解方式,配合aop实现目标方法前的加强织入;认证过程是将缓存(或数据库)中的authorizationInfo与@RequiresPermissions("xxx")中的xxx进行匹配校验;认证信息建议缓存起来。

参考

  《跟我学shiro》

相关文章
相关标签/搜索