通過注解進(jìn)行權(quán)限控制

1.自定義注解

元注解介紹

@Target

@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標(biāo)。

  1. CONSTRUCTOR:用于描述構(gòu)造器

  2. FIELD:用于描述域

  3. LOCAL_VARIABLE:用于描述局部變量

  4. METHOD:用于描述方法

  5. PACKAGE:用于描述包

  6. PARAMETER:用于描述參數(shù)

  7. TYPE:用于描述類、接口(包括注解類型) 或enum聲明

2.@Retention

表示需要在什么級別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)

3.@Documented
Documented注解表明這個(gè)注釋是由 javadoc記錄的,在默認(rèn)情況下也有類似的記錄工具。 如果一個(gè)類型聲明被注釋了文檔化,它的注釋成為公共API的一部分。

自定義注解示例
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthCheck
{
  public abstract String module();

  public abstract String oper();

  public abstract String userProfile();

  public abstract String dbYear();

  public abstract String entityId();

  public abstract String entity();

  public abstract boolean failWhenUserProfileIsNull();

  public abstract String failCode();
}

2.AOP

AOP概念

面向切面編程:擴(kuò)展功能不修改源代碼實(shí)現(xiàn),AOP采用橫向抽取機(jī)制,取代傳統(tǒng)的縱向繼承體系重復(fù)性代碼(性能監(jiān)視,事務(wù)管理,安全檢查,緩存)

AOP操作術(shù)語:

(JoinPoint)連接點(diǎn):類里面可以被增強(qiáng)的方法

(Pointcut)切入點(diǎn):實(shí)際增強(qiáng)的方法,也就是新增的方法

(Advice)通知/增強(qiáng):增強(qiáng)的邏輯,比如擴(kuò)展日志功能,這個(gè)日志功能增強(qiáng)

前置通知、后置通知、異常通知、最終通知、環(huán)繞通知

(Aspect)切面:把增強(qiáng)應(yīng)用到具體方法上,過程稱為切面

對@AuthCheck注解的處理實(shí)例
package com.supporter.prj.eip.auth_engine.service;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Service;
/**
 * 提供對@AuthCheck注解的處理.
 * @Aspect 實(shí)現(xiàn)spring aop 切面(Aspect):
 * 一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)可能會(huì)橫切多個(gè)對象。事務(wù)管理是J2EE應(yīng)用中一個(gè)關(guān)于橫切關(guān)注點(diǎn)的很好的例子。 在Spring
 * AOP中,切面可以使用通用類(基于模式的風(fēng)格) 或者在普通類中以 @Aspect 注解(@AspectJ風(fēng)格)來實(shí)現(xiàn)。
 * AOP代理(AOP Proxy): AOP框架創(chuàng)建的對象,用來實(shí)現(xiàn)切面契約(aspect contract)(包括通知方法執(zhí)行等功能)。
 * 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。 注意:Spring 2.0引入的基于模式(schema-based)風(fēng)格和@AspectJ注解風(fēng)格的切面聲明,對于使用這些風(fēng)格的用戶來說,代理的創(chuàng)建是透明的。
 */
@Service
@Aspect
public class AuthAnnotationService {
    private ILogService getLogService() {
        return LogUtil.getAuthDebugLogService();
    }
    /**
     * 構(gòu)造方法.
     */
    public AuthAnnotationService() {}
    
    /**
     * 定義一個(gè)PointCut.
     * @param point
     * @throws Throwable
     */
    @Pointcut("@annotation(com.supporter.prj.eip_service.authority.annotation.AuthCheck)")
    public void methodPointcut() {}

    // 方法執(zhí)行的前后調(diào)用
    @Around("methodPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        //getLogService().debug("類實(shí)例:" + point.getTarget().getClass() + ":" + point.getTarget().hashCode());
        boolean authCheckPassed = true;
        getLogService().debug("[權(quán)限引擎]注解方式開始執(zhí)行,進(jìn)入around(), 方法:" + point.getSignature().getName());
        String moduleSetting = this.getModule(point);
        IModule module = EIPService.getModuleService().getModule(moduleSetting);
        if (module != null) {
            //只有module設(shè)置有效才繼續(xù),否則會(huì)影響性能
            String operSetting = this.getOper(point);
            getLogService().debug("[權(quán)限引擎]operSetting:" + operSetting);
            IOper oper = EIPService.getModuleService().getOper(operSetting, module);
            if (oper != null) {
                //只有oper設(shè)置有效才繼續(xù),否則會(huì)影響性能
                UserProfile userProfile = this.getUserProfile(point);
                //getLogService().debug("[權(quán)限引擎]userProfile:" + userProfile.getAccountLogin());
                if (userProfile != null) {
                    //只有userProfile才繼續(xù),否則影響性能 
                    String dbYearSetting = this.getDbYearSetting(point);
                    getLogService().debug("[權(quán)限引擎]dbYearSetting:" + dbYearSetting);
                    if (dbYearSetting.length() > 0) {
                        //如果存在dbYear設(shè)置,那么說明是年庫相關(guān)
                        int dbYear = this.getDbYear(point);
                        getLogService().debug("[權(quán)限引擎]dbYear:" + dbYear);
                        if (dbYear <= 0) {
                            getLogService().debug("[權(quán)限引擎]failed to get dbYear, auth check point:" + this.getMethodDesc(point));
                            authCheckPassed = false;
                        } else {
                               EIPService.getAuthorityService().canAccess(dbYear, userProfile, oper,entity);
                                if (getLogService().isDebugEnabled()) {
                                    getLogService().debug("[權(quán)限引擎]權(quán)限檢查: " + userProfile.getAccountLogin() 
                                            + ":" + oper.getModuleId() + "." + oper.getName());
                                }
                                authCheckPassed = EIPService.getAuthorityService().canAccess(userProfile, oper);
//                          }
                        }
                    } else {
                        //否則與年庫無關(guān),在應(yīng)用庫中
//                      if (entity != null) {
//                          authCheckPassed = EIPService.getAuthorityService().canAccess(userProfile, oper, entity);
//                      } else {
                            //沒有傳遞過來entityId,那么忽略數(shù)據(jù)限定條件
                            authCheckPassed = EIPService.getAuthorityService().canAccess(userProfile, oper);
//                      }
                    }
                } else {
                    //注:在userProfile為null的情況下,是否允許執(zhí)行?根據(jù)failWhenUserProfileIsNull來判斷
                    getLogService().debug("[權(quán)限引擎]userProfile is null. auch check for method:" + this.getMethodDesc(point));
                    boolean failWhenUserProfileIsNull = getFailWhenUserProfileIsNull(point);
                    if (failWhenUserProfileIsNull) authCheckPassed = false;
                }
            } else {
                getLogService().debug("[權(quán)限引擎]invalid oper:" + operSetting + ", module:" + moduleSetting);
            }
        } else {
            getLogService().debug("[權(quán)限引擎]invalid module:" + moduleSetting);
        }
        
        if (!authCheckPassed) throw new BaseRuntimeException(getFailCode(point), "[權(quán)限引擎]當(dāng)前用戶無權(quán)執(zhí)行本操作:" + this.getMethodDesc(point));
        
        //執(zhí)行被注解的方法.
        Object object;
        try {
            object = point.proceed();
        } catch (Throwable t) {
            t.printStackTrace();
            throw t;
        }
        long e = System.currentTimeMillis();
        getLogService().debug("[權(quán)限引擎]注解方式執(zhí)行完畢:" + point.getSignature().getName()+",耗時(shí):"+(e-s));
 
        return object;
    }

    // 方法運(yùn)行出現(xiàn)異常時(shí)調(diào)用  
    // @AfterThrowing(pointcut = "execution(* com.supporter..*(..))", throwing = "ex")
    public void afterThrowing(Exception ex) {
        if (ex != null) ex.printStackTrace();
    }

    /**
     * key為"className:methodName",value是一個(gè)List,里面是同名的方法(可能參數(shù)不同).
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static Map < String, List < Method > > methodMap = new ConcurrentHashMap();
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static List < Method > getMethods(String className, String methodName) {
        String key = className + ":" + methodName;
        if (methodMap.containsKey(key)) return methodMap.get(key);
        
        //否則獲取一番
        try {
            Class < ? > targetClass = Class.forName(className);
            return getMethods(targetClass, methodName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return new ArrayList();
    }
    
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static List < Method > getMethods(Class < ? > clazz, String methodName) {
        if (clazz == null) return null;
        
        String key = clazz.getName() + ":" + methodName;
        if (methodMap.containsKey(key)) return methodMap.get(key);
        
        List < Method > methodList = new ArrayList();
        //否則獲取一番
        //Method[] methods = targetClass.getMethods(); //getMethods()只返回public的方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method m : methods) {
            if (m.getName().equals(methodName)) {
                methodList.add(m);
            }
        }
        
        if (methodList.size() == 0) {
            //在當(dāng)前類中找不到,試試看祖先有沒有
            if (clazz.getSuperclass() != null) {
                methodList = getMethods(clazz.getSuperclass(), methodName);
            }
        }
        
        methodMap.put(key, methodList); //放入緩存中
        
        return methodList;
    }
    
    private String getMethodDesc(ProceedingJoinPoint joinPoint) {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        
        String paramNameStr = "";
        String[] paramNames = methodSignature.getParameterNames();
        for (String paramName : paramNames) {
            if (paramNameStr.length() > 0) paramNameStr += ",";
            paramNameStr += paramName;
        }
        return targetName + "." + methodName + "(" + paramNameStr + ")";
    }
    
    /**
     * 獲取被注解的方法所屬的模塊.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getModule(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck != null) {
                return authCheck.module();
            } else {
                return null;
            }
        }
    }
    
    /**
     * 獲取被注解的方法所屬的操作.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getOper(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck != null) {
                return authCheck.oper();
            } else {
                return null;
            }
        }
    }
    
    /**
     * 獲取被注解的方法所屬的模塊.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private boolean getFailWhenUserProfileIsNull(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck != null) {
                return authCheck.failWhenUserProfileIsNull();
            } else {
                return true;
            }
        }
    }
    
    /**
     * 獲取被注解的方法在校驗(yàn)失敗后返回的代碼.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getFailCode(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck != null) {
                return authCheck.failCode();
            } else {
                return null;
            }
        }
    }
    
    /**
     * 獲取被注解的方法所屬的userProfile.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private UserProfile getUserProfile(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck != null) {
                String userProfileSetting = authCheck.userProfile();
                if (userProfileSetting == null) throw new RuntimeException("userProfileSetting is null");
                userProfileSetting = userProfileSetting.trim();
                //logService.debug("userProfileSetting:" + userProfileSetting);
                
                if (userProfileSetting.endsWith("()")) {
                    //應(yīng)該是一個(gè)方法名,那么通過反射的形式來獲取相應(yīng)的值
                    String methodName = userProfileSetting.substring(0, userProfileSetting.length() - 2);
                    
                    Object returnVal = this.getReturnVal(methodName, joinPoint);
                    if (returnVal == null) {
                        return null;
                    } else {
                        return (UserProfile) returnVal;
                    }
                } else {
                    //應(yīng)該是參數(shù)名,那么尋找對應(yīng)的參數(shù)值
                    //尋找對應(yīng)的參數(shù)值
                    Object paramVal = getParamVal(userProfileSetting, joinPoint);
                    if (paramVal == null) {
                        return null;
                    } else {
                        return (UserProfile) paramVal;
                    }
                }
            } else {
                return null;
            }
        }
    }
    
    /**
     * 獲取被注解的方法對應(yīng)的操作相關(guān)的業(yè)務(wù)實(shí)體ID.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private Object getEnityId(ProceedingJoinPoint joinPoint) throws Exception {
        String entityIdSetting = getEntityIdSetting(joinPoint);
        if (entityIdSetting.length() == 0) {
            return null;
        }
        
        if (entityIdSetting.endsWith("()")) {
            //應(yīng)該是一個(gè)方法名,那么通過反射的形式來獲取相應(yīng)的值
            String methodName = entityIdSetting.substring(0, entityIdSetting.length() - 2);
            return this.getReturnVal(methodName, joinPoint);
        } else {
            //應(yīng)該是參數(shù)名,那么尋找對應(yīng)的參數(shù)值
            //尋找對應(yīng)的參數(shù)值
            return getParamVal(entityIdSetting, joinPoint);
        }
    }
    
    /**
     * 獲取被注解的方法所屬的entity.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private Object getEntity(ProceedingJoinPoint joinPoint) throws Exception {
        String entitySetting = getEntitySetting(joinPoint);
        if (entitySetting.length() == 0) {
            return null;
        }
        if (entitySetting.endsWith("()")) {
            //應(yīng)該是一個(gè)方法名,那么通過反射的形式來獲取相應(yīng)的值
            String methodName = entitySetting.substring(0, entitySetting.length() - 2);
            return this.getReturnVal(methodName, joinPoint);
        } else {
            //應(yīng)該是參數(shù)名,那么尋找對應(yīng)的參數(shù)值
            //尋找對應(yīng)的參數(shù)值
            return getParamVal(entitySetting, joinPoint);
        }
    }
    
    /**
     * 獲取被注解的方法所屬的dbYear設(shè)置值字符串,它的含義可能是一個(gè)參數(shù)名,也可能是一個(gè)方法名.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getDbYearSetting(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck == null) {
                EIPService.getLogService().error("無法找到相應(yīng)的注解:" + getMethodDesc(joinPoint));
                return "";
            }

            String dbYear = authCheck.dbYear();
            if (dbYear == null) throw new RuntimeException("dbYear is null");
            
            return dbYear.trim(); 
        }
    }
    
    /**
     * 獲取被注解的方法所屬的entityId設(shè)置值字符串,它的含義可能是一個(gè)參數(shù)名,也可能是一個(gè)方法名.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getEntityIdSetting(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck == null) {
                EIPService.getLogService().error("無法找到相應(yīng)的注解:" + getMethodDesc(joinPoint));
                return "";
            }

            String entityId = authCheck.entityId();
            if (entityId == null) return "";
            
            return entityId.trim(); 
        }
    }
    
    /**
     * 獲取被注解的方法所屬的entity設(shè)置值字符串,它的含義可能是一個(gè)參數(shù)名,也可能是一個(gè)方法名.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String getEntitySetting(ProceedingJoinPoint joinPoint) throws Exception {
        Method m = getMethod(joinPoint);
        if (m == null) {
            String methodDesc = this.getMethodDesc(joinPoint);
            throw new RuntimeException("找不到被注解的方法:" + methodDesc);
        } else {
            AuthCheck authCheck = m.getAnnotation(AuthCheck.class);
            if (authCheck == null) {
                EIPService.getLogService().error("無法找到相應(yīng)的注解:" + getMethodDesc(joinPoint));
                return "";
            }

            String entity = authCheck.entity();
            if (entity == null) return "";
            
            return entity.trim(); 
        }
    }
    
    /**
     * 獲取被注解的方法的指定名稱的參數(shù)值.
     * @param paramName
     * @param joinPoint
     * @return
     */
    private Object getParamVal(String paramName, ProceedingJoinPoint joinPoint) {
        String pName = CommonUtil.trim(paramName);
        if (pName.length() == 0) {
            getLogService().error("paramName is empty.");
            return null;
        }
        
        //尋找對應(yīng)的參數(shù)值
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] paramNames = methodSignature.getParameterNames();
        Object[] paramVals = joinPoint.getArgs();
        for (int i = 0; i < paramNames.length; i++) {
            String name = paramNames[i];
            //logService.debug(paramNames[i] + ":" + paramVals[i]);
            if (pName.equals(name)) {
                return paramVals[i];
            }
        }
        
        getLogService().error("找不到指定的參數(shù):" + paramName + ", 方法:" + this.getMethodDesc(joinPoint));
        return null;
    }
    
    /**
     * 獲取指定方法(沒有任何參數(shù))的返回值.
     * @param methodName
     * @param joinPoint
     * @return
     */
    private Object getReturnVal(String methodName, ProceedingJoinPoint joinPoint) {
        String mName = CommonUtil.trim(methodName);
        if (mName.length() == 0) {
            getLogService().error("找不到指定的方法:" + methodName + ", 類:" + joinPoint.getTarget().getClass().getName());
            return null;
        }
        
        Object target = joinPoint.getTarget();
        List < Method > methods = getMethods(target.getClass().getName(), methodName);
        
        Method method = null; 
        for (Method mthd : methods) {
            if (mthd.getParameterTypes().length > 0) {
                continue;
            } else {
                method = mthd;
                break;
            }
        }
        if (method == null) {
            String msg = "無法在類中找到指定方法:" + methodName + ", 所屬類:" + target.getClass().getName();
            EIPService.getLogService().error(msg);
            throw new RuntimeException(msg);
        } else {
            //以反射形式執(zhí)行
            Object returnVal = null;
            try {
                method.setAccessible(true); //據(jù)稱設(shè)置為true可以讓java不再檢查方法是否私有等,有助性能提高
                returnVal = method.invoke(target);
            } catch (Throwable e) {
                e.printStackTrace();
            } 
            
            if (returnVal == null) {
                String msg = "指定方法返回null值或沒有返回值:" + methodName + ", 所屬類:" + target.getClass().getName();
                EIPService.getLogService().error(msg);
                throw new RuntimeException(msg);
            } else {
                return returnVal;
            }
        }
    }
    
    /**
     * 獲取被注解的方法所屬的userProfile.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private int getDbYear(ProceedingJoinPoint joinPoint) throws Exception {
        String dbYearSetting = getDbYearSetting(joinPoint);
        if (dbYearSetting.length() == 0) {
            return 0;
        }
        
        if (dbYearSetting.endsWith("()")) {
            //應(yīng)該是一個(gè)方法名,那么通過反射的形式來獲取相應(yīng)的值
            String methodName = dbYearSetting.substring(0, dbYearSetting.length() - 2);
            
            Object returnVal = this.getReturnVal(methodName, joinPoint);
            if (returnVal == null) {
                return 0;
            } else {
                return CommonUtil.parseInt(returnVal.toString());
            }

        } else {
            //應(yīng)該是參數(shù)名,那么尋找對應(yīng)的參數(shù)值
            //尋找對應(yīng)的參數(shù)值
            Object paramVal = getParamVal(dbYearSetting, joinPoint);
            if (paramVal == null) {
                return 0;
            } else {
                return CommonUtil.parseInt(paramVal.toString(), 0);
            }
        }
    }
    
    /**
     * 獲取被注解的方法.
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private Method getMethod(ProceedingJoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        
        List < Method > methods = getMethods(targetName, methodName);
        
        if (methods == null || methods.size() == 0) return null; //不存在,不過這個(gè)一般是不可能的
        
        if (methods.size() == 1) return methods.get(0);
        
        for (Method m : methods) {
            //如果不止一個(gè)同名方法,那么要判斷一番
            Class < ? > [] paramTypes = m.getParameterTypes();
            if (paramTypes.length != arguments.length) continue; //參數(shù)的個(gè)數(shù)不一樣,跳過

            //檢查是否真的匹配
            boolean matched = true; //先假設(shè)是匹配的
            for (int i = 0; i < paramTypes.length; i++) {
                Class < ? > paramType = paramTypes[i];
                Object argument = arguments[i];
                if (argument == null) continue;
                if (!paramType.isInstance(argument)) {
                    matched = false;
                    break; //不匹配,不需要再繼續(xù)
                }
            }

            if (matched) return m; 
        }
        
        return null;
    }
     
}

權(quán)限控制邏輯處理

1.通過反射機(jī)制在目標(biāo)對象中獲取用戶信息
2.通過應(yīng)用id及權(quán)限項(xiàng)找到所有有該用戶權(quán)限的用戶
3.若所有有該權(quán)限用戶集合中包含此目標(biāo)用戶則放行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容