Shiro配置跳过权限验证

本文介绍如何在开发和测试环境中通过配置和AOP代理,实现Apache Shiro权限验证的临时关闭,以便快速调试。涉及自定义PermissionAnnotationHandler、AOP拦截器和Spring配置的修改。
摘要由CSDN通过智能技术生成

需求

因为在开发环境,测试环境,有时候需要跳过shiro的权限验证.所以想写个简单的配置跳过shiro的权限验证.
跳过权限验证的原理就是重写**@RequiresPermissions**的实现,然后在配置文件中写一个开关,最后通过Aop注入进去就大功告成.

@RequiresPermissions 处理类

在 org.apache.shiro.authz.aop.PermissionAnnotationHandler 中处理这个注解,这就是我们要
覆写的类.我准备将它替换成log日志.

/**
 * 检查是否有@{@link org.apache.shiro.authz.annotation。注释是
 *声明,如果是,执行权限检查,看是否允许调用<code>Subject</code>继续
 *访问。
 *
 * @since 0.9.0
 */
public class PermissionAnnotationHandler extends AuthorizingAnnotationHandler {
  
   /**
     *确保调用<code>Subject</code>具有注释指定的权限,如果没有,则抛出
     * <code>AuthorizingException</code>表示拒绝访问。
     * 
     */
    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]);
            
        }
    }
}

通过 AopAllianceAnnotationsAuthorizingMethodInterceptor 加入拦截处理,通过Shiro starterConguration配置到将AuthorizationAttributeSourceAdvisor注入到 Spring Bean中.AuthorizationAttributeSourceAdvisor 实现了 StaticMethodMatcherPointcutAdvisor

破解

既然找到了实现的方法,那么注入一个自己实现类就可以跳过shiro的权限了.
但是为了只在测试和开发环境破解,需要使用配置来实现

1.配置跳过shiro开关

首先在spring的配置中加入 spring.profiles.active ,同时配置 xfs.shiro.skipShiro为true.
破解时根据当前运行环境和skipShiro来判断是否要跳过shiro

spring:
  # 环境 dev|test|prod
  profiles:
    active: dev
  application:
    name: system
xfs:
  shiro:
    skipShiro: true #危险配置,跳过shiro权限控制,用于开发和测试环境调试,慎用
2.重写自己的@RequiresPermissions处理方法

在日志上打log,防止严重的生产问题

package cn.lanhi.auth.starter.interceptor;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.aop.PermissionAnnotationHandler;

import java.lang.annotation.Annotation;

/**
 * <p></p>
 *
 * @author : snx cn.shennaixin@gmail.net
 * @date : 2020-06-16 11:39
 */
@Slf4j
public class ShiroPermissionHandler extends PermissionAnnotationHandler {


    public ShiroPermissionHandler() {
        super();
        log.warn("使用了自定义的PermissionHandler,如果是生产环境,使用这个类将会导致权限控制模块失效");
    }

    /**
     * 重写权限认证方法,仅仅打印log,不做拦截处理
     *
     * @param a 注解
     * @throws AuthorizationException 一个不可能抛出的异常
     */
    @Override
    public void assertAuthorized(Annotation a) throws AuthorizationException {
        if (!(a instanceof RequiresPermissions)) return;
        //如果是数组,打印效果不好,使用json序列化
        log.warn("警告!!   跳过了权限:{}", JSON.toJSONString(getAnnotationValue(a)));
    }
}
3.设置注解处理器

在 AnnotationsAuthorizingMethodInterceptor 这个抽象类的实现类中,添加了对注解的拦截器

package org.apache.shiro.spring.security.interceptor;

public class AopAllianceAnnotationsAuthorizingMethodInterceptor
        extends AnnotationsAuthorizingMethodInterceptor implements MethodInterceptor {
	//Shiro拦截器
    public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
        List<AuthorizingAnnotationMethodInterceptor> interceptors =
                new ArrayList<AuthorizingAnnotationMethodInterceptor>(5);
        AnnotationResolver resolver = new SpringAnnotationResolver();
        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);
    	}
    	........
  }

那么破解一下,自己继承一下AopAllianceAnnotationsAuthorizingMethodInterceptor,然后获取自身属性,修改值

package cn.lanhi.auth.starter.shiro;

import com.xfs.auth.starter.interceptor.ShiroPermissionHandler;
import org.apache.shiro.authz.aop.AuthorizingAnnotationMethodInterceptor;
import org.apache.shiro.authz.aop.PermissionAnnotationMethodInterceptor;
import org.apache.shiro.spring.security.interceptor.AopAllianceAnnotationsAuthorizingMethodInterceptor;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>shiro 权限重定义</p>
 *
 * @author : snx cn.shennaixin@gmail.net
 * @date : 2020-06-16 11:34
 */
public class ShiroMethodInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor {
    public ShiroMethodInterceptor() {
        super();
    }

    /**
     * 跳过shiro RequirePremissions 注解验证
     */
    public ShiroMethodInterceptor skipPremissionHandler() {
        List<AuthorizingAnnotationMethodInterceptor> interceptors = this.getMethodInterceptors().stream()
                .filter(authorizingAnnotationMethodInterceptor ->
                        !(authorizingAnnotationMethodInterceptor instanceof PermissionAnnotationMethodInterceptor))
                .collect(Collectors.toList());
        PermissionAnnotationMethodInterceptor interceptor = new PermissionAnnotationMethodInterceptor();
        //设置成自己的注解处理器!
        interceptor.setHandler(new ShiroPermissionHandler());
        interceptors.add(interceptor);
        setMethodInterceptors(interceptors);
        return this;
    }
}
4.重写shiroAop
package org.apache.shiro.spring.config;
/**
 * shiro AOP 
 * @since 1.4.0
 */
@Configuration
public class ShiroAnnotationProcessorConfiguration extends AbstractShiroAnnotationProcessorConfiguration{

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    protected DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return super.defaultAdvisorAutoProxyCreator();
    }

    @Bean
    protected AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        return super.authorizationAttributeSourceAdvisor(securityManager);
    }

}

   --------------------
   --------------------
   --------------------
  这个 AuthorizationAttributeSourceAdvisor 提供了AOP的拦截器实现.接下来我们要覆盖他,
 /**
     * Create a new AuthorizationAttributeSourceAdvisor.
     */
    public AuthorizationAttributeSourceAdvisor() {
        setAdvice(new AopAllianceAnnotationsAuthorizingMethodInterceptor());
    }
   --------------------
   --------------------
   --------------------
5.覆盖Shiro Bean

判断了一下环境,跳过Shiro 权限验证,仅在测试和开发环境生效,且需要开启配置
注意bean要设置成@primary

 /**
     * 跳过Shiro 权限验证,仅在测试和开发环境生效
     *
     * @param securityManager
     * @return
     */
    @Bean("authorizationAttributeSourceAdvisor")
    @Primary
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            SecurityManager securityManager,
            Environment environment,
            @Value("${xfs.shiro.skipShiro:false}") boolean skipShiro) {
        //获取当前运行环境
        String profile = environment.getRequiredProperty("spring.profiles.active");
        //创建要生成的Bean
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        //判断是否可以跳过shiro
        if (skipShiro && ("dev".equals(profile) || "test".equals(profile))) {
        	//运行跳过shiro权限验证方法
            advisor.setAdvice(new ShiroMethodInterceptor().skipPremissionHandler());
        }
        return advisor;
    }

到此为止,就大功告成啦.
转载请注明出处!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值