Bootstrap

权限解决方案:Shiro自定义Permission、AOP + Shiro

一、说明

二、三、四都是推导过程,结论在五,可以直接应用。

主要有两点:

  1. 自定义Permission
  2. AOP + Shiro

二、spring-shiro如何实例化并执行对应的permission?

permission执行的片段过程PermissionAnnotationHandler的assertAuthorized,里面会调用DelegatingSubjectDelegatingSubject里面会委托SecurityManager,默认实例化的SecurityManager就是AuthorizingSecurityManagerAuthorizingSecurityManager会再把任务委托给Authorizer,这里Authorizer的实例是ModularRealmAuthorizerModularRealmAuthorizer会把任务再委托给AuthorizingRealmAuthorizingRealm会把任务再给Permission

实例过程:

PermissionAnnotationHandler -> DelegatingSubject -> AuthorizingSecurityManager-> ModularRealmAuthorizer -> AuthorizingRealm -> Permission

抽象过程:

PermissionAnnotationHandler -> Subject -> SecurityManager -> Authorizer -> Realm -> Permission

如果没有这个权限,会在ModularRealmAuthorizerassertRealmsConfigured抛出IllegalStateException异常。

核心想法:自定义permission

1. 核心想法&思路

我们详细说一下,我们可以利用的过程。我们沿着一个核心的问题出发,自定义permission

  • 问题

    1. 在permission执行的片段过程中,它在哪里开始初始化?

    2. 最后我们可不可以自定义permission?

    3. 在哪里处理permission逻辑?

    4. 如果可以那如何去定义?

问题一

问题:在permission执行的片段过程中,它在哪里开始初始化?

经过调试,把代码定位到了AuthorizerRealm上面。其它作用看具体解答的第二处。

重点关注第一个isPermitted的注释说明。

结论:如果要自定义Permission,就得自定义PermissionResolver,让它去实现我们自定义的Permission。但是要覆盖AuthorizingRealm的setPermissionResolver方法。

public abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
    
    // 本次问题的主角依赖。get/set不贴出来了
    private PermissionResolver permissionResolver;
    private RolePermissionResolver permissionRoleResolver;
    
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        // 问题核心,这里直接得到Permission的实例。
        // PermissionReasolver的作用就是创建Permission实例的地方,在resolvePermission方法中创建。
        // WildcardPermissionResolver就是一个好例子。
        // 重点:所以我们自己自定义一个PermissionResolver,然后覆盖掉AuthorizingRealm的setPermissionResolver就行了。
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted(permission, info);
    }

    //visibility changed from private to protected per SHIRO-332
    protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
        Collection<Permission> perms = getPermissions(info);
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                if (perm.implies(permission)) {
                    return true;
                }
            }
        }
        return false;
    }
}
问题二

问题:最后我们可不可以自定义permission?

由问题一的解答,我们就知道Permission是可以自定义,但是要实现自己的PermissionResolver。而且这个PermissionResolver必须要给到AuthorizerRealm对象。

问题三

问题:在哪里处理permission逻辑?

由开头说的实例过程

PermissionAnnotationHandler -> DelegatingSubject -> AuthorizingSecurityManager-> ModularRealmAuthorizer -> AuthorizingRealm -> Permission

我们就知道是AuthorizingRealm把任务委托给Permission处理,这个才是最终的执行过程。shiro默认实现是WildcardPermission

问题四

问题:如果可以那如何去定义?

这个问题是我自己提问的,但是我觉得这是一个比较具有方向性的问题。

  1. 首先我们已经知道要自定义Permission ,必须要自定义 WildcardPermissionResolver,再把它的对象给到Authorizer。
  2. 其次我们再决定 Permission是如何实现权限的比对的,可以参考WildcardPermission。会在目录三仔细分析
  3. 最后我们可以模仿PermissionAnnotationHandler,实现更细颗粒度的权限控制,比如:基于数据做权限控制,基于栏目关系做权限控制。会在目录四仔细分析

2. 具体解读

(1)初始化PermissionResolver

UserRealm继承了AuthorizingRealm,所以我们在初始化UserRealm之后,会初始化AuthorizingRealm

里面默认给我们实现了WildcardPermissionResolver,它的resolvePermission方法实例化的就是WildcardPermission

ublic abstract class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware {
    public AuthorizingRealm() {
        this(null, null);
    }
    public AuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
        super();
        if (cacheManager != null) {setCacheManager(cacheManager);}
        if (matcher != null) {setCredentialsMatcher(matcher);}

        this.authorizationCachingEnabled = true;
        // 默认的PermissionResolver
        this.permissionResolver = new WildcardPermissionResolver();

        int instanceNumber = INSTANCE_COUNT.getAndIncrement();
        this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
        if (instanceNumber > 0) {
            this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
        }
    }
}

(2) 实例化并执行permission

下面三个方法共同构成了单个permission的整个逻辑。

它主要获取了PermissionResolver的具体实现,这里默认的是WildcardPermissionResolver。它主要的作用是实例化具体permission的实现。

它主要是执行了一个抽象方法getAuthorizationInfo,其实也就是我们UserRealm实现的getAuthorizationInfo, 主要是获取AuthorizationInfo

它就是执行具体的permission了,比对 注解获取到的permission和AuthorizationInfo里面的permission,会返回比较的结果。这里有一个过程 SecurityManager -> Authorizer -> Realm,执行isPermitted是从SecurityManager开始,一直通过委托,最后在realm执行,realm就是我们可以定义扩展的地方。

class AuthorizingRealm extends AuthenticatingRealm
        implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware{
    /**
     * 下面截取的只是String的permission,因为其它的跟这个逻辑都差不多。
     */
    
    // 一
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        Permission p = getPermissionResolver().resolvePermission(permission);
        return isPermitted(principals, p);
    }

    // 二
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        AuthorizationInfo info = getAuthorizationInfo(principals);
        return isPermitted(permission, info);
    }

    // 三
    protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
        Collection<Permission> perms = getPermissions(info);
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                if (perm.implies(permission)) {
                    return true;
                }
            }
        }
        return false;
    }
}

(3) shiro是如何处理permission返回的结果?

[1] 继承关系SecurityManager

在这里插入图片描述
在这里插入图片描述

对比上面两个继承图,最后一个是SpringBoot使用的。

// shiro简单的配置
class ShiroConfig{
     @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        return shiroFilterFactoryBean;
    }

    @Bean("securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Autowired Realm realm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
    
    /**
     * 开启shiro的注解扫描。
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 解决注解扫描不生效的问题。
     * @return
     */
     @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
}
[2] 逻辑终结

AuthorizingSecurityManager permission回调逻辑结束点之一,权限不足的时候,会抛出UnauthorizedException,并停止往下执行

class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
    // 重要属性
    protected Collection<Realm> realms;
    
    public AuthorizingSecurityManager() {
        super();
        this.authorizer = new ModularRealmAuthorizer();
    }
    
    // checkPermission 会调用对应的isPermission
    public void checkPermission(PrincipalCollection principals, String permission) throws AuthorizationException {
        assertRealmsConfigured();
        if (!isPermitted(principals, permission)) {
            throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
        }
    }
	
    public void checkPermission(PrincipalCollection principals, Permission permission) throws AuthorizationException {
        assertRealmsConfigured();
        if (!isPermitted(principals, permission)) {
            throw new UnauthorizedException("Subject does not have permission [" + permission + "]");
        }
    }
    
    // 下面两个都把授权委托给了AuthorizerRealm
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }
     public boolean isPermitted(PrincipalCollection principals, String permission) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)) continue;
            if (((Authorizer) realm).isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }
}

上面有两个checkPermission方法,其实逻辑都是一样的,只是permission的参数类型不一样。

他们都调用了isPermission,根据返回的结果,再决定抛出异常,终止程序继续往下运行,这就是其中之一的结束点。

### (4) PermissionAnnotationHandler - @RequisePermission

class 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.checkPermission(perms[0]);
            return;
        }
        if (Logical.AND.equals(rpAnnotation.logical())) {
            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]);

        }
    }
}

三、ModularRealmAuthorizer

继承图

在这里插入图片描述

由继承图看出,ModularRealmAuthorizer具有初始化 PermissionResolver、和初始化RolePermissionResolver的功能。

还具有Authorizer授权的功能。它是permission整个委托过程的一环,AuthorizingSecurityManager -> ModularRealmAuthorizer -> UserRealm

具体初始化地方是在,AuthorizingSecurityManager的构造函数里边。

public abstract class AuthorizingSecurityManager extends AuthenticatingSecurityManager {
	/**
     * The wrapped instance to which all of this <tt>SecurityManager</tt> authorization calls are delegated.
     */
    private Authorizer authorizer;

    /**
     * Default no-arg constructor that initializes an internal default
     * {@link org.apache.shiro.authz.ModularRealmAuthorizer ModularRealmAuthorizer}.
     */
    public AuthorizingSecurityManager() {
        super();
        this.authorizer = new ModularRealmAuthorizer();
    }
}

ModularRealmAuthorizer

class ModularRealmAuthorizer{
    // 给realm初始化RolePermissionResolver
    protected void applyRolePermissionResolverToRealms() {
        RolePermissionResolver resolver = getRolePermissionResolver();
        Collection<Realm> realms = getRealms();
        if (resolver != null && realms != null && !realms.isEmpty()) {
            for (Realm realm : realms) {
                if (realm instanceof RolePermissionResolverAware) {
                    ((RolePermissionResolverAware) realm).setRolePermissionResolver(resolver);
                }
            }
        }
    }

    // 授权,这里会直接调用我们UserRealm继承的AuthorizingRealm的isPermitted()
    public boolean isPermitted(PrincipalCollection principals, Permission permission) {
        assertRealmsConfigured();
        for (Realm realm : getRealms()) {
            if (!(realm instanceof Authorizer)){ continue;}
            if (((Authorizer) realm).isPermitted(principals, permission)) {
                return true;
            }
        }
        return false;
    }
}

四、WildcardPermission对比规则

下面我就粘出部分重要的代码逻辑。

public class WildcardPermission implements Permission, Serializable {
    protected static final String WILDCARD_TOKEN = "*";
    protected static final String PART_DIVIDER_TOKEN = ":";
    protected static final String SUBPART_DIVIDER_TOKEN = ",";
    protected static final boolean DEFAULT_CASE_SENSITIVE = false;
    // 这个方法是把wildcardString字符串分割成一个List<Set<String>>,先通过:分割,之后基于:分割结果,再通过,分割。
    // 
    protected void setParts(String wildcardString, boolean caseSensitive) {
        wildcardString = StringUtils.clean(wildcardString);
        if (wildcardString == null || wildcardString.isEmpty()) {
            throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.");
        }
        if (!caseSensitive) {
            wildcardString = wildcardString.toLowerCase();
        }
        // 通过:分割
        List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN));
        this.parts = new ArrayList<Set<String>>();
        for (String part : parts) {
            // 基于:分割的结果,再通过,进行分割
            Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));
            if (subparts.isEmpty()) {
                throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted.");
            }
            this.parts.add(subparts);
        }

        if (this.parts.isEmpty()) {
            throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted.");
        }
    }
    
    // 权限逻辑对比。
    // 前提:括号这个形参是通过PermissionAnnotationHandler扫描注解得来的。然后本身类的对象是通过AuthorizationInfo实例化出来的。
    public boolean implies(Permission p) {
        if (!(p instanceof WildcardPermission)) {
            return false;
        }
        WildcardPermission wp = (WildcardPermission) p;
        List<Set<String>> otherParts = wp.getParts();
        int i = 0;
        // 例子:p(形参) [[user], [update, save]] obj(当前类对象)[[user], [update, insert]]
        // 1. false的逻辑: obj存在通配符 与 obj包含p的所有子元素,两个都不满足的情况下,就是权限不够。
        for (Set<String> otherPart : otherParts) {
            if (getParts().size() - 1 < i) {
                return true;
            } else {
                Set<String> part = getParts().get(i);
                if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
                    return false;
                }
                i++;
            }
        }
        // 例子:p(形参) [[user], [update, save]] obj(当前类对象)[[user], [update, insert], [aa]]
        // 2. false的逻辑: 如果obj比p元素多的情况下,obj不包含*就代表权限不足。
        for (; i < getParts().size(); i++) {
            Set<String> part = getParts().get(i);
            if (!part.contains(WILDCARD_TOKEN)) {
                return false;
            }
        }
        // 以上false逻辑都不满足的情况下,就代表权限满足。
        return true;
    }
}

五、基于业务自定义permission & 重写AuthorizerRealm的isPermitted(重点)

比如我们要基于数据层面做权限,不是基于接口层面。我们可以使用SpringAOP实现,下面展示代码例子。

思路说明

这里主要通过 aop 拦截具体的方法,执行Subject.checkPermission。

然后流程就是

AOP -> DelegatingSubject -> AuthorizingSecurityManager-> ModularRealmAuthorizer -> AuthorizingRealm -> Permission

shiro自带的流程是

PermissionAnnotationHandler -> DelegatingSubject -> AuthorizingSecurityManager-> ModularRealmAuthorizer -> AuthorizingRealm -> Permission

这里我们自己的流程自带两个扩展点,AOP这里是一个,自定义Permission是一个。

-------------- 下面是重点 --------------

  1. 原本的PermissionAnnotationHandler是获取方法上的注解拿到 具体的Permission字符串,现在我们改成AOP,获取Permission字符串的地方我们可以改成数据库。

  2. 最后就是Permission,我们可以自己定义Permision实现,对比的规则也可以根据自己的业务进行扩展。而不仅仅限于WildcardPermission提供的规则。

自定义Permission & AuthorizingRealm

/**
 * @author :orange
 * @date :Created in 2022/6/1 10:30
 * @description:自定义permission处理器
 */
public class OrangePermissionResolver extends WildcardPermissionResolver {

    @Override
    public Permission resolvePermission(String permissionString) {
        return new OrangePermission(permissionString);
    }
}


/**
 * @author :orange
 * @date :Created in 2022/6/1 10:46
 * @description:具体处理
 */
public class OrangePermission extends WildcardPermission {

    /**
     * 业务标识符
     */
    private final String DISTINCTION = "data";

    public OrangePermission(String wildcardString) {
        this(wildcardString, false);
    }
    public OrangePermission(String wildcardString, boolean caseSensitive) {
        super(wildcardString, caseSensitive);
    }

    /**
     * 具体扩展的业务
     * @return
     */
    public boolean business(Permission permission) {
        return true;
    }

    /**
     * 这里可以自定义了。
     * @param permission
     * @return
     */
    @Override
    public boolean implies(Permission permission) {
        if (!(permission instanceof OrangePermission)) {
            return false;
        }
        return super.implies(permission);
    }
}


实现AuthorizerRealm,重写isPermitted(重要)

/**
 * @author :orange
 * @date :Created in 2022/6/6 12:28
 * @description:节点和权限判断。
 */
@Component
public class NodeRolePermission {
  
    /**
     * 针对于 父级没有权限,子级有权限,但是要以父级为准的情况。
     * 权限是否处于开启状态是available字段
     * @return
     */
    private boolean level(CMSPermission cmsPermission) {
        return true;
    }

    /**
     * 针对于节点和角色,以节点为准,角色为次。
     * 如果节点不开启,那么角色就算有权限也不能访问。
     * 如果节点开启,角色没有权限也不能访问。
     * 要两个同时开启才能有权限。
     * @return
     */
    private boolean intersection(CMSPermission cmsPermission) {
        return false;
    }

    /**
     * 不需要多次处理,处理一次就行了。
     * @param wildcard
     * @return
     */
    @Cacheable(value = "NodeRolePermissionBusiness", key = "#wildcard")
    public boolean business(String wildcard) {
        SysLanguage sysLanguage = langUtil.getLoginUserLang();
        Optional<CMSPermission> optionalPermission = cmsPermissionRepository.findByNameAndDeletedIsFalseAndSysLanguage(wildcard, sysLanguage);
        if (optionalPermission.isEmpty()) {
            return false;
        }
        CMSPermission cmsPermission = optionalPermission.get();
        boolean intersection = intersection(cmsPermission);
        boolean level = level(cmsPermission);
        return intersection && level;
    }
}

@Slf4j
public class UserRealm extends AuthorizingRealm {
	@Resource(type = NodeRolePermission.class)
    private NodeRolePermission nodeRolePermission;
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(Arrays.asList(new String[]{"user:show,login"}));
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("authenticationInfo");
        String username = (String) authenticationToken.getPrincipal();
        String password = new String((char[]) authenticationToken.getCredentials());
        String auth = "orange";
        if(!auth.equals(username)) {
            return null;
        }
        if (!auth.equals(password)) {
            return null;
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }

	// 这里也是业务扩展点,就比如你在对比permission之后,还需要对比栏目的父级是否也开启的权限。
	@Override
    protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
        Collection<Permission> perms = getPermissions(info);
        if (perms != null && !perms.isEmpty()) {
            for (Permission perm : perms) {
                if (perm.implies(permission)) {
                    return true && nodeRolePermission.business(((KguPermission) permission).getWildcard());
                }
            }
        }
        return false;
    }
}

shiro配置

/**
 * @author :orange
 * @date :Created in 2022/5/30 0:20
 * @description:shiro配置
 */
@Configuration
@Slf4j
public class ShiroConfig {

    /**
     * ShiroFilterFactoryBean作用是创建AbstractShiroFilter对象。
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        return shiroFilterFactoryBean;
    }

    @Bean("userRealm")
    public UserRealm createRealm() {
        UserRealm userRealm = new UserRealm();
        userRealm.setPermissionResolver(new OrangePermissionResolver());
        return userRealm;
    }

    /**
     * SecurityManager的web默认实现
     * @return
     */
    @Bean("securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Autowired UserRealm userRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }

    /**
     * 开启shiro的注解扫描。
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 解决注解扫描不生效的问题。
     * @return
     */
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setUsePrefix(true);
        return defaultAdvisorAutoProxyCreator;
    }
}

aop切入

这个注解建议可以写在方法上。

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OrangePermission {}
@Aspect
@Component
public class ShiroAdvice {

    @Pointcut("@annotation(OrangePermission)")
    public void data(){};

    @Before("data()")
    public void advice() {
        Subject subject = SecurityUtils.getSubject();
        subject.checkPermission("user:none");
        System.out.println("接口之前");
    }

}

六、衍生问题

;