天天看點

通用資料級别權限的架構設計與實作(4)-單條記錄的權限控制

檢視上篇文章 通用資料級别權限的架構設計與實作(3)-資料清單的權限過濾

,我們開始在原來的基礎上實作單條權記錄的權限控制。

相信前面的清單權限控制,很多系統都可以做到,但如何在上面清單的權限過濾中實作通用性

原理:我們在權限過濾中,通過AOP接截相關記錄,攔截的時候,我們先判斷目前人員是否有角色權限,沒有的話,我們生成查詢權限的SQL,進行權限查找.

1.先定義生成權限的相關注解,相關注解用于權限攔截,及擷取攔截的相關參數.

Target({ ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthCheck {
    /**權限校驗規則
     * @return
     */
    Class classModel() ;

    /**
     *  判斷是否IN查詢
     * @return
     */
    boolean isIn() default true;

}
           

注解AuthCheck用于定義單條記錄權限攔截的規則

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public  @interface AuthParam {
}

           

AuthParam隻是用于标記哪個參數用于權限校驗.

2.生成權限校驗SQL的類AuthFiledFilter修改

/**
     * 生成對一條記錄的權限校限SQL
     * @param id
     * @param valueList
     * @return
     */

    public String getAuthSqlIn(Object id, List valueList){
        StringBuffer sb = new StringBuffer();
        sb.append("select count(1) from ");
        sb.append (this.getSalveTableName() );
        sb.append(" where    ").append(this.getSalveTableField()).append("=").append(id);
        sb.append(" and ");
        sb.append(this.buildLogicIN(this.getSearchUserField(), valueList));
        return sb.toString();
    }

    /**
     * 生成對一條記錄的權限校驗SQL,用EQ
     * @param id
     * @param userId
     * @return
     */

    public String getAuthSqlEq(Object id,Object userId){
        StringBuffer sb = new StringBuffer();
        sb.append("select count(1) from ");
        sb.append (this.getSalveTableName() );
        sb.append(" where    ").append(this.getSalveTableField()).append("=").append(id);
        sb.append(" and ");
        sb.append(  this.getSearchUserField()).append("=").append(userId);
        return sb.toString();
    }
           

3.權限攔截判斷的類AuthValidatorUtil的規則增加

//生成校驗的
    public static String  getAuthModelSql(String classModel, Object id,boolean isIn) {
        AuthValidatorModel authValidatorModel = AuthConfig.get(classModel);
        if (authValidatorModel == null) {
            return "";
        }
        boolean isAuth = authValidatorModel.getRoles().stream().anyMatch(role -> UserUtil.containRole(role));
        if (isAuth) {
            return "";
        }
        String checkAuthSql="";
        if (isIn) {
            checkAuthSql = authValidatorModel.getAuthFiledFilter().getAuthSqlIn(id, UserUtil.getOrgIds());
        } else {
            checkAuthSql = authValidatorModel.getAuthFiledFilter().getAuthSqlEq(id, UserUtil.getUserId());
        }
        return checkAuthSql;
    }
           

4.關鍵對有權限辨別注解的權限攔截

@Aspect
@Order(1)
@Component
public class AuchCheckAspect {
    protected final Log logger = LogFactory.getLog(AuchCheckAspect.class);

    @Autowired
    AuthMapper authMapper;

    /**
     * 權限校驗
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Before(value = "@annotation( com.starmark.auth.anno.AuthCheck)")
    public void timeAroundAdvice(JoinPoint joinPoint) throws Throwable {
        //相關參數
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        AuthCheck AuthCheck = method.getAnnotation(AuthCheck.class);
        String classModel = AuthCheck.classModel().getName();
        Assert.notNull(classModel, " 權限校驗模型不允許為空");
        Object id = null;
        Object[] args = joinPoint.getArgs();
        //從方法中擷取相關主鍵校驗參數
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            AuthParam authParam = parameters[i].getAnnotation(AuthParam.class);
            if (authParam != null) {
                id = args[i];
                break;
            }
        }
        AuthValidatorModel authValidatorModel = AuthConfig.get(classModel);
        String checkAuthSql = AuthValidatorUtil.getAuthModelSql(classModel,id,AuthCheck.isIn());
        if (StringUtils.isNotEmpty(checkAuthSql)) {
            boolean isAuth = authMapper.isAuth(checkAuthSql);
            if (!isAuth) {
                throw new AuthCheckException();
            }
        }
    }


}

           

這裡提示一下,AuthMapper是我們執行一條生成的SQL方法,是注入一條SQL,因為SQL是我們背景代碼生成的,無須考慮SQL注入的問題

<!-- 生成 SQL判斷有沒有權限-->
    <select id="isAuth"  resultType="boolean" >
         ${authSql}
    </select>
    
           

5.相關權限的功能使用

@GetMapping(value = "/{id}")
    @AuthCheck(classModel = SysAuthRole.class)
    public Object get(@PathVariable("id") @AuthParam Long id) {
        return sysAuthRoleService.get(id);
    }
           

6.測試:

打開有權限的記錄,正常結果如下:

通用資料級别權限的架構設計與實作(4)-單條記錄的權限控制

有權限記錄.png

打開沒有權限的記錄,抛出一個無權限的異常。

通用資料級别權限的架構設計與實作(4)-單條記錄的權限控制

無權限記錄.png

至此,單條記錄的權限攔截已完成!

個人代碼已經完成,如需要請打賞後通知我。謝謝.

如果你覺得該文章對你有幫助,麻煩點贊。

歡迎繼續檢視下篇文章-

通用資料級别權限的架構設計與實作(5)-總結與延伸思考