(原)

shiro最核心的2个操作,一个是登录的实现,一就是过滤器了。登录有时间再补录说明,这里分析下shiro过滤器怎样玩的。

1、目标

这里会按如下顺序逐一看其实原理,并尽量找出其出处。

先看一下shiro过滤器有哪些及它们的别名分别对应哪些类:点这里

这里只分析平时用的最多的一个:authc过滤器,对应的处理器的类是FormAuthenticationFilter。

2、继承关系及结构

authc: 1.AbstractFilter > 2.NameableFilter > 3.OncePerRequestFilter > 4.AdviceFilter > 5.PathMatchingFilter > 6.AccessControlFilter > 7.AuthenticationFilter > 8.AuthenticatingFilter > 9.FormAuthenticationFilter

根据这个结构关系可以清楚的知道,接下来要分析的FormAuthenticationFilter上面还有8个。

3、Filter过滤器简介

tomcat中的自定义过滤器必需实现Filter接口,过滤器的实现方法为Filter中的

doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

如果一个请求被该过滤器拦截,想要到达用户最后请求的地址,那么一定得有:filterChain.doFilter(request, response);这个方法的调用,表示通过过滤,请求可以到达用户的请求地址。否则用户的请求地址不会被调用,用户的请求被过滤器拦截。


4、shiro过滤器分析

1、AbstractFilter

位于shiro过滤器最顶层,是个抽象方法,直接继承了Filter,

它只做了一些其本的初始化操作,并没有实现Filter中的doFilter方法。

总结:AbstractFilter只做了一些基本的初始化,并没有过多的逻辑在里面。

2、NameableFilter

允许通过getName和setName方式给过滤器命名。如果没有给该过滤器命名,那么默认将会使用web.xml中给定的名称(FilterConfiger的filterName)


该类可以理解为为过滤器命名用的,也没有实现Filter中的doFilter方法。

总结:NameableFilter是用于给过滤器命名使用的。

3、OncePerRequestFilter

filter的基类(说白了,就是正真实现了Filter中的doFilter方法的类),它用来保证在每个servlet容器上,第个请求都只会被过滤一次,不会重复过滤。

通过getAlreadyFilteredAttributeName()方法来鉴别请求是否已经被过滤过。默认的实现基于具体过滤的实例名称。

接下来看看具体逻辑:

1、 判断过滤器是否已经执行过,如果执行过,调用filterChain.doFilter(request, response)直接放行,不再重复执行该过滤器的处理逻辑,直接走下一个过滤器或者通过该过滤进入到实际请求方法中。

2、 判断过滤器是否开启,该类有个成员变量eabled,默认为true(注释给的解释是大多数的过滤器都是希望开启的,所以默认值为true),过滤器为开启状态,如果该过滤器没有被开启中,也同上面的逻辑一样,直接走下一个过滤器或者通过该过滤进入到实际请求方法中。

3、这里又分3步,

1、  设置一个已经执行过滤器的属性名称在request中。

2、  调用doFilterInternal方法,执行真正的过滤器处理逻辑。

3、  这个过滤器处理完后,将过滤器的属性名称从request中移出。这样如果程序执行到第2步,过滤又被调用了,它将会走到第一个if中,直接略过这个过滤器的处理,这样就保证了,每个过滤器在处理一个请求的时候只会被执行一次。

总结:OncePerRequestFilter提供了一个实际处理过滤业务的方法doFilterInternal,并且保证每个请求只会被该过滤器过滤一次。

4、AdviceFilter

这个过滤器,类似于开启了AOP环绕通知,提供了preHandle,postHandle和afterCompletion这三个方法。

其过滤逻辑为:doFilterInternal方法。

executeChain方法中的实现为:chain.doFilter(request, response);

所以如果preHandle方法返回false,则说明过滤器不会执行chain.doFilter,意味着请求被拦截掉了,不会进入到用户请求的地址上去。如果为true,表示过滤器放行了过滤的逻辑通过。

然后会执行postHandle方法(该类中的postHandle方法为空实现),

最后会执行一个,这个方法用于对一些异常进行处理(目前也是空实现)。

通过以上可以看出,过滤的逻辑代码实际上是在preHandle这个方法中处理的,这个方法的返回值true和false决定了请求放行和请求被拦截。

总结:AdviceFilter提供了类似于AOP环绕通知式的编程方式,其处理拦截的逻辑是在preHandle方法中完成的。preHandle方法返回true和false代表了通过过滤,请求可以到达用户的请求地址和过滤器拦截掉了用户的请求。

 

5、PathMatchingFilter

这个过滤器会处理指定的请求路径,和对其它路径的请求放行。

打个比方:如果配置如下拦截,/hello=authc,

这就意味着,请用户请求/hello时,这时的authc过滤就会对这个请求拦截并进行过滤逻辑处理。如果一个用户请求是/world,则不会对此请求过滤。

这里有2个处理逻辑,

1、判断:appliedPaths为空,则放行,不作拦截请求的处理。

这里的appliedPaths是一个地址过滤器的映射map。

key存放的是请求的URL,value是对应该URL处理的Filter过滤器,这个 value也可以是空值。

2、如果用户的请求与配置中拦截的请求匹配,则会调用isFilterChainContinued方法进行下一步处理。

如果拦截器是开启的,则调用方法onPreHandle进行拦截处理。

onPreHandle方法默认返回true,也不对请求进行拦截处理,所以如果需要有自定义处理拦截的逻辑,需要由子类覆盖这个方法来完成。

总结:PathMatchingFilter对配置了url请求拦截的地址进行过滤器过滤,对没有配置拦截的url请求直接放行,不进行拦截。如果请求需要过滤,则处理过滤的逻辑由子类实现onPreHandle完成。

 

6、AccessControlFilter

如果用户没有认证(即登录),那么这个过滤器就是控制访问资源和用户重定向到登录页面的过滤器的父类。当一个用户没有认证(即登录)时,可以通过saveRequestAndRedirectToLogin这个方法,重定向到登录页面。

以上是该类的文档说明,实际还有一些其它的逻辑在里面,接着5、PathMatchingFilter的思路走,我们得看看onPreHandle中的实现。

其中调用了二个方法的逻辑或,isAccessAllowed和onAccessDenied。

isAccessAllowed如果请求允许正常处理,返回true。否则返回false由方法onAccessDenied进行处理请求。即isAccessAllowed返回true表示用户已登录过,false表示用户还未登录。

onAccessDenied即登录验证在isAccessAllowed方法中被拒绝以后调用,其中参数mappedValue可以通过配置获取到,也可以为null。这个方法的委托方法isAccessAllowed(request, response)处理,只有2个参数,大多数被isAccessAllowed方法拒绝后的行为(即isAccessAllowed返回false),再做处理时不需要mappedValue这个配置参数。

总结:AccessControlFilter中的onPreHandle处理真正的拦截逻辑,isAccessAllowed方法验证用户是否登录,onAccessDenied处理用户没登录后的逻辑,在这个过滤器中并没有给出isAccessAllowed和onAccessDenied方法的实现,下一步得去子类中看,目前只能通过文档的注释去了解这些方法大概会执行什么样的操作。

7、AuthenticationFilter

需要对当前用户认证的过滤器的基类,这个类封装了一些检查用户是否在系统中已经验证过的逻辑,子类需要对未验证的请求执行特定的逻辑。

AuthenticationFilter这个类的子类为AuthenticatingFilter,通过第6个过滤器可以知道,最后处理登录认验的实际上就是isAccessAllowed和onAccessDenied这二个方法。再根据文档的描述,这个类实现了isAccessAllowed的逻辑,而它的子类AuthenticatingFilter实现了onAccessDenied的逻辑。

先看isAccessAllowed方法:

先取出当前用户,然后再返回当前用户是否已认证。

通过以上逻辑可以看到,如果用户已认证(即用户已登录),那么用户的请户的将会往后执行,不再进行过滤拦截,如果用户没有登录,那么将会执行后面onAccessDenied方法中的内容。而onAccessDenied是在其子类中实现的。

总结:AuthenticationFilter实现了isAccessAllowed方法,如果用户已登录,那么过滤器将直接放行,如果用户没有登录,那么再由其子类中的onAccessDenied方法处理后续逻辑。

 

8、AuthenticatingFilter


该类会尝试基于用户的请求,自动的去执行一些身份认证。

先看下这个过滤器所有的方法。

该类并没有实现onAccessDenied方法。而是提供了一些登录,创建用户,和登录成功或失败后的重定向跳转等方法。

以下是登录:

如果登录成功执行onLoginSuccess,失败则执行onLoginFailure。

总结:AuthenticatingFilter提供了一些如登录,和重定向跳转的方法,并没有onAccessDenied方法的实现,那么这个实现就只能在最后一个子类FormAuthenticationFilter中完成了。

 

9、FormAuthenticationFilter

要求请求用户进行身份认证,以便使请求继续,如果没有认证,则强制让该请求重定向到你配置中的登录URL(loginUrl)。

这个构造器会构造一个UsernamePasswordToken对象,里面包含了username, password,和rememberMe这三个请求参数。当调用Subject.login(usernamePasswordToken)方法时,它会尝试自动的执行登录操作,要注意的是,这个尝试登录的操作仅仅只会在isLoginSubmission(request,response)返回true且是一个POST请求的登录操作。

如果尝试登录失败,则会将AuthenticationException异常写入到request的属性当中,这个属性的key是是failureKeyAttribute(这是个变量的名字,其值为shiroLoginFailure),FQCN能用作i18n的key或查找机制,向用户解释为什么会登录失败。(也就是说,如果被拦截,可以在request. getAttribute(“shiroLoginFailure”)中得到返回的错误消息)。

如果你想用你自己的代码处理身份认证和登录,可以用使用PassThruAuthenticationFilter,它允许loginUrl的请求直接传递到你自己的容器代码中去。

这个文档说明比较长,我们这里主要看onAccessDenied的实现。

1、  isLoginRequest是否是登录请求,如果不是登录请求,直接通过saveRequestAndRedirectToLogin方法返回到登录页面(这里的登录请求指的是loginUrl,它有一个默认地址是/login.jsp)。

saveRequestAndRedirectToLogin这个方法在AccessControlFilter中实现。

2、如果是登录请求,则通过isLoginSubmission判断是否是http的post请求。如果不是则返回false,说明用户请求的是登录页面的get请求,用户就是直接通过浏览器访问登录页面的,这时只需要返回true,放行就可以了。如果isLoginSubmission返回true,表明用户是一个http的post请求,并且是访问登录的url请求。

3、如果用户是一个http的post请求,那么就执行executeLogin(request, response)方法做登录操作。

首先会从createToken中把请求参数封装了token(可以理解为一个登录对象),这个token中包含了用户名密码。

其中用户名,密码是一个固定的变量值username,password。

即页请求传过来的参数默认是username和password。它会根据这2个参数封装成authenticationToken对象。然后根据这个对象进行登录操作。(一般这个操作都是会在我们自定义的realm中完成,这里就暂不介绍登录)

登录成功,直接执行onLoginSuccess(token, subject, request, response)方法。

登录成功执行issueSuccessRedirect方法重定向到登录成功的页面,然后返回false(这里返回false是因为用户是请求的登录操作,然后被authc过滤器给拦截掉并且登录成功了,就没必需继续再往后面走了。),注意:这里的onLoginSuccess方法是FormAuthenticationFilter中的,不是AuthenticatingFilter中的。

登录失败则执行onLoginFailure方法,这个方向会在request中的属性中存入失败原因,key值为:shiroLoginFailure,并且最终返回true,意味着用户的登录请求最后到达到我们的后台,只是在过滤器中已经做过一次登录了,并且是登录失败,所以我们自己的登录地址在编码时不需要再重复做登录认证操作,只需要从request中取出shiroLoginFailure认证报错信息,做相应的逻辑处理就可以了。

总结:FormAuthenticationFilter实现了认证失败后的处理逻辑,即用户在未登录情况下处理的后续操作,如果用户是一个非登录请求,那么会直接重定向到登录页面(即配置中的loginUrl页面),如果是一个登录请求,且是GET请页,那么直接放行登录请求,如果是POST请求,则会从请求中获取默认的username,password去尝试登录,如果登录成功,则由过滤器直接重定向到登录成功的url上去,并拦截掉用户的请求。如果登录失败,则由过滤器直接重定向到登录失败的url上去,并将失败信息写入到request中去,key值为shiroLoginFailure,然后过滤器放行,直接到达用户请求的地址,我们就可以在这个请求地址中取到登录失败的数据做相应的操作了。

最后梳理一下全部流程中方法的调用。

首先过滤器会调用doFilter(在OncePerRequestFilter中)方法,然后再调用doFilterInternal方法(在AdviceFilter中),然后再调用preHandle、executeChain、postHandle(在AdviceFilter中)这3个方法,实际拦截业务在preHandle方法中,然后再调用onPreHandle(在AccessControlFilter中)方法,然后再调用isAccessAllowed(在AuthenticationFilter中)方法和onAccessDenied(在FormAuthenticationFilter中)方法。

shiro过滤器详解分析的更多相关文章

  1. MVC过滤器详解

    MVC过滤器详解   APS.NET MVC中(以下简称"MVC")的每一个请求,都会分配给相应的控制器和对应的行为方法去处理,而在这些处理的前前后后如果想再加一些额外的逻辑处理. ...

  2. HttpRuntime详解分析

    HttpRuntime详解分析(上) 文章内容 从上章文章都知道,asp.net是运行在HttpRuntime里的,但是从CLR如何进入HttpRuntime的,可能大家都不太清晰.本章节就是通过深入 ...

  3. Asp.Net MVC学习总结之过滤器详解(转载)

    来源:http://www.php.cn/csharp-article-359736.html   一.过滤器简介 1.1.理解什么是过滤器 1.过滤器(Filters)就是向请求处理管道中注入额外的 ...

  4. 转:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法、shiro认证与shiro授权

    原文地址:JAVAWEB开发之权限管理(二)——shiro入门详解以及使用方法.shiro认证与shiro授权 以下是部分内容,具体见原文. shiro介绍 什么是shiro shiro是Apache ...

  5. lspci详解分析

    lspci详解分析 一.PCI简介 PCI是一种外设总线规范.我们先来看一下什么是总线:总线是一种传输信号的路径或信道.典型情况是,总线是连接于一个或多个导体的电气连线,总 线上连接的所有设备可在同一 ...

  6. Wireshark过滤器详解

    Wireshark过滤器详解 1.Wireshark主要提供两种主要的过滤器 捕获过滤器:当进行数据包捕获时,只有那些满足给定的包含/排除表达式的数据包会被捕获 显示过滤器:该过滤器根据指定的表达式用 ...

  7. angular-ngSanitize模块-linky过滤器详解

    本篇主要讲解angular中的linky这个过滤器.此过滤器依赖于ngSanitize模块. linky能找出文本中的链接,然后把它转换成html链接.什么意思,就是说,一段文本里有一个链接,但是这个 ...

  8. JavaScript 内存详解 & 分析指南

    前言 JavaScript 诞生于 1995 年,最初被设计用于网页内的表单验证. 这些年来 JavaScript 成长飞速,生态圈日益壮大,成为了最受程序员欢迎的开发语言之一.并且现在的 JavaS ...

  9. Shiro学习详解

    1.Shiro基本架构 一.什么是Shiro Apache Shiro是一个强大易用的Java安全框架,提供了认证.授权.加密和会话管理等功能: 认证 - 用户身份识别,常被称为用户“登录”: 授权 ...

随机推荐

  1. Linux 用户及权限详解

    Linux 用户及权限详解 用户 , 组 ,权限 安全上下文(secure context): 权限: r,w,x 文件: r : 可读,可以使用类似cat 等命令查看文件内容. w : 可写,可以编 ...

  2. 学习iOS最权威的网站

    一.Apple  开发者网站 官方文档 https://developer.apple.com/documentation 除了这两个.还有 Core Foundation  Core Animati ...

  3. 一键解决更改计算机名后无法启动MSSQLSERVER服务问题

    问题版本:SQL Server 2012. 解决办法:打开服务,Win + R运行services.msc,找到 SQL SERVER(MSSQLSERVER)服务右键->属性,切换至登录选项卡 ...

  4. C++ 最简单的日志类

    最近搞一个 C++ 项目的二次开发,没玩过 C++,可谓步履维艰.自己写个简单的日志类都被各种坑折磨.终于搞定了. 参考了这篇博客,并且进一步简化:https://www.cnblogs.com/Ds ...

  5. [AI分享]零高数理解人工智能和深度学习

  6. 字典fromkeys方法和update方法

    #Author : Kelvin #Date : 2019/1/17 15:27 #字典的update方法,是向调用者字典中添加另外一个字典 dict1 = {"name":&qu ...

  7. docker初体验,搭建自用的gitlab服务

    一.前言 git在如日中天的版本管理系统,现在如果不是工作在git版本管理系统下,几乎都不好意思给人打招呼.现在就有现成的互联网的git服务器提供给大家使用,例如号称程序的社交网络github. 正好 ...

  8. ASP.NET Core中使用GraphQL - 第四章 GraphiQL

    ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 ASP ...

  9. [深度概念]·K-Fold 交叉验证 (Cross-Validation)的理解与应用

    K-Fold 交叉验证 (Cross-Validation)的理解与应用 我的网站 1.K-Fold 交叉验证概念 在机器学习建模过程中,通行的做法通常是将数据分为训练集和测试集.测试集是与训练独立的 ...

  10. [开源]Entity Framework 6 Repository 一种实现方式

    在使用Entity Framework这种ORM框架得时候,一般结合Repository仓储形式来处理业务逻辑:虽然这种模式带来很多好处,但是也会引发一些争议,在此抛开不谈,小弟结合项目经验来实现一下 ...