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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013219624/article/details/83589863

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分支

猜你喜欢

转载自blog.csdn.net/u013219624/article/details/83589863