一、目标
通过自定义Filter实现权限配置,如某个URL需要某个角色的某个权限才能操作
二、前言
在前面的一篇博文《Shiro权限控制(一):Spring整合Shiro》中,介绍了如何在Spring MVC中整合Shiro权限框架,其中在spring-shiro-web.xml配置文件中,有个 id="shiroFilter"的bean org.apache.shiro.spring.web.ShiroFilterFactoryBean,在这个bean中,定义了URL的访问规则,这些访问规则,就是通过Shiro的Filter实现的,下面就来介绍如何实现自定义的Filter来实现访问控制
三、Shiro内置Filter
在Shiro框架中,已经提供了很多内置的Filter,其中常见的有anon,authc,perms,roles,如下列表
在没有自定义的情况下,默认如
anon表示使用org.apache.shiro.web.filter.authc.AnonymousFilter处理
authc表示使用org.apache.shiro.web.filter.authc.FormAuthenticationFilter处理
如果想实现自定义Filter,就需要覆盖Shiro中内置的Filter,如何实现呢?
四、自定义Filter
上面说到在spring-shiro-web.xml配置文件中,有个 id="shiroFilter"的bean org.apache.shiro.spring.web.ShiroFilterFactoryBean,在这个Bean中有两个非常重要的属性filterChainDefinitions和filters
filterChainDefinitions:定义了URL的访问规则,从源码中可以看到,这些规则最后存储在ShiroFilterFactoryBean的filterChainDefinitionMap属性中,key值是URL规则,value值是filter的引用,表示使用哪些Filter处理URL
filters:属性filters是一个Map集合,key值是Filter Name,value值是具体的Filter类
下面我们自定义一个perms的Filter,来处理 /user/deleteUser/**,需要USER角色的删除权限才可以访问,如下配置
<!-- 自定义登录验证过滤器 -->
<bean id="loginCheckPermissionFilter" class="com.bug.filter.LoginCheckPermissionFilter"></bean>
<!-- 自定义权限认证器 -->
<bean id="permissionsAuthorizationFilter" class="com.bug.filter.PermissionsAuthorizationFilter"></bean>
<!-- Shiro的web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<!-- <property name="unauthorizedUrl" value="/index.jsp"></property> -->
<property name="filters">
<map>
<entry key="authc" value-ref="loginCheckPermissionFilter"></entry>
<entry key="perms" value-ref="permissionsAuthorizationFilter"></entry>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/user/queryUserInfo = authc
/user/deleteUser/** = perms[USER:DELETE]
</value>
</property>
</bean>
上面的配置, 在filterChainDefinitions中配置了/user/deleteUser/**=perms[USER:DELETE],表示需要有USER角色的删除权限才能访问,而perms又指向了filters中定义的perms,该perms使用permissionsAuthorizationFilter处理,从而覆盖了默认的 org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter,默认的PermissionsAuthorizationFilter,继承了AuthorizationFilter,并重写isAccessAllowed方法,源码如下
因此我们自定义的permissionsAuthorizationFilter,也需要继承AuthorizationFilter,并重写isAccessAllowed方法,如下
package com.bug.filter;
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import com.bug.common.JSONParseUtils;
import com.bug.model.common.ResponseVO;
/**
* 权限校验过滤器
* @author longwentao
*
*/
public class PermissionsAuthorizationFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
Subject subject = getSubject(request, response);
String[] perms = (String[]) mappedValue;
boolean isPermitted = true;
if (perms != null && perms.length > 0) {
if (perms.length == 1) {
if (!subject.isPermitted(perms[0])) {
isPermitted = false;
}
} else {
if (!subject.isPermittedAll(perms)) {
isPermitted = false;
}
}
}
return isPermitted;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("Content-Type", "application/json;charset=UTF-8");
ResponseVO<String> responseVO = new ResponseVO<String>();
responseVO.setStatus(ResponseVO.failCode);
responseVO.setMessage("没有权限,请联系管理员");
byte[] bytes = JSONParseUtils.readJson2Byte(responseVO);
httpServletResponse.getOutputStream().write(bytes);
return false;
}
}
到此,自定义Filter已经完成了,下面补充一下如何将权限给Shiro框架
可以在用户身份证验证通过后,从DB中获得用户的角色权限放到AuthorizationInfo中,并返回给Shiro框架,该代码是在自定义的Realm中实现的,可参考前面的文章《Shiro权限控制之整合Spring(一)》
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
if(username == null) {
throw new BugException("未登录");
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<String>();
Set<String> stringPermissions = new HashSet<String>();
roles.add("USER");
stringPermissions.add("USER:DELETE");//角色:权限
info.setRoles(roles);
info.setStringPermissions(stringPermissions);
return info;
}