問題源於專案開發
最近專案中需要做一個許可權管理模組,按照之前同事的做法是在controller層的每個介面呼叫之前上做邏輯判斷,這樣做也沒有不妥,但是程式碼重複率太高,而且是體力勞動,so,便有了如題所說的使用spring aop做一個切點來實現通用功能的許可權管理,這樣也就降低了專案後期開發的可擴充套件性。
許可權管理的程式碼實現與配置檔案
在最小的程式碼修改程度上,aop無疑是最理想的選擇。專案中有各種許可權的複合,相對來說邏輯複雜度比較高,所以一步步來。因為許可權涉及到的是後端介面的呼叫所以樓主選擇在controller層程式碼做切面,而切點就是controller中的各個方法塊,對於通用訪問許可權,我們使用execution表示式進行排除。
只讀管理員許可權的實現及切點選擇
對於實現排除通用的controller,樓主採用的是execution表示式邏輯運算。因為只讀管理員擁有全域性讀許可權,而對於增刪改許可權,樓主採用的是使用切點切入是增刪改的方法,so,這個時候規範的方法命名就很重要了。對於各種與只讀管理員進行復合的各種管理員,我們在程式碼中做一下特殊判斷即可。下面是spring aop的配置檔案配置方法。
<!--非法許可權丟擲異常的spring mvc的處理-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.thundersoft.metadata.exception.AccessDeniedException">forward:/auth/readOnly</prop>
</props>
</property>
</bean>
<bean id="usersPermissionsAdvice"
class="com.thundersoft.metadata.aop.UsersPermissionsAdvice"/>
<aop:config>
<!--定義切面 -->
<aop:aspect id="authAspect" ref="usersPermissionsAdvice">
<!-- 定義切入點 (配置在com.thundersoft.metadata.web.controller下所有的類在呼叫之前都會被攔截) -->
<aop:pointcut
expression="(execution(* com.thundersoft.metadata.web.controller.*.add*(..)) or
execution(* com.thundersoft.metadata.web.controller.*.edit*(..)) or
execution(* com.thundersoft.metadata.web.controller.*.del*(..)) or
execution(* com.thundersoft.metadata.web.controller.*.update*(..)) or
execution(* com.thundersoft.metadata.web.controller.*.insert*(..)) or
execution(* com.thundersoft.metadata.web.controller.*.modif*(..))) or
execution(* com.thundersoft.metadata.web.controller.*.down*(..))) and (
!execution(* com.thundersoft.metadata.web.controller.FindPasswordController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.SelfServiceController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.HomeController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.UserStatusController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.DashboardController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.MainController.*(..))))"
id="authPointCut"/>
<!--方法被呼叫之前執行的 -->
<aop:before method="readOnly"
pointcut-ref="authPointCut"/>
</aop:aspect>
</aop:config>
複製程式碼
只讀管理員許可權管理程式碼實現
上面說了那麼多,廢話不多說了,下面是對只讀許可權與各種複合許可權進行控制的切面程式碼實現。
/**
* 對只讀管理員以及其複合管理員進行aop攔截判斷.
* @param joinPoint 切入點.
* @throws IOException
*/
public void readOnly(JoinPoint joinPoint) throws IOException {
/**
* 獲取被攔截的方法.
*/
String methodName = joinPoint.getSignature().getName();
/**
* 獲取被攔截的物件.
*/
Object object = joinPoint.getTarget();
logger.info("許可權管理aop,方法名稱{}" + methodName);
HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
String roleFlag = GetLoginUserInfor.getLoginUserRole(request);
/**
* 超級管理員
*/
if (PermissionsLabeled.super_Admin.equals(roleFlag)) {
return;
}
/**
* 只讀管理員做資料更改許可權的判斷
*/
if (PermissionsLabeled.reader_Admin.equals(roleFlag)) {
if (methodName.contains("redirectToLogout")) {
return;
}
logger.error("只讀管理員無操作許可權!");
throw new AccessDeniedException("您無權操作!");
}
/**
* 部門管理員,且為只讀管理員,
*/
if (PermissionsLabeled.dept_reader_Admin.equals(roleFlag)) {
if (object instanceof DepartmentController) {
return;
}
if (object instanceof UserController) {
if (methodName.contains("addAdmin")) {
throw new AccessDeniedException("您無權操作!");
}
if (methodName.contains("deleteAdmin")) {
throw new AccessDeniedException("您無權操作!");
}
if (methodName.contains("updateAdmin")) {
throw new AccessDeniedException("您無權操作!");
}
return;
}
if (object instanceof GroupController) {
return;
}
logger.error("部門管理員,且為只讀管理員無操作許可權!");
throw new AccessDeniedException("您無權操作!");
}
/**
* 應用管理員,且為只讀管理員
*/
if (PermissionsLabeled.app_reader_Admin.equals(roleFlag)) {
if (object instanceof AppController) {
return;
}
if (object instanceof AppPolicyController) {
return;
}
logger.error("應用管理員,且為只讀管理員無操作許可權!");
throw new AccessDeniedException("您無權操作!");
}
/**
* 部門管理員,且為應用管理員,且為只讀管理員
*/
if (PermissionsLabeled.dept_app_reader_Admin.equals(roleFlag)) {
if (object instanceof DepartmentController) {
return;
}
if (object instanceof UserController) {
return;
}
if (object instanceof GroupController) {
return;
}
if (object instanceof AppController) {
return;
}
if (object instanceof AppPolicyController) {
return;
}
logger.error("部門管理員,且為應用管理員,且為只讀管理員無操作許可權");
throw new AccessDeniedException("您無權操作!");
}
}
複製程式碼
具有專門功能的管理員許可權控制的切點選擇
因為具有專門的管理員許可權比較特殊,樓主採用的方式除了通用訪問許可權之外的controller全切,特殊情況在程式碼邏輯裡面做實現即可。配置檔案程式碼如下:
<aop:config>
<!--定義切面 -->
<aop:aspect id="authAspect" ref="usersPermissionsAdvice">
<!-- 定義切入點 (配置在com.thundersoft.metadata.web.controller下所有的類在呼叫之前都會被攔截) -->
<aop:pointcut
expression="(execution(* com.thundersoft.metadata.web.controller.*.*(..)) and (
!execution(* com.thundersoft.metadata.web.controller.FindPasswordController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.SelfServiceController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.HomeController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.UserStatusController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.DashboardController.*(..)) and
!execution(* com.thundersoft.metadata.web.controller.MainController.*(..))))"
id="appAuthPointCut"/>
<!--方法被呼叫之前執行的 -->
<aop:before method="appDeptAuth"
pointcut-ref="appAuthPointCut"/>
</aop:aspect>
</aop:config>
複製程式碼
許可權管理的切面程式碼實現
/**
* 對應用管理員以及部門管理員進行aop攔截判斷.
* @param joinPoint 切入點.
* @throws IOException
*/
public void appDeptAuth(JoinPoint joinPoint) throws IOException {
/**
* 獲取被攔截的方法.
*/
String methodName = joinPoint.getSignature().getName();
/**
* 獲取被攔截的物件.
*/
Object object = joinPoint.getTarget();
logger.info("許可權管理aop,方法名稱{}",methodName);
HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
String roleFlag = GetLoginUserInfor.getLoginUserRole(request);
/**
* 超級管理員
*/
if (PermissionsLabeled.super_Admin.equals(roleFlag)) {
return;
}
/**
* 應用管理員做資料更改許可權的判斷
*/
if (PermissionsLabeled.app_Admin.equals(roleFlag)) {
if (object instanceof AppController) {
return;
}
if (object instanceof AppPolicyController) {
return;
}
logger.error("應用管理員無操作許可權");
throw new AccessDeniedException("您無權操作!");
} else if (PermissionsLabeled.dept_Admin.equals(roleFlag)) {
if (object instanceof DepartmentController) {
return;
}
if (object instanceof UserController) {
return;
}
if (object instanceof GroupController) {
return;
}
if ("getAllDepartments".equals(methodName)) {
return;
}
logger.error("應用管理員無操作許可權");
throw new AccessDeniedException("您無權操作!");
} else {
return;
}
}
複製程式碼
自定義許可權非法異常程式碼
/**
* @author wuhf0703@thundersoft.com
* @date 2017/12/12
*/
public class AccessDeniedException extends RuntimeException {
/**
* Constructs a <code>AccessDeniedException</code> with the specified message.
*
* @param msg the detail message.
*/
public AccessDeniedException(String msg) {
super(msg);
}
/**
* Constructs a {@code AccessDeniedException} with the specified message and root cause.
*
* @param msg the detail message.
* @param t root cause
*/
public AccessDeniedException(String msg, Throwable t) {
super(msg, t);
}
}
複製程式碼