基於Controller方法註解的極簡權限控制解決方案

背景

因項目需要做比較詳細的權限控制,大致瞭解了sprin sercurity、shiro兩個名氣比較大的權限管理框架。

得到的結論是:功能足夠強大,用戶、權限、認證等等,你想到的它全都有,沒想到的也有。但是我並不需要這麼多功能(項目的用戶認證已由其他方案完成,現只需要權限控制)。而且連號稱輕量級的shiro我也覺得太龐大了些。因此,決定根據項目需求自己實現一套極簡權限控制方案。

關於權限的數據庫表

表結構不是本文重點,簡單提一下。與權限相關的表:

  • 用戶表
  • 角色表
  • 權限表(非必要,除非要做細粒度、可配置)
  • 用戶角色表
  • 角色權限表

基於接口的權限控制

如何實現控制

  1. 根據當前session提供的用戶信息,查詢到該用戶的角色
  2. 通過反射獲取到當前訪問的接口的註解,用註解裏面的值去與數據庫中該用戶角色的權限集合(可以緩存)比對,有權限則通過,否則拒絕。

爲什麼要基於controller

客戶端與服務端的交互是以http請求爲單位的,所以基於請求進行權限控制是比較合理的選擇。

實現

寫一個註解類

/**
 * 權限控制註解。
 * @author yangsheng
 * @Date  2019/8/1 14:08
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
    int[] allow() default RoleConstant.NORMAL_ROLE;
}

寫攔截器

這裏的攔截器也可以替換爲切面


/**
 * @author
 * @program onlineScoring
 * @description 權限控制攔截器
 * @create 2019-08-01 14:45
 **/
@Slf4j
public class PermissionInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Permission permission = null;
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            permission = handlerMethod.getMethod().getAnnotation(Permission.class);
        }
        if (permission == null) {
            return true;
        }
        int allow = permission.allow();
        // 這裏可以寫控制邏輯。建議建立專門的權限控制業務邏輯的類來處理。
        switch (allow) {
            case RoleConstant.SUPER_ADMIN_ROLE:
                System.out.println("超級管理員");
            default:
                System.out.println("emmmm");
        }
        responseResult(response, new JsonResponse().error("您沒有操作權限"));
        return super.preHandle(request, response, handler);
    }

    private void responseResult(HttpServletResponse response, JsonResponse result) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setStatus(200);
        try {
            response.getWriter().write(JSON.toJSONString(result));
        } catch (IOException ex) {
            log.error(ex.getMessage());
        }
    }
}

註冊攔截器

<mvc:interceptors>
    <bean class="com.qckj.permission.intercepter.PermissionInterceptor"/>
</mvc:interceptors>
<mvc:annotation-driven />

注意,這裏需要寫<mvc:annotation-driven />,否則handler轉換成HandlerMethod的時候會拋異常,因爲此時的handler實際上是Controller的代理。

如果不寫<mvc:annotation-driven />,也可以這樣寫:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

使用

在Controller方法上面寫上@Permission註解,就完成工作了!

@Permission(allow = RoleConstant.ADMIN_ROLE)
@RequestMapping("/examination")
public void index() {
    
}

該方案的優缺點

優點

  • 權限表結構簡單,數據庫只需要維護用戶-角色關係,可以不關心角色-權限的關係(這個關係已經交給註解維護)
  • 實現非常簡單,無需寫過多的代碼或是接入龐大的相關框架
  • 高效。角色的權限集合是直接通過聲明的註解獲取的,無需查詢數據庫或緩存。

缺點

  • 因爲不關心角色-權限的關係,所以角色和權限的關係基本上是死的(通過註解綁定死了),不能通過頁面操作隨時變更角色的權限。

此方案更適合於中小型、用戶角色與功能權限的關係比較穩定、對性能要求高的系統。

JAVA 註解的基本原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章