最近項目中須要作一個權限管理模塊,按照以前同事的作法是在controller層的每一個接口調用以前上作邏輯判斷,這樣作也沒有不妥,可是代碼重複率過高,並且是體力勞動,so,便有了如題所說的使用spring aop作一個切點來實現通用功能的權限管理,這樣也就下降了項目後期開發的可擴展性。git
在最小的代碼修改程度上,aop無疑是最理想的選擇。項目中有各類權限的複合,相對來講邏輯複雜度比較高,因此一步步來。由於權限涉及到的是後端接口的調用因此樓主選擇在controller層代碼作切面,而切點就是controller中的各個方法塊,對於通用訪問權限,咱們使用execution表達式進行排除。github
對於實現排除通用的controller,樓主採用的是execution表達式邏輯運算。由於只讀管理員擁有全局讀權限,而對於增刪改權限,樓主採用的是使用切點切入是增刪改的方法,so,這個時候規範的方法命名就很重要了。對於各類與只讀管理員進行復合的各類管理員,咱們在代碼中作一下特殊判斷便可。下面是spring aop的配置文件配置方法。web
<!--非法權限拋出異常的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>
複製代碼
上面說了那麼多,廢話很少說了,下面是對只讀權限與各類複合權限進行控制的切面代碼實現。spring
/**
* 對只讀管理員以及其複合管理員進行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全切,特殊狀況在代碼邏輯裏面作實現便可。配置文件代碼以下:express
<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);
}
}
複製代碼
博客首發連接後端