需求
因為在開發環境,測試環境,有時候需要跳過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;
}
到此為止,就大功告成啦.