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自定义Permission、AOP + Shiro -> 正文阅读

[Java知识库]权限解决方案: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(重点)

比如我们要基于数据层面做权限,不是基于接口层面。我们可以使用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

/**
 * @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;
    }

    /**
     * 业务分发:
     * 如果一个permission的字符串是以 data 开头那就走我们自己定义的business逻辑。
     * 如果不是data开头,就走WildcardPermission的逻辑。
     * @param permission
     * @return
     */
    @Override
    public boolean implies(Permission permission) {
        if (!(permission instanceof OrangePermission)) {
            return false;
        }
        OrangePermission wp = (OrangePermission) permission;
        List<Set<String>> otherParts = wp.getParts();
        if (otherParts.size() > 0) {
            Set<String> one = otherParts.get(0);
            if (one.size() == 1 && one.remove(DISTINCTION)){
                return business(permission);
            }else {
                return super.implies(permission);
            }
        }
        return false;
    }
}


实现AuthorizerRealm

@Slf4j
public class UserRealm extends AuthorizingRealm {
    @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());
    }
}

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("接口之前");
    }

}

六、衍生问题

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-03 23:53:36  更:2022-06-03 23:54:22 
 
开发: 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/23 20:27:54-

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