在使用Shiro进行权限判断的时候,某一个访问路径,可能只需要两种权限中的其中一个,就可以访问。但是Shiro中,提供的权限过滤,必须都满足两个权限之后,才能访问,这样会造成一些权限控制的不合理人性化。因此自定义权限过滤器是非常有必要的,在日后的开发中,自定义权限过滤器是很频繁的操作。
#需要同时拥有order:add和order:query权限才可以访问
/order-add = perms["order:add","order:query"]
#只需要order:del权限就可以访问
/order-del = perms["order:del"]
perms表示的就是权限控制,中括号中就是需要访问等号之前路径,需要的权限名称。如果在使用Shiro过滤器的时候,不配置过滤器,就会使用默认的过滤器。
以下是默认权限过滤器的源码。
public class PermissionsAuthorizationFilter extends AuthorizationFilter {
public PermissionsAuthorizationFilter() {
}
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = this.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;
}
}
从上面的代码可以看出,我们的配置会默认被强转为string类型的字符串数组。当只有一个权限时,会直接判断有没有该权限; 当配置多个权限时,从下面的代码可以看出只用在请求url的用户拥有所有的权限时,才会返回true,否则就会被拒绝访问。
shiro源码只实现了权限同时拥有或者单个拥有才能访问指定路径的问题,并没有实现用户拥有多个权限中的其中一个权限就可以访问指定路径的问题。
因此为了满足业务需求,在自定义拦截器中需要重写PermissionsAuthorizationFilter的isAccessAllowed方法。
public class PermissionOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
Subject subject = this.getSubject(request, response);
String[] perms = (String[]) ((String[]) mappedValue);
boolean isPermitted = true;
if (perms != null && perms.length > 0) {
if (perms.length == 1) {
if (!isOneOfPermitted(perms[0], subject)) {
isPermitted = false;
}
} else if (!isAllPermitted(perms,subject)) {
isPermitted = false;
}
}
return isPermitted;
}
/**
* 以“,”分割的权限为并列关系的权限控制,分别对每个权限字符串进行“|”分割解析
* 若并列关系的权限有一个不满足则返回false
*
* @param permStrArray 以","分割的权限集合
* @param subject 当前用户的登录信息
* @return 是否拥有该权限
*/
private boolean isAllPermitted(String[] permStrArray, Subject subject) {
boolean isPermitted = true;
for (int index = 0, len = permStrArray.length; index < len; index++) {
if (!isOneOfPermitted(permStrArray[index], subject)) {
isPermitted = false;
}
}
return isPermitted;
}
/**
* 判断以“|”分割的权限有一个满足的就返回true,表示权限的或者关系
*
* @param permStr 权限数组种中的一个字符串
* @param subject 当前用户信息
* @return 是否有权限
*/
private boolean isOneOfPermitted(String permStr, Subject subject) {
boolean isPermitted = false;
String[] permArr = permStr.split("\\|");
if (permArr.length > 0) {
for (int index = 0, len = permArr.length; index < len; index++) {
if (subject.isPermitted(permArr[index])) {
isPermitted = true;
}
}
}
return isPermitted;
}
}
/**
* @Description 加载自定义过滤器
*/
private Map<String, Filter> filters(){
Map<String,Filter> map = new HashMap<>();
//map的键,表示在配置权限控制的时候中括号前的属性值
//例如:/order-add = permissions-or["order:add","order:query"]
map.put("permissions-or", new PermissionOrFilter());
return map;
}
//shiro过滤器管理
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置该配置类中的安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager());
//设置自定义的过滤器
shiroFilterFactoryBean.setFilters(filters());
Map<String,String> map = new HashMap();
....
权限配置省略
.....
//过滤器链
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/login");
return shiroFilterFactoryBean;
}
这样的配置,可以在权限过滤器配置的时候,使用逗号 , 表示并列拥有才可以访问,使用 | 表示多个权限拥有其中一个权限就可以访问。
//表示同时拥有这两个权限才可以访问
/order-add = perms["order:add","order:query"]
//表示拥有这两个权限其中一个就可以访问
/order-add = perms["order:add"|"order:query"]