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