spring aop實現許可權管理

haifeiWu發表於2017-12-06

問題源於專案開發

最近專案中需要做一個許可權管理模組,按照之前同事的做法是在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);
    }
}

複製程式碼

部落格首發連結

相關文章