一、目標
通過自定義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;
}