版权声明:本文为博主原创文章,未经博主允许不得转载。 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分支