這個稍微有一點復雜,我是通過AOP來實現的,前半部分跟上一章類似,主要在配置上有點不同
讀取方法與權限對應列表DAO
~~~
public List<Map<String,String>> getMethodResourceMapping(){
String sql = "SELECT S3.RESOURCE_PATH,S2.AUTHORITY_MARK FROM SYS_AUTHORITIES_RESOURCES S1 "+
"JOIN SYS_AUTHORITIES S2 ON S1.AUTHORITY_ID = S2.AUTHORITY_ID "+
"JOIN SYS_RESOURCES S3 ON S1.RESOURCE_ID = S3.RESOURCE_ID AND S3.RESOURCE_TYPE='METHOD' ORDER BY S3.PRIORITY DESC";
List<Map<String,String>> list = new ArrayList<Map<String,String>>();
Query query = this.entityManager.createNativeQuery(sql);
List<Object[]> result = query.getResultList();
Iterator<Object[]> it = result.iterator();
while(it.hasNext()){
Object[] o = it.next();
Map<String,String> map = new HashMap<String,String>();
map.put("resourcePath", (String)o[0]);
map.put("authorityMark", (String)o[1]);
list.add(map);
}
return list;
}
~~~
這里只針對方法名攔截,對于同一個類中的同名方法統統作為一個方法攔截。如果需要更細粒度的攔截,即攔截到參數,請在完成這個內容之后自行進行深入的研究。
MethodKey用來做主鍵用的
~~~
public class MethodKey {
private String className;
private String methodName;
public MethodKey(){};
public MethodKey(String fullName){
this.className = StringUtils.stripFilenameExtension(fullName);
this.methodName = StringUtils.getFilenameExtension(fullName);
};
public MethodKey(Method method) {
super();
this.className = method.getDeclaringClass().getName();
this.methodName = method.getName();
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getFullMethodName(){
return this.className + "." + this.methodName;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof MethodKey))return false;
MethodKey target = (MethodKey)obj;
if(this.className.equals(target.getClassName()) &&
this.methodName.equals(target.getMethodName()))return true;
return false;
}
}
~~~
getDeclaringClass獲取到的是接口,這里我們只攔截接口。
MethodSecurityMetadataSource
~~~
public class MethodSecurityMetadataSource extends
AbstractMethodSecurityMetadataSource implements InitializingBean{
//protected Log logger = LogFactory.getLog(getClass());
private final static List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();
private final static String RES_KEY = "resourcePath";
private final static String AUTH_KEY = "authorityMark";
private Map<MethodKey, Collection<ConfigAttribute>> requestMap;
@Autowired
private SysResourceRepository sysResourceRepository;
/**
* 根據方法獲取到訪問方法所需要的權限
* @param method 訪問的方法
* @param targetClass 方法所屬的類
*/
@Override
public Collection<ConfigAttribute> getAttributes(Method method,
Class<?> targetClass) {
MethodKey key = new MethodKey(method);
Collection<ConfigAttribute> attrs = NULL_CONFIG_ATTRIBUTE;
for (Map.Entry<MethodKey, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
if (entry.getKey().equals(key)) {
attrs = entry.getValue();
break;
}
}
logger.info("METHOD資源:"+key.getFullMethodName()+ " -> " +attrs);
return attrs;
}
/**
* 獲取到所有方法對應的權限集合
*/
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
for (Map.Entry<MethodKey, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
allAttributes.addAll(entry.getValue());
}
return allAttributes;
}
/**
* 初始化方法權限對應集合,綁定方法權限集合
*/
@Override
public void afterPropertiesSet() throws Exception {
this.requestMap = this.bindRequestMap();
}
/**
* 從數據庫中獲取方法及權限對應信息
* @return
*/
private Map<String,String> loadMehod(){
Map<String,String> resMap = new LinkedHashMap<String, String>();
List<Map<String,String>> list = this.sysResourceRepository.getMethodResourceMapping();
for(Map<String,String> map : list){
String resourcePath = map.get(RES_KEY);
String authorityMark = map.get(AUTH_KEY);
if(resMap.containsKey(resourcePath)){
String mark = resMap.get(resourcePath);
resMap.put(resourcePath, mark+","+authorityMark);
}else{
resMap.put(resourcePath, authorityMark);
}
}
return resMap;
}
/**
* 封裝從數據庫中獲取的方法權限集合
* @return
*/
public Map<MethodKey, Collection<ConfigAttribute>> bindRequestMap(){
Map<MethodKey, Collection<ConfigAttribute>> resMap =
new LinkedHashMap<MethodKey, Collection<ConfigAttribute>>();
Map<String,String> map = this.loadMehod();
for(Map.Entry<String, String> entry : map.entrySet()){
MethodKey key = new MethodKey(entry.getKey());
Collection<ConfigAttribute> atts =
SecurityConfig.createListFromCommaDelimitedString(entry.getValue());
resMap.put(key, atts);
}
return resMap;
}
}
~~~
與資源的SecurityMetadataSource類似,只不過攔截方法的SecurityMetadataSource需要繼承自AbstractMethodSecurityMetadataSource或實現MethodSecurityMetadataSource
具體配置
~~~
<bean id="methodSecurityInterceptor"
class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="authenticationManager" ref="authenticationManager" />
<property name="securityMetadataSource" ref="methodSecurityMetadataSource" />
</bean>
<bean id="methodSecurityMetadataSource"
class="com.zrhis.system.security.MethodSecurityMetadataSource" />
<aop:config>
<aop:pointcut id="sevicePointcut"
expression="execution(* com.zrhis.**.service.*.*(..))"/>
<aop:advisor advice-ref="methodSecurityInterceptor" pointcut-ref="sevicePointcut" order="1"/>
</aop:config>
~~~
首先創建pointcut,pointcut是項目中的Service層。然后在advisor中配置攔截器及切面的對應關系
方法的攔截是通過AOP來實現的,方法的攔截到此結束
有資源的攔截和方法的攔截基本上就能保證項目的權限能夠靈活分配了。
- 前言
- (大綱)----學習過程分享
- (1)----SpringSecurity3.2環境搭建
- (2)----SpringSecurity簡單測試
- (3)---- 自定義登錄頁面
- (4)---- 數據庫表結構的創建
- (5)---- 國際化配置及UserCache
- (6)---- 使用數據庫管理用戶及權限
- (7)---- 解決UsernameNotFoundException無法被捕獲的問題
- (8)---- 自定義決策管理器及修改權限前綴
- (9)---- 自定義AccessDeniedHandler
- (10)---- 自定義登錄成功后的處理程序及修改默認驗證地址
- (11)---- 使用數據庫來管理資源
- (12)---- 使用數據庫來管理方法
- (13)---- 驗證碼功能的實現
- (14)---- Logout和SessionManager