shiro源码分析之自定义注解RequiredPermission(可代替RequiresPermissions)

1.现象

shiro使用RequiresPermissions等注解必须添加如下切面,否则不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}

2.源码分析

分析AuthorizationAttributeSourceAdvisor(继承自StaticMethodMatcherPointcutAdvisor)源码发现,所有授权注解校验都在AUTHZ_ANNOTATION_CLASSES中    
private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
        new Class[] {
                RequiresPermissions.class, RequiresRoles.class,
                RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
        };
        
        
并且其构造方法中加了AopAllianceAnnotationsAuthorizingMethodInterceptor(继承自AnnotationsAuthorizingMethodInterceptor)拦截器        
public AuthorizationAttributeSourceAdvisor() {
    setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
}   
    

而AopAllianceAnnotationsAuthorizingMethodInterceptor拦截器构造方法中加了一堆的对应注解拦截器
public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
    List<AuthorizingAnnotationMethodInterceptor> interceptors =
            new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);

    //use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
    //raw JDK resolution process.
    AnnotationResolver resolver = new SpringAnnotationResolver();
    //we can re-use the same resolver instance - it does not retain state:
    interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
    interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
    interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
    interceptors.add(new UserAnnotationMethodInterceptor(resolver));
    interceptors.add(new GuestAnnotationMethodInterceptor(resolver));

    setMethodInterceptors(interceptors);
}    

      
分析注解对应拦截器(都继承自AuthorizingAnnotationMethodInterceptor)发现,在其构造方法中添加校验权限的handler(PermissionAnnotationMethodInterceptor为例)
public PermissionAnnotationMethodInterceptor(AnnotationResolver resolver) {
    super( new PermissionAnnotationHandler(), resolver);
}


而他们的handler都是简单的调用权限校验方法,源码如下
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]);
        
    }
}

3.自定义注解RequiredPermission, 用于授权校验

这里只是简单的加了des, 可根据自己需求去改    
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredPermission {

    String[] value();

    Logical logical() default Logical.AND;

    String des() default "";
}

4.自定义MineAuthorizationAttributeSourceAdvisor继承自StaticMethodMatcherPointcutAdvisor

其实和AuthorizationAttributeSourceAdvisor的区别是AUTHZ_ANNOTATION_CLASSES中加入自己的注解, 然后使用自己的拦截器MineAopAllianceAnnotationsAuthorizingMethodInterceptor
public class MineAuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {
    private static final Logger log = LoggerFactory.getLogger(org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor.class);

    private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
            new Class[] {
                    RequiresPermissions.class, RequiresRoles.class,
                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class,
                    RequiredPermission.class
            };

    protected SecurityManager securityManager = null;

    public MineAuthorizationAttributeSourceAdvisor() {
        setAdvice(new MineAopAllianceAnnotationsAuthorizingMethodInterceptor());
    }

    public SecurityManager getSecurityManager() {
        return securityManager;
    }

    public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
        this.securityManager = securityManager;
    }

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

        if ( isAuthzAnnotationPresent(m) ) {
            return true;
        }

        if ( targetClass != null) {
            try {
                m = targetClass.getMethod(m.getName(), m.getParameterTypes());
                if ( isAuthzAnnotationPresent(m) ) {
                    return true;
                }
            } catch (NoSuchMethodException ignored) {
            }
        }

        return false;
    }

    private boolean isAuthzAnnotationPresent(Method method) {
        for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
            Annotation a = AnnotationUtils.findAnnotation(method, annClass);
            if ( a != null ) {
                return true;
            }
        }
        return false;
    }

}

5.自定义拦截器MineAopAllianceAnnotationsAuthorizingMethodInterceptor (继承自AopAllianceAnnotationsAuthorizingMethodInterceptor)

就是把自己的RequiredPermissionAnnotationMethodInterceptor拦截器添加到advice中
public class MineAopAllianceAnnotationsAuthorizingMethodInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor {

    public MineAopAllianceAnnotationsAuthorizingMethodInterceptor() {
        super();
        this.methodInterceptors.add(new RequiredPermissionAnnotationMethodInterceptor(new SpringAnnotationResolver()));
    }
}

6.自定义RequiredPermissionAnnotationMethodInterceptor拦截器 (继承自AuthorizingAnnotationMethodInterceptor)

定义自己的注解拦截器,并使用自己的RequiredPermissionAnnotationHandler
public class RequiredPermissionAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {

    public RequiredPermissionAnnotationMethodInterceptor(){
        super(new RequiredPermissionAnnotationHandler());
    }

    public RequiredPermissionAnnotationMethodInterceptor(AnnotationResolver resolver){
        super(new RequiredPermissionAnnotationHandler(), resolver);
    }
}

7.RequiredPermissionAnnotationHandler (继承自AuthorizingAnnotationHandler)

在assertAuthorized中添加自己的校验逻辑
public class RequiredPermissionAnnotationHandler extends AuthorizingAnnotationHandler {

    public RequiredPermissionAnnotationHandler() {
        super(RequiredPermission.class);
    }

    protected String[] getAnnotationValue(Annotation a) {
        RequiredPermission rpAnnotation = (RequiredPermission) a;
        return rpAnnotation.value();
    }

    @Override
    public void assertAuthorized(Annotation a) throws AuthorizationException {
        if (!(a instanceof RequiredPermission)) {
            return;
        }
        RequiredPermission rpAnnotation = (RequiredPermission) 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())) {
            boolean hasAtLeastOnePermission = false;
            for (String permission : perms) {
                if (getSubject().isPermitted(permission)) {
                    hasAtLeastOnePermission = true;
                }
            }
            if (!hasAtLeastOnePermission) {
                getSubject().checkPermission(perms[0]);
            }
        }
    }
}

8.替换MineAuthorizationAttributeSourceAdvisor替换AuthorizationAttributeSourceAdvisor

@Bean
public MineAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    MineAuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new MineAuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}

9.测试

@RequestMapping(value="/addTeacher.do", method =RequestMethod.GET)
@RequiredPermission(value = {"teacher:add"}, des = "添加老师")
public ResultMessage addTeacher(){
   return ResultMessage.success("permission success.");
}

@RequestMapping(value="/updateTeacher.do", method =RequestMethod.GET)
@RequiredPermission(value = {"teacher:update"},  des = "添加老师")
public ResultMessage updateTeacher(){
   return ResultMessage.success("permission success.");
}

源码 https://gitee.com/jsjack_wang/springboot-demo dev-shiro分支

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,针对您的问题,您可以使用Shiro提供的权限注解 `@RequiresPermissions`,并通过自定义注解来实现权限控制。 以下是一个示例代码,其实现了一个自定义注解 `@ApiPermission`,并在 `ShiroFilterFactoryBean` 中添加了该注解,用于控制开放接口的访问权限: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ApiPermission { String[] value() default {}; } public class ApiPermissionFilter extends AuthorizationFilter { @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { Subject subject = getSubject(request, response); String[] permissions = (String[]) mappedValue; if (permissions == null || permissions.length == 0) { return true; } for (String permission : permissions) { if (subject.isPermitted(permission)) { return true; } } return false; } } @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); Map<String, Filter> filterMap = new LinkedHashMap<>(); filterMap.put("apiPermission", new ApiPermissionFilter()); factoryBean.setFilters(filterMap); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/api/**", "apiPermission[api:read]"); filterChainDefinitionMap.put("/**", "authc"); factoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return factoryBean; } } @RestController public class ApiController { @GetMapping("/api/test") @ApiPermission("api:read") public String test() { return "Hello, World!"; } } ``` 在上述代码中,我们定义了一个 `ApiPermissionFilter`,用于判断用户是否具有访问开放接口的权限。然后,在 `ShiroFilterFactoryBean` 中添加了该过滤器,并在 `filterChainDefinitionMap` 中使用 `apiPermission[api:read]` 来控制 `/api/**` 下的所有请求的访问权限。 最后,在 `ApiController` 中使用 `@ApiPermission("api:read")` 注解来标识需要具有 `api:read` 权限才能访问的接口。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值