Shiro配置跳过权限验证
需求
因为在开发环境,测试环境,有时候需要跳过shiro的权限验证.所以想写个简单的配置跳过shiro的权限验证.
跳过权限验证的原理就是重写**@RequiresPermissions**的实现,然后在配置文件中写一个开关,最后通过Aop注入进去就大功告成.
@RequiresPermissions 处理类
在 org.apache.shiro.authz.aop.PermissionAnnotationHandler 中处理这个注解,这就是我们要
覆写的类.我准备将它替换成log日志.
/**
* 检查是否有@{@link org.apache.shiro.authz.annotation。注释是
*声明,如果是,执行权限检查,看是否允许调用Subject
继续
*访问。
*
* @since 0.9.0
*/
public class PermissionAnnotationHandler extends AuthorizingAnnotationHandler {
/**
*确保调用Subject
具有注释指定的权限,如果没有,则抛出
* AuthorizingException
表示拒绝访问。
*
*/
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 starter 的 Conguration配置到将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;
/**
*
*
* @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;
/**
*
shiro 权限重定义
*
* @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;
}
到此为止,就大功告成啦.