shiro 自定义拦截器

1.拓展 OncePerRequestFilter 

用于防止多次执行 Filter 的;也就是说一次请求只会走一次拦截器链;另外提供 enabled 属性,表示是否开启该拦截器实例,默认 enabled=true 表示开启,如果不想让某个拦截器工作,可以设置为 false 即可。

保证一次请求只调用一次 doFilterInternal,即如内部的 forward 不会再多执行一次 doFilterInternal

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.web.servlet.OncePerRequestFilter;

/**
 * 自定义的OncePerRequestFilter拓展
 * doFilterInternal只执行一次
 * @author cxy
 *
 */
public class OncePerRequestFilterTest extends OncePerRequestFilter{

	@Override
	protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		System.out.println("一次请求只调用一次");
		chain.doFilter(request, response);
		
	}

}

spring-context-shiro.xml  配置拦截器

  <bean id="oncePerRequestFilter" class="com.cxy.filter.testFilter.OncePerRequestFilterTest"/> 
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
     <!-- 调用我们配置的权限管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 配置我们的登录请求地址 -->
        <property name="loginUrl" value="/login" />
        <!-- 退出 -->
        <property name="filters">
            <util:map>
                 <entry key="once" value-ref ="oncePerRequestFilter" />
            </util:map>
        </property>
        <!-- 权限配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- anon表示此地址不需要任何权限即可访问 -->
                /login=anon
                <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
              <!--  /** = authc-->  
    
                /** = once
            </value>
        </property>
    </bean>

2、扩展 AdviceFilter 

AdviceFilter 提供了 AOP 风格的支持,类似于 SpringMVC 中的 Interceptor:

boolean preHandle(ServletRequest request, ServletResponse response) throws Exception 
void postHandle(ServletRequest request, ServletResponse response) throws Exception 
void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) 
throws Exception;

preHandler:类似于 AOP 中的前置增强;在拦截器链执行之前执行;如果返回 true 则继续
拦截器链;返回false将中断后续的拦截器链的执行直接返回;进行预处理(如基于表单的身份验
证、授权)
postHandle:类似于 AOP 中的后置返回增强;在拦截器链执行完成后执行;进行后处理(如
记录执行时间之类的);
afterCompletion:类似于 AOP 中的后置最终增强;即不管有没有异常都会执行;可以进行
清理资源(如接触 Subject 与线程的绑定之类的);

package com.cxy.filter.testFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.web.servlet.AdviceFilter;

public class MyAdviceFilter extends AdviceFilter{
	@Override 
	 protected boolean preHandle(ServletRequest request, ServletResponse response) throws 
	Exception { 
	 System.out.println("====预处理/前置处理"); 
	 return true;//返回 false 将中断后续拦截器链的执行
	 } 
	 @Override 
	 protected void postHandle(ServletRequest request, ServletResponse response) throws 
	Exception { 
	 System.out.println("====后处理/后置返回处理"); 
	 } 
	 @Override 
	 public void afterCompletion(ServletRequest request, ServletResponse response, Exception 
	exception) throws Exception { 
	 System.out.println("====完成处理/后置最终处理"); 
	 } 
	
}
  <bean id="myAdviceFilter" class="com.cxy.filter.testFilter.MyAdviceFilter"/> 
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
     <!-- 调用我们配置的权限管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 配置我们的登录请求地址 -->
        <property name="loginUrl" value="/login" />
        <!-- 退出 -->
        <property name="filters">
            <util:map>
                 <entry key="advice" value-ref ="myAdviceFilter" />
            </util:map>
        </property>
        <!-- 权限配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- anon表示此地址不需要任何权限即可访问 -->
                /login=anon
                <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
              <!--  /** = authc-->  
    
                /** = advice
            </value>
        </property>
    </bean>

 

3.、PathMatchingFilter  

继承了AdviceFilter,提供了 url 模式过滤的功能,如果需要对指定的请求进行处理,可以扩展 PathMatchingFilter:

只继承onPreHandle就行了.

  • preHandle:会进行 url 模式与请求 url 进行匹配,如果匹配会调用 onPreHandle;如果没有配置 url 模式/没有 url 模式匹配,默认直接返回 true;
  • onPreHandle:如果 url 模式与请求 url 匹配,那么会执行 onPreHandle,并把该拦截器配置的参数传入。默认什么不处理直接返回 true
package com.cxy.filter;

import java.util.Arrays;
import java.util.Set;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.cxy.service.PermissionService;
/**
 * 1. 如果没登录就跳转到登录
2. 如果当前访问路径没有在权限系统里维护,则允许访问
3. 当前用户所拥有的权限如何不包含当前的访问地址,则跳转到/unauthorized,否则就允许访问
 * @author Administrator
 *
 */

public class URLPathMatchingFilter extends PathMatchingFilter {
	@Autowired
	PermissionService permissionService;

	@Override
	protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue)
			throws Exception {
		String requestURI = getPathWithinApplication(request);

		System.out.println("requestURI:" + requestURI);

		Subject subject = SecurityUtils.getSubject();
		// 如果没有登录,就跳转到登录页面
		if (!subject.isAuthenticated()) {
			WebUtils.issueRedirect(request, response, "/login");
			return false;
		}

		// 看看这个路径权限里有没有维护,如果没有维护,一律放行(也可以改为一律不放行)
		boolean needInterceptor = permissionService.needInterceptor(requestURI);
		if (!needInterceptor) {
			return true;
		} else {
			boolean hasPermission = false;
			String userName = subject.getPrincipal().toString();
			Set<String> permissionUrls = permissionService.listPermissionURLs(userName);
			for (String url : permissionUrls) {
				// 这就表示当前用户有这个权限
				if (url.equals(requestURI)) {
					hasPermission = true;
					break;
				}
			}

			if (hasPermission) {
				System.out.println("有权限时打印mappedValue");
				System.out.println("url matches,config is " + Arrays.toString((String[])mappedValue));
				return true;
			}
			else {
				UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径 " + requestURI + " 的权限");

				subject.getSession().setAttribute("ex", ex);

				WebUtils.issueRedirect(request, response, "/unauthorized");
				return false;
			}

		}

	}
}
<bean id="urlPathMatchingFilter" class="com.cxy.filter.URLPathMatchingFilter"/> 
  
    <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 调用我们配置的权限管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 配置我们的登录请求地址 -->
        <property name="loginUrl" value="/login" />
        <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
        <property name="unauthorizedUrl" value="/unauthorized" />
        <!-- 退出 -->
        <property name="filters">
            <util:map>
                <entry key="logout" value-ref="logoutFilter" />
                 <entry key="url" value-ref="urlPathMatchingFilter" />
           
            </util:map>
        </property>
        <!-- 权限配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- anon表示此地址不需要任何权限即可访问 -->
                /login=anon
                /index=anon
                /static/**=anon
                /config/**=anon
                /doLogout=logout  
                <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
              <!--  /** = authc-->  
                /** = url[config,config2]    
            </value>
        </property>
    </bean>

注意:这里/**=url[config1,config2] 

/**就是注册给 PathMatchingFilter 的 url 模式,config 就是拦截器的配置参数,可以设置一些角色多个之间逗
号分隔,onPreHandle 使用 mappedValue 接收参数值。

扩展 AccessControlFilter 

AccessControlFilter 提供了访问控制的基础功能;比如是否允许访问/当访问拒绝时如何处理等

abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object 
mappedValue) throws Exception; 
boolean onAccessDenied(ServletRequest request, ServletResponse response, Object 
mappedValue) throws Exception; 
abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws 
Exception; 

isAccessAllowed:表示是否允许访问;mappedValue 就是[urls]配置中拦截器参数部分,如
果允许访问返回 true,否则 false;
onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回 true 表示需要继续处理;
如果返回 false 表示该拦截器实例已经处理了,将直接返回即可。
onPreHandle 会自动调用这两个方法决定是否继续处理:

boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) 
throws Exception { 
 return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, 
response, mappedValue); 
} 

另外 AccessControlFilter 还提供了如下方法用于处理如登录成功后/重定向到上一个请求

void setLoginUrl(String loginUrl) //身份验证时使用,默认/login.jsp 
String getLoginUrl() 
Subject getSubject(ServletRequest request, ServletResponse response) //获取 Subject 实例
boolean isLoginRequest(ServletRequest request, ServletResponse response)//当前请求是否是
登录请求
void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) 
throws IOException //将当前请求保存起来并重定向到登录页面
void saveRequest(ServletRequest request) //将请求保存起来,如登录成功后再重定向回该请
求
void redirectToLogin(ServletRequest request, ServletResponse response) //重定向到登录页面

使用:AccessControlFilter 继承了 PathMatchingFilter,并扩展了了两个方法

isAccessAllowed:即是否允许访问,返回 true 表示允许;
onAccessDenied:表示访问拒绝时是否自己处理,如果返回 true 表示自己不处理且继续拦
截器链执行,返回 false 表示自己已经处理了(比如重定向到另一个页面)

应用:系统仅限在2019-1-29登录(举个栗子)

package com.cxy.filter.testFilter;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.web.filter.AccessControlFilter;

public class MyAccessControlFilter extends  AccessControlFilter{
	
	/**
	 * 是否允许访问,
	 * 应用:在特定时间允许访问
	 */
	@Override
	protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
		System.out.println("仅限2019-1-29号访问");
	    String today = "2019-1-29";//已经设置今天,无法运行
	    
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd");
        Date now =sdf.parse(today);
        Date d = new Date();
  
        if(d.getTime()>now.getTime()+1000*60*60*24){
            System.err.println("——————未成成功运行——————");
            System.err.println("——————未成成功运行——————");
            System.err.println("本页面维护中,today变量为今天,如:" + sdf.format(new Date()));
            return false;
        }
  
        if(false) {
            return false;
        }
		return true;
	}

	@Override
	protected boolean onAccessDenied(ServletRequest reuqest, ServletResponse response) throws Exception {
		System.out.println("访问拒绝也不自己处理,继续拦截器链的执行");
		return true;
		/*返回一个重定向页面(还没有做),表示自己处理了
		return false  
		*/
	}

}

spring-context-shiro.xml里配置


 默认拦截器

 

 

例子:

               /login=anon
                /index=anon
                /static/**=anon
                /config/**=anon
                /doLogout=logout   

猜你喜欢

转载自blog.csdn.net/qq_38930240/article/details/86685389