新项目集成了shiro,但是发现原生的只支持在配置文件配置权限,就像这样
<property name="filterChainDefinitions">
<!-- , roles[admin], perms[document:read] -->
<value>
<!-- 对静态资源设置匿名访问 -->
/js/** = anon
/css/** = anon
/img/** = anon
/fonts/** = anon
/scripts/** = anon
/user/login.action = authc
/user/logout = logout
<!-- 进入后台需要权限:admin:* -->
/backstage/** = perms[admin:*]
/user/** = user
</value>
</property>
可是,正常项目的权限不可能是静态的,一般都是从数据库获取的动态权限数据,如果使用配置文件,每次权限改动都要修改配置文件,然后重启服务器,这显然不是我们想要的,所以只能对shiro框架进行扩展,下面是我自己的一些思路和代码
首先看原来的配置
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="${shiro.login.page.url}" />
<property name="unauthorizedUrl" value="${shiro.unauthorized.url}" />
<property name="filters">
<map>
<entry key="myPermissionFilter" value-ref="myPermissionFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/resources/** = anon
</value>
</property>
</bean>
很明显要想扩展代码,首先要看
org.apache.shiro.spring.web.ShiroFilterFactoryBean中的给filterChainDefinitions赋值的setFilterChainDefinitions(String definitions)方法源码
public void setFilterChainDefinitions(String definitions) {
Ini ini = new Ini();
ini.load(definitions);
//did they explicitly state a 'urls' section? Not necessary, but just in case:
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
//no urls section. Since this _is_ a urls chain definition property, just assume the
//default section contains only the definitions:
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
setFilterChainDefinitionMap(section);
}
看到源码发现很简单definitions参数就是配置的字符串,方法里的源码就是把配置字符串转换成shiro需要的数据,具体的过程我们不需要关心,我们只需要在definitions上“动手脚”就可以了,具体怎么做?最简单的是从数据库读取出权限列表然后字符串拼接即可
有了思路,那么就写一个继承于ShiroFilterFactoryBean类的子类ShiroPermissionFactory,重写setFilterChainDefinitions(String definitions)方法即可
public class ShiroPermissionFactory extends ShiroFilterFactoryBean {
/**配置中的过滤链*/
public static String definitions;
/**权限service*/
@Autowired
private ShiroPermissionsService permissionsService;
/**
* 从数据库动态读取权限
*/
@Override
public void setFilterChainDefinitions(String definitions) {
ShiroPermissionFactory.definitions = definitions;
//数据库动态权限
List<PermissionsPo> permissions = permissionsService.findAll();
for(PermissionsPo po : permissions){
//字符串拼接权限
definitions = definitions+po.getUrl() + " = "+"perms["+po.getId()+"]";
}
//从配置文件加载权限配置
Ini ini = new Ini();
ini.load(definitions);
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
//加入权限集合
setFilterChainDefinitionMap(section);
}
}
然后在配置文件配置bean的类为自定义类即可
<!-- 使用自定义的Shiro Web过滤器 -->
<bean id="shiroFilter" class="com.longmap.passport.shiro.ShiroPermissionFactory">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="${shiro.login.page.url}" />
<property name="unauthorizedUrl" value="${shiro.unauthorized.url}" />
<property name="filters">
<map>
<entry key="myPermissionFilter" value-ref="myPermissionFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/resources/** = anon
</value>
</property>
</bean>
就这么简单,动态权限控制就完成了
PS:要记住在增删改权限数据库时记得调用setFilterChainDefinitions()方法重新加载