Shiro与spring已经有完整的整合方法,所以,我们先在web.xml中添对过滤器,将需要验证的请求,拦截到shiro中。
<!—shiro过滤器-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在spring配置中添加下面的支持bean.
<!-- 权限or判定器-->
<beanid="roleOrFilter"class="com.my.commons.RolesOrFilter">
</bean>
<!-- 认证数据库存储-->
<beanid="myRealm"class="com.my.service.impl.DbAuthRealm">
</bean>
<!-- 权限管理器-->
<beanid="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<propertyname="realms">
<list>
<refbean="myRealm"/>
</list>
</property>
</bean>
<beanid="shiroFilter"class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"depends-on="roleOrFilter">
<propertyname="securityManager"ref="securityManager"/>
<propertyname="loginUrl"value="/login.jsp"/>
<propertyname="successUrl"value="/main"/>
<propertyname="unauthorizedUrl"value="/commons/unauth.jsp"/>
<!-- 读取自定义权限内容-->
<propertyname="filterChainDefinitions"value="#{authService.loadFilterChainDefinitions()}"/>
<propertyname="filters">
<map>
<entrykey="roleOrFilter"value-ref="roleOrFilter">
</entry>
</map>
</property>
</bean>
<beanid="lifecycleBeanPostProcessor"class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
其中filterChainDefinitions部分,简单的项目可以直接写死,我们这里采用的是启动时读取生成,如果写死,将会出现类似下面的代码:
<property name="filterChainDefinitions">
<value>
/js/** = anon
/css/** = anon
/login.jsp = anon
/login = anon
/docs/doc1.jsp= authc,roleOrFilter[admin,dev]
/admin/manager.jsp = authc, roleOrFilter[admin]
/admin/user/* = authc, rest[/admin/user/*]
/userinfo/myinfo.jsp = authc,roleOrFilter[test,dev,admin]
/** = authc
</value>
</property>
这段静态权限代码中用到了过滤器,我得给大家说一下:
anon部分是不需要进行验证的,即这部分是要放行的(注意**与*是有区别的,**表示该目录下的所有,当然也包括子目录下的东西,而*仅指当前目录下的东西)。
Authc,这个是需要登录的人才可以访问的。
roleOrFile,这个是我定义的,只要这个过滤器中的一个角色满足,即可访问,而shiro提供的role过滤器,是当所有角色都满足时才可访问。
Rest是restful过滤器,会将get,post,put,delete转换为对资源的read.create,modify,delete操作。
当然,shiro还提供了其它的过滤器,大家可以自己去看看,比如:permission过滤器,shiro对权限的描述采用wildcard字串,功能强大且可读性强。
另外一点要接出,shiro在里使用最先匹配规则,一旦匹配成功,将不再进行后续的过滤规则检查,因此,在书写时一定要注意顺序,比如,你把/** = anon写到第一行,那么后面的一切都将不会再检测。
好了,下面来讲如何动态生成这个规则,大家看我的配置中有value="#{authService.loadFilterChainDefinitions()}",这个是spring el表达式语言,表示调用容器中的另一个bean的方法,把这个方法的返回结果,赋值给filterChainDefinitions属性。
在些再补充一些权限字串的知识:
shiro所支持的广义权限字串表达式,共有三种:
1、简单方式
比如:subject.isPermitted("editNews"),表示判断某操作者是否有【编辑新闻】的权限。
2、细粒度方式
比如:subject.isPermitted("News:create"),表示判断某操作者是否有【新建新闻】的权限。
3、实例级访问方式
比如:subject.isPermitted("News:edit:10"),表示判断某操作者是否有【编辑id号是10新闻】的权限。
上面3种方式中,可以用*表示所有,例如:"News:*"为对所有新闻的操作,"*:create"对所有事务都可以新增。还可以用 逗号 表示或都,"News:edit:10,11"表示可对10,11号新闻进行编辑。
如果要写页面权限,可参照如下配置:
/index.jsp = anon
/admin/** = authc, roles[admin]
/docs/** = authc, perms[document:read]
/** = authc
我定义了一个权限相关的接口,如下:
/**
* 权限管理相关方法
* @authorljh
*
*/
public interfaceIAuthService{
/**
* 加载过滤配置信息
* @return
*/
publicString loadFilterChainDefinitions();
/**
* 重新构建权限过滤器
* 一般在修改了用户角色、用户等信息时,需要再次调用该方法
*/
public voidreCreateFilterChains();
}
其中一个方法用于加载生成权限规则字串,另一个,用于用户在系统中更改了角色-菜单,角色-功能关系时,动态重新生效的方法,实现类如下:
@Service(value="authService")
public classAuthServiceImplimplementsIAuthService {
private static finalLogger log= Logger.getLogger(AuthServiceImpl.class);
//注意/r/n前不能有空格
private static finalString CRLF= "\r\n";
private static finalString LAST_AUTH_STR= "/** =authc\r\n";
@Resource
privateShiroFilterFactoryBean shiroFilterFactoryBean;
@Resource
privateIBaseDao dao;
@Override
publicString loadFilterChainDefinitions() {
StringBuffer sb = newStringBuffer("");
sb.append(getFixedAuthRule())
.append(getDynaAuthRule())
.append(getRestfulOperationAuthRule())
.append(LAST_AUTH_STR);
returnsb.toString();
}
//生成restful风格功能权限规则
privateString getRestfulOperationAuthRule() {
List<Operation> operations = dao.queryEntitys("from Operation o", newObject[]{});
Set<String> restfulUrls = newHashSet<String>();
for(Operation op : operations) {
restfulUrls.add(op.getUrl());
}
StringBuffer sb = newStringBuffer("");
for(Iterator<String> urls = restfulUrls.iterator(); urls.hasNext(); ) {
String url = urls.next();
if(! url.startsWith("/")) {
url = "/"+ url ;
}
sb.append(url).append("=").append("authc, rest[").append(url).append("]").append(CRLF);
}
returnsb.toString();
}
//根据角色,得到动态权限规则
privateString getDynaAuthRule() {
StringBuffer sb = newStringBuffer("");
Map<String, Set<String>> rules = newHashMap<String,Set<String>>();
List<Role> roles = dao.queryEntitys("from Role r left join fetch r.menus", newObject[]{});
for(Role role: roles) {
for(Iterator<Menu> menus =role.getMenus().iterator(); menus.hasNext();) {
String url = menus.next().getUrl();
if(!url.startsWith("/")) {
url = "/"+ url;
}
if(!rules.containsKey(url)) {
rules.put(url, newHashSet<String>());
}
rules.get(url).add((role.getRoleCode()));
}
}
for(Map.Entry<String, Set<String>> entry :rules.entrySet()) {
sb.append(entry.getKey()).append(" = ").append("authc,roleOrFilter").append(entry.getValue()).append(CRLF);
}
returnsb.toString();
}
//得到固定权限验证规则串
privateString getFixedAuthRule() {
StringBuffer sb = newStringBuffer("");
ClassPathResource cp = newClassPathResource("fixed_auth_res.properties");
Properties properties = newOrderedProperties();
try{
properties.load(cp.getInputStream());
} catch(IOException e) {
log.error("loadfixed_auth_res.properties error!", e);
throw newRuntimeException("load fixed_auth_res.properties error!");
}
for(Iteratorits = properties.keySet().iterator();its.hasNext();) {
String key = (String)its.next();
sb.append(key).append(" = ").append(properties.getProperty(key).trim()).append(CRLF);
}
returnsb.toString();
}
@Override
//此方法加同步锁
public synchronized voidreCreateFilterChains() {
AbstractShiroFilter shiroFilter = null;
try{
shiroFilter = (AbstractShiroFilter)shiroFilterFactoryBean.getObject();
} catch(Exception e) {
log.error("getShiroFilter from shiroFilterFactoryBean error!", e);
throw newRuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
}
PathMatchingFilterChainResolver filterChainResolver =(PathMatchingFilterChainResolver)shiroFilter.getFilterChainResolver();
DefaultFilterChainManager manager =(DefaultFilterChainManager)filterChainResolver.getFilterChainManager();
//清空老的权限控制
manager.getFilterChains().clear();
shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
shiroFilterFactoryBean.setFilterChainDefinitions(loadFilterChainDefinitions());
//重新构建生成
Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
for(Map.Entry<String, String> entry :chains.entrySet()) {
String url = entry.getKey();
String chainDefinition =entry.getValue().trim().replace(" ", "");
manager.createChain(url,chainDefinition);
}
}
}
联系客服