Shiro权限控制之自定义Filter(二)

时间:2024-04-09 07:38:20

一、目标
通过自定义Filter实现权限配置,如某个URL需要某个角色的某个权限才能操作

二、前言
在前面的一篇博文《Shiro权限控制之整合Spring(一)》中,介绍了如何在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,如下列表
Shiro权限控制之自定义Filter(二)
在没有自定义的情况下,默认如
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方法,源码如下
Shiro权限控制之自定义Filter(二)

因此我们自定义的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;
	}