Shiro学习之过滤器详解

一、过滤器种类

在shiro中,除过登录认证操作,最核心的就属一些列过滤器
shiro过滤器以及对应的类:过滤器
shiro默认过滤器

二、过滤器详解

authc过滤器为例,对应的处理器类是FormAuthenticationFilter

1. 继承关系及结构

在这里插入图片描述
authc继承关系:
1.AbstractFilter > 2.NameableFilter > 3.OncePerRequestFilter > 4.AdviceFilter > 5.PathMatchingFilter > 6.AccessControlFilter > 7.AuthenticationFilter > 8.AuthenticatingFilter > 9.FormAuthenticationFilter

2. filter过滤器简介

tomcat中的自定义过滤器必需实现Filter接口,过滤器的实现方法为Filter中的
doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain),如果一个请求被该过滤器拦截,想要到达用户最后请求的地址,那么一定得有:filterChain.doFilter(request, response);这个方法的调用,表示通过过滤,请求可以到达用户的请求地址。否则用户的请求地址不会被调用,用户的请求被过滤器拦截

3. shiro过滤器分析

3.1 AbstractFilter

源码:

package org.apache.shiro.web.servlet;
public abstract class AbstractFilter extends ServletContextSupport implements Filter {
    private static final transient Logger log = LoggerFactory.getLogger(AbstractFilter.class);
    protected FilterConfig filterConfig;
    public AbstractFilter() {
    }
    ... ...
 }

从源码中可以看到AbstractFilter 是个抽象类,直接实现了Filter接口
Filter.class
在这里插入图片描述
AbstractFilter .class
在这里插入图片描述
总结:AbstractFilter只做了一些基本的初始化,并没有过多的逻辑在里面,没有实现Filter中的doFilter方法

3.2 NameableFilter

在这里插入图片描述
大致意思是:允许通过getName和setName方式给过滤器命名。如果没有给该过滤器命名,那么默认将会使用web.xml中给定的名称(FilterConfiger的filterName)
在这里插入图片描述
总结:NameableFilter是用于给过滤器命名使用的,没有实现Filter中的doFilter方法

3.3 OncePerRequestFilter

在这里插入图片描述
大致意思是:filter的基类(就是真正实现了Filter中的doFilter方法的类),它用来保证在每个servlet容器上,第个请求都只会被过滤一次,不会重复过滤。
通过getAlreadyFilteredAttributeName()方法来鉴别请求是否已经被过滤过。默认的实现基于具体过滤的实例名称。

doFilter()具体实现逻辑:
在这里插入图片描述

  1. 判断过滤器是否已经执行过过滤,如果执行过,调用filterChain.doFilter(request, response)直接放行,不再重复执行该过滤器的处理逻辑,直接走下一个过滤器或者通过该过滤进入到实际请求方法中。
  2. 判断过滤器是否开启,该类有个成员变量eabled,默认为true(注释给的解释是大多数的过滤器都是希望开启的,所以默认值为true),过滤器为开启状态,如果该过滤器没有被开启中,也同上面的逻辑一样,直接调用filterChain.doFilter(request, response)走下一个过滤器或者通过该过滤进入到实际请求方法中。
    在这里插入图片描述
  3. 分为三步:
    (1)设置一个已经执行过滤器的属性名称在request中,默认为TRUE
    (2)调用doFilterInternal方法,执行真正的过滤器处理逻辑
    (3)这个过滤器处理完后,将过滤器的属性名称从request中移出。这样如果程序执行到第2步,过滤又被调用了,它将会走到第一个if中,直接略过这个过滤器的处理,这样就保证了,每个过滤器在处理一个请求的时候只会被执行一次
    总结:OncePerRequestFilter提供了一个实际处理过滤业务的方法doFilterInternal,并且保证每个请求只会被该过滤器过滤一次。

3.4 AdviceFilter

在这里插入图片描述
大致意思是:类似于开启了AOP环绕通知,提供了preHandle,postHandle和afterCompletion这三个方法

其过滤逻辑为:doFilterInternal方法
在这里插入图片描述

  1. executeChain方法中的实现为:chain.doFilter(request, response);所以如果preHandle方法返回false,则说明过滤器不会执行chain.doFilter,意味着请求被拦截掉了,不会进入到用户请求的地址上去。如果为true,表示过滤器放行了过滤的逻辑通过
  2. 然后会执行postHandle方法(该类中的postHandle方法为空实现)
  3. 最后会执行一个,这个方法用于对一些异常进行处理(目前也是空实现)
    总结:AdviceFilter提供了类似于AOP环绕通知式的编程方式,其处理拦截的逻辑是在preHandle方法中完成的。preHandle方法返回true和false代表了通过过滤,请求可以到达用户的请求地址和过滤器拦截掉了用户的请求

3.5 PathMatchingFilter

在这里插入图片描述
大致意思是:这个过滤器会处理指定的请求路径,和对其它路径的请求放行。
比如:如果配置如下拦截,/hello=authc,这就意味着,请用户请求/hello时,这时的authc过滤就会对这个请求拦截并进行过滤逻辑处理。如果一个用户请求是/world,则不会对此请求过滤
在这里插入图片描述

  1. 判断appliedPaths为空,则放行,不作拦截请求的处理。这里的appliedPaths是一个地址过滤器的映射map。key存放的是请求的URL,value是对应该URL处理的Filter过滤器,这个 value也可以是空值。
    在这里插入图片描述
  2. 如果用户的请求与配置中拦截的请求匹配,则会调用isFilterChainContinued方法进行下一步处理。如果拦截器是开启的,则调用方法onPreHandle进行拦截处理.
    在这里插入图片描述
    onPreHandle方法默认返回true,也不对请求进行拦截处理,所以如果需要有自定义处理拦截的逻辑,需要由子类覆盖这个方法来完成
    在这里插入图片描述
    总结:PathMatchingFilter对配置了url请求拦截的地址进行过滤器过滤,对没有配置拦截的url请求直接放行,不进行拦截。如果请求需要过滤,则处理过滤的逻辑由子类实现onPreHandle完成

3.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方法的实现,下一步得去子类中看,目前只能通过文档的注释去了解这些方法大概会执行什么样的操作

3.7 AuthenticationFilter

在这里插入图片描述
大致意思是:需要对当前用户认证的过滤器的基类,这个类封装了一些检查用户是否在系统中已经验证过的逻辑,子类需要对未验证的请求执行特定的逻辑
AuthenticationFilter这个类的子类为AuthenticatingFilter,通过第6个过滤器可以知道,最后处理登录认验的实际上就是isAccessAllowed和onAccessDenied这二个方法。再根据文档的描述,这个类实现了isAccessAllowed的逻辑,而它的子类AuthenticatingFilter实现了onAccessDenied的逻辑

isAccessAllowed方法:
先取出当前用户,然后再返回当前用户是否已认证。
在这里插入图片描述
AccessControlFilter中的onPreHandle():
在这里插入图片描述
总结:AuthenticationFilter实现了isAccessAllowed方法,如果用户已登录,那么过滤器将直接放行,如果用户没有登录,那么再由其子类中的onAccessDenied方法处理后续逻辑

3.8 AuthenticatingFilter

在这里插入图片描述
大致意思是:该类会尝试基于用户的请求,自动的去执行一些身份认证
先看下这个过滤器所有的方法:
在这里插入图片描述
该类并没有实现onAccessDenied方法。而是提供了一些登录,创建用户,和登录成功或失败后的重定向跳转等方法。
以下是登录方法,如果登录成功执行onLoginSuccess,失败则执行onLoginFailure
在这里插入图片描述
总结:AuthenticatingFilter提供了一些如登录,和重定向跳转的方法,并没有onAccessDenied方法的实现,那么这个实现就只能在最后一个子类FormAuthenticationFilter中完成了

3.9 FormAuthenticationFilter

在这里插入图片描述
大致意思是:

  1. 要求请求用户进行身份认证,以便使请求继续,如果没有认证,则强制让该请求重定向到你配置中的登录URL(loginUrl)
  2. 这个构造器会构造一个UsernamePasswordToken对象,里面包含了username, password,和rememberMe这三个请求参数。当调用Subject.login(usernamePasswordToken)方法时,它会尝试自动的执行登录操作,要注意的是,这个尝试登录的操作仅仅只会在isLoginSubmission(request,response)返回true且是一个POST请求的登录操作。
  3. 如果尝试登录失败,则会将AuthenticationException异常写入到request的属性当中,这个属性的key是是failureKeyAttribute(这是个变量的名字,其值为shiroLoginFailure),FQCN能用作i18n的key或查找机制,向用户解释为什么会登录失败。(也就是说,如果被拦截,可以在request. getAttribute(“shiroLoginFailure”)中得到返回的错误消息)

这个文档说明比较长,我们这里主要看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,然后过滤器放行,直接到达用户请求的地址,我们就可以在这个请求地址中取到登录失败的数据做相应的操作了

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

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

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 在 Spring Boot 中使用 shiro 配置自定义过滤器需要以下几个步骤: 1. 引入 shiro-spring-boot-starter 依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建自定义过滤器: ``` public class CustomFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { // 在这里实现自定义的过滤逻辑,返回 true 表示通过过滤器,返回 false 表示未通过过滤器 return true; } @Override protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { // 如果 isAccessAllowed 返回 false,则会进入到这里,可以在这里处理未通过过滤器的情况 return false; } } ``` 3. 配置 shiro 的 FilterChainDefinition: ``` @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); // 添加自定义过滤器,其中 key 是过滤器名称,value 是该过滤器对应的路径 chainDefinition.addPathDefinition("/custom/**", "custom"); return chainDefinition; } ``` 4. 配置自定义过滤器: ``` @Bean("custom") public CustomFilter customFilter() { return new CustomFilter(); } ``` 5. 配置 shiro 的注解支持: ``` @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } ``` 完成以上步骤后,就可以在 Spring Boot 中使用 shiro 配置自定义过滤器了。 ### 回答2: 在 Spring Boot 中使用 Shiro 配置自定义过滤器分为三个步骤。 第一步,创建自定义过滤器类。可以通过实现 Shiro 的 Filter 接口来创建自定义过滤器。在自定义过滤器中需要实现过滤规则,并对请求进行相应的处理。 第二步,配置 Shiro 过滤器链。在 Spring Boot 的配置类中,通过创建 ShiroFilterFactoryBean 对象来配置 Shiro过滤器链。可以使用 Shiro 的 FilterChainDefinitionMap 对象来配置过滤器链,然后将该对象设置给 ShiroFilterFactoryBean。 第三步,启用 Shiro 过滤器。在 Spring Boot 的配置类中,通过创建 DefaultFilterChainManager 对象,并将该对象设置给 ShiroFilterFactoryBean,启用自定义过滤器。 有了以上三步,就可以在 Spring Boot 中使用 Shiro 配置自定义过滤器了。可以通过在自定义过滤器中实现过滤规则来对请求进行拦截或处理,然后在 Shiro 过滤器链中配置该过滤器,最后启用该过滤器。这样就可以实现对请求的自定义过滤器处理。 值得注意的是,在使用 Shiro 进行自定义过滤器配置时,需要保证 Shiro 的配置文件中已经进行了相应的配置,包括认证和授权等相关配置。只有在正确配置的前提下,才能正确使用 Shiro 进行自定义过滤器的配置。 ### 回答3: 在Spring Boot中使用Shiro配置自定义过滤器通常需要以下几个步骤: 1. 引入ShiroSpring Boot依赖。在pom.xml文件中添加ShiroSpring Boot Starter依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 创建自定义过滤器类。可以通过实现`javax.servlet.Filter`接口或者继承`org.apache.shiro.web.servlet.OncePerRequestFilter`类来创建自定义过滤器。例如,创建一个名为`CustomFilter`的自定义过滤器类: ``` public class CustomFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 过滤器逻辑处理 // ... filterChain.doFilter(request, response); } } ``` 3. 在Shiro配置类中注册自定义过滤器。创建一个Shiro配置类,并使用`@Configuration`注解标记为配置类。通过`@Bean`注解将自定义过滤器注册到Shiro过滤器链中。例如,在配置类`ShiroConfig`中注册`CustomFilter`: ``` @Configuration public class ShiroConfig { @Bean public FilterRegistrationBean<CustomFilter> customFilterRegistrationBean() { FilterRegistrationBean<CustomFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new CustomFilter()); registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 过滤器执行顺序 registrationBean.addUrlPatterns("/*"); // 过滤器路径 return registrationBean; } } ``` 4. 配置Shiro的过滤规则。在Shiro配置文件中,可以设置自定义过滤器的拦截规则。例如,在`shiro.ini`配置文件中,设置自定义过滤器的拦截规则: ``` [urls] /** = customFilter // 对所有请求都使用自定义过滤器 ``` 通过以上步骤,在Spring Boot中使用Shiro配置自定义过滤器就可以实现对特定请求的拦截和处理。在`CustomFilter`类的`doFilterInternal`方法中编写自定义的过滤器逻辑,例如鉴权、权限验证等。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一位不知名民工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值