個人覺得,上篇中講到的注解的方式來控制方法的訪問權限並沒有那么靈活,且需要在代碼中硬編碼,復用性不高,移植會很麻煩,故研究了下從數據庫中動態加載權限
即shiro.xml中的ShiroFilterFactoryBean,之前是直接加載apache源碼的bean:<bean id="shiroFilter" class="cn.cjq.util.shiro.ShiroPermissionFactory">
現在做出以下自定義shiro的filter 該bean繼承ShiroPermissionFactory,重寫父類的setFilterChainDefinitions(java.lang.String definitions)方法
首先來看ShiroPermissionFactory源碼,配置文件中的property 都是在為ShiroPermissionFactory屬性賦值
源碼部分:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package org.apache.shiro.spring.web;
public class ShiroFilterFactoryBean implements org.springframework.beans.factory.FactoryBean, org.springframework.beans.factory.config.BeanPostProcessor {
private static final transient org.slf4j.Logger log;
private org.apache.shiro.mgt.SecurityManager securityManager;
private java.util.Map<java.lang.String,javax.servlet.Filter> filters;
private java.util.Map<java.lang.String,java.lang.String> filterChainDefinitionMap;
private java.lang.String loginUrl;
private java.lang.String successUrl;
private java.lang.String unauthorizedUrl;
private org.apache.shiro.web.servlet.AbstractShiroFilter instance;
public ShiroFilterFactoryBean() { /* compiled code */ }
public org.apache.shiro.mgt.SecurityManager getSecurityManager() { /* compiled code */ }
public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) { /* compiled code */ }
public java.lang.String getLoginUrl() { /* compiled code */ }
public void setLoginUrl(java.lang.String loginUrl) { /* compiled code */ }
public java.lang.String getSuccessUrl() { /* compiled code */ }
public void setSuccessUrl(java.lang.String successUrl) { /* compiled code */ }
public java.lang.String getUnauthorizedUrl() { /* compiled code */ }
public void setUnauthorizedUrl(java.lang.String unauthorizedUrl) { /* compiled code */ }
public java.util.Map<java.lang.String,javax.servlet.Filter> getFilters() { /* compiled code */ }
public void setFilters(java.util.Map<java.lang.String,javax.servlet.Filter> filters) { /* compiled code */ }
public java.util.Map<java.lang.String,java.lang.String> getFilterChainDefinitionMap() { /* compiled code */ }
public void setFilterChainDefinitionMap(java.util.Map<java.lang.String,java.lang.String> filterChainDefinitionMap) { /* compiled code */ }
public void setFilterChainDefinitions(java.lang.String definitions) { /* compiled code */ }
public java.lang.Object getObject() throws java.lang.Exception { /* compiled code */ }
public java.lang.Class getObjectType() { /* compiled code */ }
public boolean isSingleton() { /* compiled code */ }
protected org.apache.shiro.web.filter.mgt.FilterChainManager createFilterChainManager() { /* compiled code */ }
protected org.apache.shiro.web.servlet.AbstractShiroFilter createInstance() throws java.lang.Exception { /* compiled code */ }
private void applyLoginUrlIfNecessary(javax.servlet.Filter filter) { /* compiled code */ }
private void applySuccessUrlIfNecessary(javax.servlet.Filter filter) { /* compiled code */ }
private void applyUnauthorizedUrlIfNecessary(javax.servlet.Filter filter) { /* compiled code */ }
private void applyGlobalPropertiesIfNecessary(javax.servlet.Filter filter) { /* compiled code */ }
public java.lang.Object postProcessBeforeInitialization(java.lang.Object bean, java.lang.String beanName) throws org.springframework.beans.BeansException { /* compiled code */ }
public java.lang.Object postProcessAfterInitialization(java.lang.Object bean, java.lang.String beanName) throws org.springframework.beans.BeansException { /* compiled code */ }
private static final class SpringShiroFilter extends org.apache.shiro.web.servlet.AbstractShiroFilter {
protected SpringShiroFilter(org.apache.shiro.web.mgt.WebSecurityManager webSecurityManager, org.apache.shiro.web.filter.mgt.FilterChainResolver resolver) { /* compiled code */ }
}
}------------------------------------------------------------------------------------------------------------------------------------------------
shiro.xml配置部分內容
<!-- 自定義shiro的filter -->
<bean id="shiroFilter" class="cn.cjq.util.shiro.ShiroPermissionFactory">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login/toLogin.do"/>
<property name="unauthorizedUrl" value="/login/403.do"/>
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</map>
</property>
<property name="filterChainDefinitions"> //該部分的值被重寫,部分數據從數據庫加載
<value>
/login/**=anon
/api/** = authc
/main.do = authc
</value>
</property>
</bean>
------------------------------------------------------------------------------------------------------------------------------------
自定義的shiro攔截器ShiroPermissionFactory
package cn.cjq.util.shiro;
import cn.cjq.entity.Menu;
import cn.cjq.service.user.UserService;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import javax.annotation.Resource;
import java.util.List;
public class ShiroPermissionFactory extends ShiroFilterFactoryBean {
/**配置中的過濾鏈*/
public static String definitions;
/**權限service*/
@Resource
private UserService userServiceImpl;
/**
* 從數據庫動態讀取權限
*/
@Override
public void setFilterChainDefinitions(String definitions) {
ShiroPermissionFactory.definitions = definitions;
//數據庫動態權限
List<Menu> Menulist= null;
try {
Menulist = userServiceImpl.ListMenu();
} catch (Exception e) {
e.printStackTrace();
}
//拼接所有請求(即url)所需要的權限(認證,權限ID),拼接時必須加入\n用來換行,否則無效。
for(Menu menu : Menulist){
//字符串拼接權限
definitions = definitions+menu.getUrl() + " = "+"authc,perms["+menu.getMenuid()+"]\n";
}
//從配置文件加載權限配置
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);
}
/**
* 從數據庫動態加載權限
*/
// public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
// ShiroFilterFactoryBean shiroFilterFactoryBean=null;
// try {
// shiroFilterFactoryBean = new MyShiroFilterFactoryBean();
// //下列屬性在配置文件中已配置
//// shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
//// shiroFilterFactoryBean.setLoginUrl("/login");
//// shiroFilterFactoryBean.setSuccessUrl("/index.html");
//// shiroFilterFactoryBean.setUnauthorizedUrl("/403");
// loadShiroFilterChain(shiroFilterFactoryBean);
// } catch (Exception e) {
// e.printStackTrace();
// }
// return shiroFilterFactoryBean;
// }
//
// private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean) throws Exception {
// List<Menu> Menulist = userServiceImpl.ListMenu();
// Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// for (Menu menu : Menulist) {
// filterChainDefinitionMap.put(menu.getUrl(),"authc,perms["+menu.getMenuid()+"]");
// }
// //配置文件中已配置
//// filterChainDefinitionMap.put("/login", "anon");
// shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// }
}
-------------------------------------------------------------------------------------------------------------------------------
上述列出的是所有url所需的相對應權限ID,下面是自定義realm中的授權信息,列出當前用戶所有的權限ID,通過比較是否存在權限問題。(也可通過角色去比較,暫不做說明)
/**
* 授權信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// UserService userService = (UserService) SpringContextUtil.getBean("userServiceImpl");
try {
authorizationInfo.setRoles(userMapper.findUserRoles(username));
authorizationInfo.setStringPermissions(userMapper.findUserPermissions(username));
} catch (Exception e) {
e.printStackTrace();
}
return authorizationInfo;
}
------------------------------------------------------------------------------------------------------------------------------------------
根據當前用戶獲取所有權限ID:也可以不根據權限ID,使用別的也可,但必須統一,既然是使用了權限ID,所有查詢結果返回權限ID即可
<!-- 根據用戶名查詢獲取權限列表ID -->
<select id="findUserPermissions" resultType="java.lang.String" parameterType="java.lang.String" >
SELECT
m.menuId
FROM mif_user u
LEFT JOIN mif_role_user_ref ur ON u.uerId=ur.userId
LEFT JOIN mif_role r ON r.roleId=ur.roleId
LEFT JOIN mif_role_authority_ref ar ON ar.roleId=r.roleId
LEFT JOIN mif_authority a ON a.authorityId=ar.authorityId
LEFT JOIN mif_authotity_menu_ref mr ON mr.authorityId=ar.authorityId
LEFT JOIN mif_menu m ON m.menuId=mr.menuId
WHERE u.isdelete = 0 AND r.isdelete=0 AND a.isdelete=0 AND m.isdelete=0 AND u.userName = #{0}
</select>
--------------------------------------------------------------------------------------------------------------------------------------------
坑1:上述標紅的\n未使用,無效果
坑2:在初始化靜態攔截的時候存在初始值/person/**= authc,即/person下的所有方法只要通過認證即可請求。但是shiro權限中數據庫的動態數據也包含了/person下的方法,是無效的,原因
是自定義的shiro的filter先加載配置文件中的數據,再加載數據庫動態數據,存在/person/**= authc在前面時會讓部分數據庫/person下的url失效,注釋掉/person/**= authc即可
<property name="filterChainDefinitions"> //該部分的值被重寫,部分數據從數據庫加載
<value>
/login/**=anon
/api/** = authc
/main.do = authc
/person/**= authc
</value>
</property>
坑3:該bean只會在系統啟動時加載一次,之后不會調用,所有存在任何權限上的變動時,需手動調用setFilterChainDefinitions()方法
PS:要記住在增刪改權限數據庫時記得調用setFilterChainDefinitions()方法重新加載
相關url:http://blog.csdn.net/u010351766/article/details/70432227