1. 程式人生 > >基於shiro廣東11選5源碼下載的自定義註解擴展

基於shiro廣東11選5源碼下載的自定義註解擴展

用戶 search ets cati 實例 n) cal swa ali

根據廣東11選5源碼下載 dsluntan.com 我的上一篇文章,權限設計的雜談中,涉及到了有關於前後端分離中,頁面和api接口斷開表與表層面的關聯,另辟蹊徑從其他角度找到方式進行關聯。這裏我們主要采取了shiro的自定義註解的方案。本篇文章主要解決以下的問題。

如何通過邏輯進行頁面與api接口的關聯。
shiro的自身註解的用法。
如何編寫自定義註解。
如何通過邏輯進行頁面與api接口的關聯
在表與表的結構關系中,頁面和接口表最終都是與權限表進行的關聯(詳情請查看我的上一篇文章《權限設計的雜談》)。 權限實體圖 我們現在希望用另一種方案去替代他,實現一個低成本同時兼顧一定程度的權限控制。這裏我們引入兩個概念。業務模塊,操作類型。

業務模塊
概念:將系統中的業務模塊抽象成一種數據,我們可以用字符串的形式去表示,例如:角色管理對應是role-manage、用戶管理對應是user-manage等等。我們將系統中所存在的業務模塊通過“最小特權原則”進行劃分,最終形成一批可分配的數據。
使用原則:api接口和頁面以及功能從本質上來說,都和業務模塊有邏輯關系,於是,我們可以對api接口與頁面(以及功能點)進行邏輯匹配,來判斷頁面與接口的關系。
操作類型
概念:將系統中的所有的操作類型抽象成一種數據,我們也可以用字符串的形式去表示,例如:新增對應的是add、分配對應的是allot等等。我們將系統中所有的操作類型根據業務模塊通過“數據許可證”進行劃分,最終形成一批可分配的數據。

使用原則:頁面是展示,功能點是動作,而接口是最終動作的資源提供,通過“業務模塊”確定了調取的資源,通過“操作類型”確定了資源的使用方式。通過兩者可以大致無誤的判斷頁面的功能點觸發的接口是否在鑒權之內。
現在提出了這兩個概念,他們最終的實際的使用方式是什麽,我們先從以下幾個角度去思考一下。

數據庫中的頁面表或的api接口表中的數據就是真實有效嗎?
頁面或接口的實際使用,是以功能存在為前提,還是以數據庫表中的數據存在為前提。
權限結構中,“控制對象”的存儲只有數據庫這一種途徑嗎?
我們從結論出發來看這幾個問題,首先“控制對象”的存儲除了在數據庫中也可以代碼中,也可以在配置文件中,並不一定非得在數據庫;那麽接著回答第二個問題,當數據庫存在的接口信息,而服務端並沒有開發這個接口的時候,數據庫的信本身就有問題,亦或者,數據庫裏新增的接口必定是服務端上已經部署的接口才能生效;接著就是第一個問題,那麽數據庫中關於“控制對象”的表中的數據並不一定是真實有效的。所以我們可以得出以下的解決方案

我們可以在接口上用註解的形式補充“業務模塊”和“操作類型”的數據信息,這兩類信息都可以存於常量類中,
在數據庫添加創建頁面表結構和頁面功能表結構的時候,添加“業務模塊”和“操作類型”字段。
可以將“業務模塊”和“操作類型”的信息存於數據庫的字典表中。
模塊的新增或操作的新增,必定帶來了接口的新增,那麽就會帶來一次系統部署活動,這個運維成本是無法減少的,並不能通過表結構來減少。
業務模塊與頁面和接口的關系

但是這種方案僅適用於非強控制接口型的項目,在強控制型的接口項目仍然要將頁面與接口進行綁定,雖然這會帶來巨大的運維成本。另外也可以通過接口路由規則進行劃分,例如:/api/page/xxxx/(僅對頁面使用),/api/mobile/xxxxx(僅對移動端使用)將僅供頁面使用的接口進行分類,這類接口僅做認證不做授權,也可以達到目的。

shiro的自身註解的用法
通過一個理論上的思路認可之後,剩下的則是付諸技術上的實踐,我們這邊采用的是Apache Shiro的安全框架,在Spring Boot的環境下應用。簡要說明以下幾個shiro的註解。

註解名 作用
@RequiresAuthentication 作用於的類、方法、實例上。調用時,當前的subject是必須經過了認證的。
@RequiresGuest 作用於的類、方法、實例上。調用時,subject可以是guest狀態。
@RequiresPermissions 作用於的類、方法、實例上。調用時,需要判斷suject中是否包含當前接口中的Permission(權限信息)。
@RequiresRoles 作用於的類、方法、實例上。調用時,需要判斷subject中是否包含當前接口中的Role(角色信息)。
@RequiresUser 作用於的類、方法、實例上。調用時,需要判斷subject中是否當前應用中的用戶。
/**

  • 1.當前接口需要經過"認證"過程
  • @return
    */
    @RequestMapping(value = "/info",method = RequestMethod.GET)@RequiresAuthentication
    br/>@RequiresAuthentication
    return "恭喜你,拿到了參數信息";
    }

    /**

  • 2.1.當前接口需要經過權限校驗(需包含 角色的查詢 或 菜單的查詢)
  • @return
    */
    @RequestMapping(value = "/info",method = RequestMethod.GET)@RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    br/>@RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    return "恭喜你,拿到了參數信息";
    }

    /**

  • 2.2.當前接口需要經過權限校驗(需包含 角色的查詢 與 菜單的查詢)
  • @return
    */
    @RequestMapping(value = "/info",method = RequestMethod.GET)@RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    br/>@RequiresPermissions(value={"role:search","menu":"search"},logical=Logical.OR)
    return "恭喜你,拿到了參數信息";
    }

    /**

  • 3.1.當前接口需要經過角色校驗(需包含admin的角色)
  • @return
    */
    @RequestMapping(value = "/info",method = RequestMethod.GET)@RequiresRoles(value={"admin"})
    br/>@RequiresRoles(value={"admin"})
    return "恭喜你,拿到了參數信息";
    }

    /**

  • 3.2.當前接口需要經過角色與權限的校驗(需包含admin的角色,以及角色的查詢 或 菜單的查詢)
  • @return
    */
    @RequestMapping(value = "/info",method = RequestMethod.GET)@RequiresRoles(value={"admin"})
    br/>@RequiresRoles(value={"admin"})
    public String test(){
    return "恭喜你,拿到了參數信息";
    }

在我們的實際使用過程中,實際上只需要使用@RequiresPermissions和@RequiresAuthentication就可以了這一個註解就可以了,在上一小節的結尾,我們采取了業務模塊與操作的結合方案來解耦頁面和api接口的關系,和apache Shiro的這種方式正好一致。但是@RequiresRoles這個我們盡可能不采用,因為角色的組合形式太多,角色名沒有辦法在接口中具象唯一化(很難指定接口歸某個角色調用,但是一定能知道接口歸屬於某些業務模塊的某些操作。)

現在我們來回顧一下整個運轉的流程。

shiro權限的驗證流程

如何編寫自定義註解
但是僅僅是擁有shiro中的這5個註解肯定是不夠使用的。在實際的使用過程中,根據需求,我們會在權限認證中加入我們自己特有的業務邏輯的,我們為了便捷則可以采用自定義註解的方式進行使用。這種方法不僅僅適用於Apache Shiro,很多其他的框架如:Hibernate Validator、SpringMVC、甚至我們可以寫一套校驗體系,在aop中去驗證權限,這都是沒問題的。所以自定義註解的作用很廣。但是在這裏,我僅僅基於shiro的來實現適用於它的自定義註解。

定義註解類
/**

  • 用於認證的接口的註解,組合形式默認是“或”的關系*/
    @Target({ElementType.METHOD})
    br/>*/
    @Target({ElementType.METHOD})
    public @interface Auth {
    /**
    • 業務模塊
    • @return
      */
      String[] module();
      /**
    • 操作類型
      */
      String[] action();

}

定義註解的處理類
/**

  • Auth註解的操作類
    */
    public class AuthHandler extends AuthorizingAnnotationHandler {

    public AuthHandler() {
    //寫入註解
    super(Auth.class);
    }

    @Override
    public void assertAuthorized(Annotation a) throws AuthorizationException {
    if (a instanceof Auth) {
    Auth annotation = (Auth) a;
    String[] module = annotation.module();
    String[] action = annotation.action();
    //1.獲取當前主題
    Subject subject = this.getSubject();
    //2.驗證是否包含當前接口的權限有一個通過則通過
    boolean hasAtLeastOnePermission = false;
    for(String m:module){
    for(String ac:action){
    //使用hutool的字符串工具類
    String permission = StrFormatter.format("{}:{}",m,ac);
    if(subject.isPermitted(permission)){
    hasAtLeastOnePermission=true;
    break;
    }
    }
    }
    if(!hasAtLeastOnePermission){
    throw new AuthorizationException("沒有訪問此接口的權限");
    }

    }

    }
    }
    定義shiro攔截處理類
    /**

  • 攔截器
    */
    public class AuthMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {

    public AuthMethodInterceptor() {
    super(new AuthHandler());
    }

    public AuthMethodInterceptor(AnnotationResolver resolver) {
    super(new AuthHandler(), resolver);
    }

    @Override
    public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
    // 驗證權限
    try {
    ((AuthHandler) this.getHandler()).assertAuthorized(getAnnotation(mi));
    } catch (AuthorizationException ae) {
    if (ae.getCause() == null) {
    ae.initCause(new AuthorizationException("當前的方法沒有通過鑒權: " + mi.getMethod()));
    }
    throw ae;
    }
    }
    }

定義shiro的aop切面類
/**

  • shiro的aop切面
    */
    public class AuthAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor {
    public AuthAopInterceptor() {
    super();
    // 添加自定義的註解攔截器
    this.methodInterceptors.add(new AuthMethodInterceptor(new SpringAnnotationResolver()));
    }
    }

定義shiro的自定義註解啟動類
/**

  • 啟動自定義註解
    */
    public class ShiroAdvisor extends AuthorizationAttributeSourceAdvisor {

    public ShiroAdvisor() {
    // 這裏可以添加多個
    setAdvice(new AuthAopInterceptor());
    }

    @SuppressWarnings({"unchecked"})@Override
    br/>@Override
    Method m = method;
    if (targetClass != null) {
    try {
    m = targetClass.getMethod(m.getName(), m.getParameterTypes());
    return this.isFrameAnnotation(m);
    } catch (NoSuchMethodException ignored) {

        }
    }
    return super.matches(method, targetClass);

    }

    private boolean isFrameAnnotation(Method method) {
    return null != AnnotationUtils.findAnnotation(method, Auth.class);
    }
    }

總體的思路順序:定義註解類(定義業務可使用的變量)->定義註解處理類(通過註解中的變量做業務邏輯處理)->定義註解的攔截器->定義aop的切面類->最後定義shiro的自定義註解啟用類。其他的自定義的註解的編寫思路和這個也是類似的。

基於shiro廣東11選5源碼下載的自定義註解擴展