IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Shiro流程简析 注解 -> 正文阅读

[Java知识库]Shiro流程简析 注解

简介

Shiro框架中的授权注解有:

  • @RequiresPermissions
  • @RequiresRoles
  • @RequiresUser
  • @RequiresGuest
  • @RequiresAuthentication

一般在Controller的方法上加上授权注解,表明需要满足一定条件,才能执行该方法;

举例:

@RestController
public class DemoController {

    // 当发起请求的主体拥有权限"demo:operation"才可以访问该方法
    @RequiresPermissions("demo:operation")
    @GetMapping(value = "/demo")
    public String operation() {
        return "OK";
    }
}

那么Shiro框架是如何实现的呢?

主要分为两步:

  1. DemoController创建时,会经过Bean后置处理器,其中DefaultAdvisorAutoProxyCreator会将AuthorizationAttributeSourceAdvisor应用到该Bean的代理;
  2. 访问DemoController.operation方法,其实是执行DemoController的代理的operation方法,在执行会先执行AuthorizationAttributeSourceAdvisor的逻辑,校验方法上授权注解(如果存在),校验通过则继续执行原方法,否则抛出异常;

接下来分别进行分析。

Bean的代理的创建

Spring Boot创建Bean后,会对其进行后置处理,即AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization,代码如下:

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {

    Object result = existingBean;
    // 遍历Bean后置处理器链
    // 一般Bean后置处理器,如果处理逻辑,直接返回原Bean
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            // 无需继续处理,直接返回
            return result;
        }
        result = current;
    }
    return result;
}

Bean后置处理器链,如下图所示:

TODO

其中DefaultAdvisorAutoProxyCreator(需要在Shiro配置中配置,但如果引入AOP组件,AOP组件会自动引入AnnotationAwareAspectJAutoProxyCreator,则无需配置),会为可以被切面拦截的Bean,利用切面创建增强代理,其处理逻辑如下:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 代理Bean
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

/**
  * 代理Bean
  */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        // 无需增强,设置标识
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // 获取可以拦截该Bean的切面
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        // 设置增强标识
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 利用切面创建该Bean的增强代理
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回代理
        return proxy;
    }

    // 无需增强,设置标识
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

该Bean后置处理器的后置处理主要分两步:

  1. 获取可以拦截Bean的切面;
  2. 利用切面创建Bean的增强代理;

获取可以拦截Bean的切面

AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean;

内部调用findEligibleAdvisors,代码如下:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 获取当前存在的切面(包括AuthorizationAttributeSourceAdvisor)
    // AnnotationAwareAspectJAutoProxyCreator重写父类方法,根据advisorRetrievalHelper查找切面
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 获取可以应用到该Bean的切面,即切面是否匹配该Bean的类型,内部调用AopUtils.findAdvisorsThatCanApply判断
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

获取到的candidateAdvisors,如下图所示:

TODO

然后筛选可应用的的切面,AopUtils.findAdvisorsThatCanApply方法会对candidateAdvisors进行遍历,通过canApply筛选,代码如下:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");

    // 检查类型
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        // 全匹配直接返回true
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }

    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    Set<Class<?>> classes = new LinkedHashSet<>();
    if (!Proxy.isProxyClass(targetClass)) {
        // 添加该类
        classes.add(ClassUtils.getUserClass(targetClass));
    }

    // 添加该类实现的接口类型
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    // 遍历所有类型
    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        // 遍历该类型的所有方法
        for (Method method : methods) {
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    methodMatcher.matches(method, targetClass)) {
                // 只要有一个方法满足,那么就表示切面可以应用到该类上
                return true;
            }
        }
    }
    // 都不满足,则不支持
    return false;
}

canApply实现中会调用切面的matches方法,AuthorizationAttributeSourceAdvisor.macthes的实现如下:

public boolean matches(Method method, Class targetClass) {
    Method m = method;

    // 方法上有授权注解则匹配成功
    if ( isAuthzAnnotationPresent(m) ) {
        return true;
    }

    // 本类的方法没有,则判断父类的同名方法上是否存在,或者本类上是否存在授权注解
    //The 'method' parameter could be from an interface that doesn't have the annotation.
    //Check to see if the implementation has it.
    if ( targetClass != null) {
        try {
            m = targetClass.getMethod(m.getName(), m.getParameterTypes());
            return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass);
        } catch (NoSuchMethodException ignored) {
            //default return value is false.  If we can't find the method, then obviously
            //there is no annotation, so just use the default return value.
        }
    }

    return false;
}

只要该Bean的方法上存在Shiro框架的授权注解:

  • @RequiresPermissions
  • @RequiresRoles
  • @RequiresUser
  • @RequiresGuest
  • @RequiresAuthentication

那么就可以应用切面AuthorizationAttributeSourceAdvisor到该Bean,如下图所示:

TODO

利用切面创建Bean的增强代理

AbstractAutoProxyCreator.createProxy,代码如下:

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
        @Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    // 创建代理工厂,并配置
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    // 根据拦截器构建切面
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    // 创建代理
    return proxyFactory.getProxy(getProxyClassLoader());
}

根据获取切面,构建代理需要的切面,会添加公共拦截器,代码如下:

protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
    // 根据AbstractAutoProxyCreator.interceptorNames属性,解析公共拦截器
    // Handle prototypes correctly...
    Advisor[] commonInterceptors = resolveInterceptorNames();

    List<Object> allInterceptors = new ArrayList<>();
    if (specificInterceptors != null) {
        if (specificInterceptors.length > 0) {
            // specificInterceptors may equal PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS
            allInterceptors.addAll(Arrays.asList(specificInterceptors));
        }
        if (commonInterceptors.length > 0) {
            // 添加公共拦截器
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }
    if (logger.isTraceEnabled()) {
        int nrOfCommonInterceptors = commonInterceptors.length;
        int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
        logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
    }

    // 包装拦截器为切面
    Advisor[] advisors = new Advisor[allInterceptors.size()];
    for (int i = 0; i < allInterceptors.size(); i++) {
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
}

配置好代理工厂后,就可以创建代理了:proxyFactory.getProxy(getProxyClassLoader()),内部实现为:createAopProxy().getProxy(classLoader);

createAopProxy最终由DefaultAopProxyFactory.createAopProxy创建出代理工厂,代码如下:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    if (!NativeDetector.inNativeImage() &&
            (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 创建CGLIB代理
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}

TODO

然后就是从代理工厂ObjenesisCglibAopProxy获取代理,代码如下:

public Object getProxy(@Nullable ClassLoader classLoader) {
  if (logger.isTraceEnabled()) {
    logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
  }

  try {
    Class<?> rootClass = this.advised.getTargetClass();
    Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

    Class<?> proxySuperClass = rootClass;
    if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
      proxySuperClass = rootClass.getSuperclass();
      Class<?>[] additionalInterfaces = rootClass.getInterfaces();
      for (Class<?> additionalInterface : additionalInterfaces) {
        this.advised.addInterface(additionalInterface);
      }
    }

    // Validate the class, writing log messages as necessary.
    validateClassIfNecessary(proxySuperClass, classLoader);

    // 创建CGLIB 增强器
    // Configure CGLIB Enhancer...
    Enhancer enhancer = createEnhancer();

    // 配置CGLIB 增强器
    if (classLoader != null) {
      enhancer.setClassLoader(classLoader);
      if (classLoader instanceof SmartClassLoader &&
          ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
        enhancer.setUseCache(false);
      }
    }
    enhancer.setSuperclass(proxySuperClass);
    enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
    enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
    enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

    // 获取回调
    // 会根据切面生成DynamicAdvisedInterceptor,用于AOP回调
    Callback[] callbacks = getCallbacks(rootClass);
    Class<?>[] types = new Class<?>[callbacks.length];
    for (int x = 0; x < types.length; x++) {
      types[x] = callbacks[x].getClass();
    }
    // fixedInterceptorMap only populated at this point, after getCallbacks call above
    enhancer.setCallbackFilter(new ProxyCallbackFilter(
        this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
    enhancer.setCallbackTypes(types);

    // 创建增强类和实例
    // Generate the proxy class and create a proxy instance.
    return createProxyClassAndInstance(enhancer, callbacks);
  }
  catch (CodeGenerationException | IllegalArgumentException ex) {
    throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
        ": Common causes of this problem include using a final class or a non-visible class",
        ex);
  }
  catch (Throwable ex) {
    // TargetSource.getTarget() failed
    throw new AopConfigException("Unexpected AOP exception", ex);
  }
}

至此,DefaultAdvisorAutoProxyCreator的后置处理就结束了。

Bean的代理的方法的执行

Bean的增强代理的方法的执行流程,如下图所示:

TODO

入口:CglibAopProxy.DynamicAdvisedInterceptor.intercept,代码如下:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Object target = null;
    TargetSource targetSource = this.advised.getTargetSource();
    try {
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 获取匹配当前方法的拦截器
        // 1. AopAllianceAnnotationsAuthorizingMethodInterceptor
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // Check whether we only have one InvokerInterceptor: that is,
        // no real advice, but just reflective invocation of the target.
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // We can skip creating a MethodInvocation: just invoke the target directly.
            // Note that the final invoker must be an InvokerInterceptor, so we know
            // it does nothing but a reflective operation on the target, and no hot
            // swapping or fancy proxying.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
            // 创建原方法的调用,并执行
            // We need to create a method invocation...
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

创建方法调用:new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy),代码如下:

public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
    Object[] arguments, @Nullable Class<?> targetClass,
    List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {

    // 调用父类的构造方法
    super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
    this.methodProxy = methodProxy;
    this.publicMethod = Modifier.isPublic(method.getModifiers());
}

// 父类构造方法
protected ReflectiveMethodInvocation(
        Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
        @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

    this.proxy = proxy;
    this.target = target;
    this.targetClass = targetClass;
    this.method = BridgeMethodResolver.findBridgedMethod(method);
    this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
    // 注入拦截器
    this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}

CglibMethodInvocation内部封装了拦截器集合,类似于过滤器链执行机制,CglibMethodInvocation.proceed会先执行剩下的拦截器,若所有的拦截器都执行完了,才执行代理方法;

拦截器方法的返回有两种形式:

  1. 回调CglibMethodInvocation.proceed;
  2. 抛出异常,终止代理方法的调用;

CglibMethodInvocation.proceed代码如下:

public Object proceed() throws Throwable {
    // this.currentInterceptorIndex从-1开始递增,直到遍历完所有的方法拦截器
    //  We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 遍历完所有的方法拦截器,开始执行原方法
        return invokeJoinpoint();
    }

    // 获取当前处理的方法拦截器
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            // 递归实现拦截器的遍历
            return proceed();
        }
    }
    else {
        // 调用方法拦截器
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

本文关注拦截器AopAllianceAnnotationsAuthorizingMethodInterceptor的调用过程,代码如下:

public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    // 使用Shiro框架的方法拦截器,适配AOP的方法拦截器
    org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
    // 调用父类方法执行方法拦截器
    return super.invoke(mi);
}

/**
  * 适配AOP 方法拦截器
  */
protected org.apache.shiro.aop.MethodInvocation createMethodInvocation(Object implSpecificMethodInvocation) {
    final MethodInvocation mi = (MethodInvocation) implSpecificMethodInvocation;

    // 简单的封装各方法
    return new org.apache.shiro.aop.MethodInvocation() {
        public Method getMethod() {
            return mi.getMethod();
        }

        public Object[] getArguments() {
            return mi.getArguments();
        }

        public String toString() {
            return "Method invocation [" + mi.getMethod() + "]";
        }

        public Object proceed() throws Throwable {
            return mi.proceed();
        }

        public Object getThis() {
            return mi.getThis();
        }
    };
}

/**
  * 拦截处理
  */
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    // 校验授权
    assertAuthorized(methodInvocation);
    // 继续执行方法调用
    // 类似于过滤器链执行机制,方法调用封装若干方法拦截器
    // 执行方法调用时,会先执行剩下的方法拦截器,如果没有则才执行原方法
    // 方法拦截器成功执行完拦截逻辑,会继续回调执行方法调用,从而实现遍历所有的方法拦截器
    return methodInvocation.proceed();
}

/**
  * 校验授权
  */
protected void assertAuthorized(MethodInvocation methodInvocation) throws AuthorizationException {
    // 获取配置的方法拦截器
    // 默认为:
    //default implementation just ensures no deny votes are cast:
    Collection<AuthorizingAnnotationMethodInterceptor> aamis = getMethodInterceptors();
    if (aamis != null && !aamis.isEmpty()) {
        for (AuthorizingAnnotationMethodInterceptor aami : aamis) {
            // 是否匹配当前方法拦截器
            if (aami.supports(methodInvocation)) {
                // 校验授权
                aami.assertAuthorized(methodInvocation);
            }
        }
    }
}

/**
  * 校验授权
  */
public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
    try {
        // 使用内部的AnnotationHandler校验原方法上的注解信息
        ((AuthorizingAnnotationHandler)getHandler()).assertAuthorized(getAnnotation(mi));
    }
    catch(AuthorizationException ae) {
        // Annotation handler doesn't know why it was called, so add the information here if possible. 
        // Don't wrap the exception here since we don't want to mask the specific exception, such as 
        // UnauthenticatedException etc. 
        if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
        throw ae;
    }         
}

AnnotationHandler的实现,以PermissionAnnotationHandler为例进行分析,代码如下:

/**
  * 校验授权
  */
public void assertAuthorized(Annotation a) throws AuthorizationException {
    if (!(a instanceof RequiresPermissions)) return;

    RequiresPermissions rpAnnotation = (RequiresPermissions) a;
    String[] perms = getAnnotationValue(a);
    Subject subject = getSubject();

    // 单个权限
    if (perms.length == 1) {
        // 校验当前Subject是否拥有指定的权限
        subject.checkPermission(perms[0]);
        return;
    }

    // 多个权限,需要判断是与逻辑还是或逻辑
    if (Logical.AND.equals(rpAnnotation.logical())) {
        // 校验当前Subject是否拥有指定的权限集合
        getSubject().checkPermissions(perms);
        return;
    }
    if (Logical.OR.equals(rpAnnotation.logical())) {
        // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
        boolean hasAtLeastOnePermission = false;
        for (String permission : perms) if (getSubject().isPermitted(permission)) hasAtLeastOnePermission = true;
        // 或逻辑,一个都不满足的话,则抛出第一个权限校验失败的异常
        // Cause the exception if none of the role match, note that the exception message will be a bit misleading
        if (!hasAtLeastOnePermission) getSubject().checkPermission(perms[0]);
    }
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-11-26 08:43:52  更:2021-11-26 08:46:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:07:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码