首先说明一点shiro框架我只懂得很少,大部分代码是从网上down的,考虑到shiro url授权配置不方便的地方,结合自己做的东西谈谈感受。大家轻拍。如果shrio有更新的解决方案或者大家有更好的方法可以多多指教。
废话不多说。
前提:我定义了两个权限 user:add 、user:add 和一个角色role-admin
需求: 访问/user/user 这个链接资源需要有(user:add and user:edit11 ) or role-admin , 其中and 和 or是关系逻辑,
意思就是用户同时有两个权限user:add和user:edit11 或者单独有角色role-admin,可以访问/user/user。
贴上我的shiro配置,
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- 启用shrio授权注解拦截方式 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 装配 securityManager -->
<property name="securityManager" ref="securityManager" />
<!-- 配置登陆页面 -->
<property name="loginUrl"
value="http://localhost:8080/wemall/wemall-business-web/user/nolog" />
<!-- 登陆成功后的一面 -->
<property name="successUrl" value="/jsp/success.jsp" />
<property name="unauthorizedUrl"
value="http://localhost:8080/wemall/wemall-business-web/user/unauthorized" />
<!-- 具体配置需要拦截哪些 URL, 以及访问对应的 URL 时使用 Shiro 的什么 Filter 进行拦截. -->
<!-- <property name="filterChainDefinitions"> <value> /index.jsp=anon /jsp/success.jsp=anon
/jsp/fail.jsp=anon /user/user = perms[user:view] /jsp/admin.jsp = roles[admin]
/logout = logout </value> </property> -->
<property name="filters">
<map>
<entry key="rolesor">
<bean class="com.wemall.shrio.filterchaindef.CustomRolesAuthorizationFilter" />
</entry>
<entry key="all">
<bean class="com.wemall.shrio.filterchaindef.AllAuthorizationFilter" />
</entry>
</map>
</property>
</bean>
<!-- 权限资源配置 -->
<bean id="filterChainDefinitionsService"
class="com.wemall.shrio.filterchaindef.SimpleFilterChainDefinitionsService">
<property name="definitions">
<value>
<!-- /index.jsp=anon /jsp/success.jsp=anon /jsp/fail.jsp=anon /jsp/admin.jsp
= roles[admin] -->
<!-- 此处改为从数据库读取 -->
/logout = logout
</value>
</property>
</bean>
<!-- 配置缓存管理器 -->
<!-- <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
指定 ehcache 的配置文件 <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"
/> </bean> -->
<!-- 配置进行授权和认证的 Realm -->
<!-- <bean id="myRealm" class="com.wemall.user.realm.UserRealm"> <property
name="userService" ref="userService" /> </bean> -->
<!-- <bean id="userService" class="com.gray.user.service.impl.UserServiceImpl"
/> -->
<!-- 配置 Shiro 的 SecurityManager Bean. -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm" />
<property name="sessionMode" value="native">
</property>
</bean>
<!-- 配置 Bean 后置处理器: 会自动的调用和 Spring 整合后各个组件的生命周期方法. -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans>
首先看,SimpleFilterChainDefinitionsService,资源配置,从数据库读取,采用的是下边的类
com.wemall.shrio.filterchaindef.SimpleFilterChainDefinitionsService
<!-- 权限资源配置 -->
<bean id="filterChainDefinitionsService"
class="com.wemall.shrio.filterchaindef.SimpleFilterChainDefinitionsService">
<property name="definitions">
<value>
<!-- /index.jsp=anon /jsp/success.jsp=anon /jsp/fail.jsp=anon /jsp/admin.jsp
= roles[admin] -->
<!-- 此处改为从数据库读取 -->
/logout = logout
</value>
</property>
</bean>
表结构
具体类代码SimpleFilterChainDefinitionsService
package com.wemall.shrio.filterchaindef;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import com.wemall.shriourl.dao.ShrioUrlDao;
import com.wemall.shriourl.entity.ShrioUrl;
/**
*
* 加载第三方角色资源配置服务类
*
* @author shadow
*
*/
public class SimpleFilterChainDefinitionsService extends AbstractFilterChainDefinitionsService {
private final static Logger log = Logger.getLogger(SimpleFilterChainDefinitionsService.class);
@Autowired
private ShrioUrlDao shrioUrlDao;
@Override
public Map<String, String> initOtherPermission() {
// extend to load other permission
Map<String, String> shrioUrlMap = new HashMap<String, String>();
List<ShrioUrl> shrioUrlList = shrioUrlDao.selectAllShrioUrl();
if (shrioUrlList != null && shrioUrlList.size() > 0) {
for (ShrioUrl shrioUrl : shrioUrlList) {
if (shrioUrl.getUrl() == null || shrioUrl.getUrl().isEmpty()) {
log.debug("sys_shrio_url表中id为" + shrioUrl.getId() + "的url字段为空,不进行过滤");
continue;
}
if (shrioUrl.getFiltername() == null || shrioUrl.getFiltername().isEmpty()) {
log.debug("sys_shrio_url表中id为" + shrioUrl.getId() + "的filtername字段为空,不进行过滤");
continue;
}
if (shrioUrl.getDef() == null || shrioUrl.getDef().isEmpty()) {
log.debug("sys_shrio_url表中id为" + shrioUrl.getId() + "的def字段为空,不进行过滤");
continue;
}
shrioUrlMap.put(shrioUrl.getUrl(), shrioUrl.getFiltername() + "[" + shrioUrl.getDef() + "]");
}
}
return shrioUrlMap;
}
}
大体上就是返回个map,里边放着过滤的形式,比如我的返回就是 key =/user/user
,value = all [( perm-user:add and perm-user:edit11 ) or role-admin],这个东西就会自动加到filterChainDefinitionsService的definitions里, all就对应了配置文件中的
<entry key="all">
<bean class="com.wemall.shrio.filterchaindef.AllAuthorizationFilter" />
</entry>
这就意味着访问/user/user,就要走all这个过滤器,过滤器需要对( perm-user:add and perm-user:edit11 ) or role-admin这个公式进行判断,是否可以访问。这里的perm-和role-表示是权限还是角色,这样一个公式就既支持权限又支持角色了,下面代码也会对这个perm-和role-的形式进行解析。
filter代码:
package com.wemall.shrio.filterchaindef;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.log4j.Logger;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
// AuthorizationFilter抽象类事项了javax.servlet.Filter接口,它是个过滤器。
public class AllAuthorizationFilter extends AuthorizationFilter {
private final static Logger log = Logger.getLogger(AllAuthorizationFilter.class);
@Override
protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
Subject subject = getSubject(req, resp);
String[] allArray = (String[]) mappedValue;
if (allArray == null || allArray.length == 0) { // 没有角色限制,有权限访问
log.info("all过滤条件为空,直接不进行权限控制");
return true;
}
if (allArray.length != 1) {
log.info("all过滤条件要求数组只有一个表达式,目前不唯一,所以不过滤");
return true;
}
Map<String, Boolean> hasRoleOrPerm = new HashMap<String, Boolean>();
String expnew = "";
for (String exp : allArray) {
expnew = exp;
String[] arr = exp.split("\\s+");
for (String ss : arr) {
// 获取里边的role-和perm-相关的
if (ss.indexOf("role-") != -1) {
hasRoleOrPerm.put(ss, subject.hasRole(ss.replaceAll("role-", "")));
}
// 获取里边的role-和perm-相关的
if (ss.indexOf("perm-") != -1) {
hasRoleOrPerm.put(ss, subject.isPermitted(ss.replaceAll("perm-", "")));
}
}
Set<Entry<String, Boolean>> entryset = hasRoleOrPerm.entrySet();
for (Entry<String, Boolean> entry : entryset) {
expnew = expnew.replace(entry.getKey(), entry.getValue().toString());
}
expnew = expnew.replace("and", "&&");
expnew = expnew.replace("or", "||");
}
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval(expnew);
return (Boolean) result;
//System.out.println("结果类型:" + result.getClass().getName() + ",计算结果:" + result);
//return false;
}
}
能不能访问就是看返回的true或false。代码中先判断角色和权限判断是否有,将有改为true没有改为false,公式转化为 (true && true) || false 这种形式,这种判断虽然一眼能看出结果,但是却是字符串。所以我百度到一种方法
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval(expnew);
return (Boolean) result;
这个能把我这种字符串判断真假。
到此为止,复杂形式的权限角色资源权限判断完毕。
注意,我的公式是我自己编辑的格式,都有空格,便于截取字符串,前缀perm-用来区分在权限中找还是在role-在角色中找。添加形式和解析都是大家自由发挥的地方。