1. 程式人生 > >【原創】基於SpringMVC的註解式許可權控制--轉載請註明出處

【原創】基於SpringMVC的註解式許可權控制--轉載請註明出處

1. 開發目標

Shiro的許可權分配中,存在@RequiresPermissions註解進行許可權的控制,該註解規定了所進行了註解的方法,只能被具有某些許可權的人進行訪問,且許可權之間為&的關係。這個許可權控制是不符合我們所需要的許可權控制需求的。(我們所需要的是具有某些許可權之一的使用者可以訪問,同時具有這些許可權的子許可權也可以訪問與具有這些許可權本身和其父許可權才能進行訪問的這兩種情況區分開)。

我們在該部落格中所描述的許可權控制,是一種類似於Shiro的註解式許可權控制。做到使用@ExistPermissions@HasPermissions來進行許可權控制的目的。

2. 配置攔截器配置

配置一個類為SpringMVC的攔截器時,只需要在SpringMVC.xml中新增如下程式碼即可:

<mvc:interceptors>
        <mvc:interceptor>
        <!--預設攔截的連線-->
            <mvc:mapping path="/**"/>
            <!--不攔截的連線-->
            <mvc:exclude-mapping path="/user/login.do"/>
            <bean class="com.xxx.security.filter.SecurityFilter"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
</mvc:interceptors>

其中介面 preHandle 中的程式碼會在訪問到controller 層介面前執行,而 afterCompletion postHandle 介面則是在controller 層執行完畢後,才會執行程式碼。因此我們在進行許可權處理時,要將許可權控制程式碼寫在 preHandle 方法中。 在上述XML 配置中,將 SecurityFilter  類作為攔截器進行了處理。這個攔截器需要先實現介面 HandlerInterceptor  。在該介面中有三個方法,分別為 afterCompletion postHandle
preHandle。

 

3. 註解配置

在這裡我們還需要定義兩個註解@ExistPermissions@HasPermissions

/**
 * 
 * @author Administrator
 * 該註解用於進行對許可權的控制,
 * 當用戶的許可權中存在該許可權或改許可權的子許可權時,則予以放行
 * 相關程式碼需檢視SecurityFilter
 */
@Target(value={ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExistPermissions {
	/**
	 * 可以訪問所進行註解的許可權的陣列物件,
	 * 當具有該陣列物件中任意許可權的子許可權時,均可以進行訪問
	 */
	String[] permissions();
}

/**
 * 
 * @author Administrator
 * 該註解用於進行對許可權的控制,
 * 必須在使用者持有某些許可權中的一個時,才則予以放行
 * 相關程式碼需檢視SecurityFilter
 */
@Target(value=ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HasPermissions {
	/**
	 * 可以訪問所進行註解的許可權的陣列物件,
	 * 當具有該陣列物件中的任意一個許可權時,可以進行訪問
	 */
	String[] permissions();
}
我們在這兩個註解中分別定義了一個方法permissions ,這個方法返回類行為String[] ,用於進行多許可權的處理。這樣我們就可以一次性對多個許可權進行判斷。 @Target註解定義了這個方法可以用在那些地方, @Retention註解定義了這個方法在什麼時候進行使用。

4. preHandle方法

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
}
在方法preHandle  中有三個引數,request response handler 。前兩個引數大家都是十分熟悉的,不做描述,重點在於第三個引數 handler  

handler物件是一個object物件,因此,我們需要先通過 handler.getclass()方法來檢視這個方法的具體型別。

經過

system.out.println(handler.getclass());

可以發現這個物件本身的型別是

org.springframework.web.method.HandlerMethod

那麼此時,我們對這個物件進行強制轉換,將其強轉為它本身的型別。

5. HandlerMethod 

HandlerMethod,見名知意,這是一個與method相關類,這個類是SpringMVC中定義的一個用於承載一個方法的相關資訊的類。我們可以通過這個類的例項來獲取該所進行@requestMapping 註解的方法的相關資訊。

我們所需要使用的部分為註解部分的內容。從HandlerMethod中獲取我們所需要的@HasPermissions 註解,然後從註解中獲得所我們要進行驗證的許可權的字串。

HasPermissions hasPermissions = handlerMethod.getMethodAnnotation(HasPermissions.class);
for(String permission : hasPermissions.permissions()){

}

6. preHandle方法邏輯處理

當然,用類似的方法,我們也可以獲取這個方法上的其他註解,如requestMapping註解。

@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//獲取使用者許可權資訊列表
		UserInfo user = (UserInfo) request.getSession().getAttribute("userInfo");
		if(user == null){
			return false ;
		}
		Set<String> permissions = user.getPermissions();
		//獲取相應許可權註解資訊
		HandlerMethod handlerMethod = (HandlerMethod) handler;
		//驗證使用者是否持有其中一個許可權
		HasPermissions hasPermissions = handlerMethod.getMethodAnnotation(HasPermissions.class);
		boolean hasPer = false;
		if(hasPermissions != null){
			for(String permission : hasPermissions.permissions()){
				if(this.hasPermission(permissions, permission)){
					hasPer = true;
					break;
				}
			}
		}else{
			hasPer = true;
		}
		
		//驗證使用者是否持有其中一個許可權,或者其中一個許可權的子許可權
		ExistPermissions existPermissions = handlerMethod.getMethodAnnotation(ExistPermissions.class);
		boolean existPer = false;
		if(existPermissions != null){
			for(String permission : existPermissions.permissions()){
				if(this.hasPermission(permissions, permission)){
					existPer = true;
					break;
				}
			}
		}else{
			existPer = true;
		}
		if(hasPer && existPer){
			return true;
		}else{
			System.out.println("使用者 "+user.getUserName()+" 越權訪問:"+request.getRequestURL().toString());
			return false;
		}
	}
/**
	 * 驗證使用者是否持有一個許可權,或者一個許可權的子許可權
	 * @param permissions
	 * @param permission
	 * @return
	 */
	public boolean hasPermission(Set<String> permissions, String permission){
		for(String userPermission : permissions){
			if(permission.substring(0, userPermission.length()).equals(userPermission)){
				return true;
			}
		}
		return false;
	}
/**
	 * 驗證使用者是否持有一個許可權
	 * @param permissions
	 * @param permission
	 * @return
	 */
	public boolean existPermission(Set<String> permissions, String permission){
		for(String userPermission : permissions){
			if(userPermission.substring(0, permission.length()).equals(permission)){
				return true;
			}else if(permission.substring(0, userPermission.length()).equals(userPermission)){
				return true;
			}
		}
		return false;
	}