通過注解進(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注解表明這個注釋是由 javadoc記錄的愤惰,在默認(rèn)情況下也有類似的記錄工具苇经。 如果一個類型聲明被注釋了文檔化,它的注釋成為公共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ò)展功能不修改源代碼實現(xiàn),AOP采用橫向抽取機(jī)制塑陵,取代傳統(tǒng)的縱向繼承體系重復(fù)性代碼(性能監(jiān)視,事務(wù)管理蜡励,安全檢查令花,緩存)

AOP操作術(shù)語:

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

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

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

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

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

對@AuthCheck注解的處理實例
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 實現(xiàn)spring aop 切面(Aspect):
 * 一個關(guān)注點的模塊化慎王,這個關(guān)注點可能會橫切多個對象。事務(wù)管理是J2EE應(yīng)用中一個關(guān)于橫切關(guān)注點的很好的例子宏侍。 在Spring
 * AOP中赖淤,切面可以使用通用類(基于模式的風(fēng)格) 或者在普通類中以 @Aspect 注解(@AspectJ風(fēng)格)來實現(xiàn)。
 * AOP代理(AOP Proxy): AOP框架創(chuàng)建的對象谅河,用來實現(xiàn)切面契約(aspect contract)(包括通知方法執(zhí)行等功能)咱旱。
 * 在Spring中,AOP代理可以是JDK動態(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() {}
    
    /**
     * 定義一個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("類實例:" + 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ù),否則會影響性能
            String operSetting = this.getOper(point);
            getLogService().debug("[權(quán)限引擎]operSetting:" + operSetting);
            IOper oper = EIPService.getModuleService().getOper(operSetting, module);
            if (oper != null) {
                //只有oper設(shè)置有效才繼續(xù)病袄,否則會影響性能
                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()+",耗時:"+(e-s));
 
        return object;
    }

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

    /**
     * key為"className:methodName",value是一個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;
            }
        }
    }
    
    /**
     * 獲取被注解的方法在校驗失敗后返回的代碼.
     * @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)該是一個方法名,那么通過反射的形式來獲取相應(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ù)實體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)該是一個方法名膊夹,那么通過反射的形式來獲取相應(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)該是一個方法名,那么通過反射的形式來獲取相應(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è)置值字符串,它的含義可能是一個參數(shù)名,也可能是一個方法名.
     * @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è)置值字符串但汞,它的含義可能是一個參數(shù)名,也可能是一個方法名.
     * @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è)置值字符串互站,它的含義可能是一個參數(shù)名私蕾,也可能是一個方法名.
     * @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)該是一個方法名胡桃,那么通過反射的形式來獲取相應(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; //不存在,不過這個一般是不可能的
        
        if (methods.size() == 1) return methods.get(0);
        
        for (Method m : methods) {
            //如果不止一個同名方法翠胰,那么要判斷一番
            Class < ? > [] paramTypes = m.getParameterTypes();
            if (paramTypes.length != arguments.length) continue; //參數(shù)的個數(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)限項找到所有有該用戶權(quán)限的用戶
3.若所有有該權(quán)限用戶集合中包含此目標(biāo)用戶則放行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末之景,一起剝皮案震驚了整個濱河市斤富,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闺兢,老刑警劉巖茂缚,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異屋谭,居然都是意外死亡脚囊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門桐磁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悔耘,“玉大人,你說我怎么就攤上這事我擂〕囊裕” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵校摩,是天一觀的道長看峻。 經(jīng)常有香客問我,道長衙吩,這世上最難降的妖魔是什么互妓? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上冯勉,老公的妹妹穿的比我還像新娘澈蚌。我一直安慰自己,他們只是感情好灼狰,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布宛瞄。 她就那樣靜靜地躺著,像睡著了一般交胚。 火紅的嫁衣襯著肌膚如雪份汗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天承绸,我揣著相機(jī)與錄音裸影,去河邊找鬼。 笑死军熏,一個胖子當(dāng)著我的面吹牛轩猩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荡澎,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼均践,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了摩幔?” 一聲冷哼從身側(cè)響起彤委,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎或衡,沒想到半個月后焦影,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡封断,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年斯辰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坡疼。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡彬呻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柄瑰,到底是詐尸還是另有隱情闸氮,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布教沾,位于F島的核電站蒲跨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏授翻。R本人自食惡果不足惜财骨,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一镐作、第九天 我趴在偏房一處隱蔽的房頂上張望藏姐。 院中可真熱鬧隆箩,春花似錦、人聲如沸羔杨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兜材。三九已至理澎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間曙寡,已是汗流浹背糠爬。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留举庶,地道東北人执隧。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像户侥,于是被迫代替她去往敵國和親镀琉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容