springMVC springBoot shiro 自定义shiro授权注解,自定义授权解析器

  shiro支持注解式的授权控制,共有5个:

  • @RequiresAuthentication
    当前 Subject 已经通过 login 进行了身份验证;即 Subject.isAuthenticated() 返回 true。
  • @RequiresUser
    当前 Subject 已经身份验证或者通过记住我登录的。
  • @RequiresGuest
    当前 Subject 没有身份验证或通过记住我登录过,即是游客身份。
  • @RequiresRoles
    当前 Subject 需要的角色。
  • @RequiresPermissions
    当前 Subject 需要的权限
    但这些不满足当前的一些需求。父controller有方法权限的通用规则(CRUD),子类规定具体业务(的CRUD),即类似**@RequestMapping**的拼接。查看shiro源码看看他是如何实现注解解析的,决定自行解决
/**
 * 自定义shiro授权注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DefaultPermission {
    String value();
}

/**
 * 自定义MVC注解
 * 用以配合自定义shiro注解使用(@DefaultPermission )
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface MController {
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};
    //排除需要生成的API,加上后该业务将不再生成其中的API。
    DefaultMethod[] excludeMethod() default {};
    //排除需要授权的API,加上后该方法将不再需要授权。
    DefaultMethod[] excludePermission() default {};
}

/**
 * controller的默认对外API方法名
 */
public enum DefaultMethod {
    ALL("all"),
    PAGE("defaultPage"),
    LIST("defaultList"),
    DETAIL("defaultDetail"),
    UPDATE("defaultUpdate"),
    DEL("defaultDel"),
    DELS("defaultDels"),
    DELETE("defaultDelete"),
    DELETES("defaultDeletes");
    private String methodName;
    DefaultMethod(String methodName) {
        this.methodName=methodName;
    }
    public String getMethodName(){
        return this.methodName;
    }
    public static DefaultMethod getEnum(String methodName){
        for(DefaultMethod enm:DefaultMethod.values()){
            if (enm.getMethodName().equals(methodName))return enm;
        }
        return null;
    }
}

public class BaseController<T extends BaseService,M extends BaseModel> {
    @Autowired
    protected T service;
    /**
     * 默认对外接口
     * @param modelMap
     * @param param
     * @return
     */
    @ApiOperation("分页查询")
    @GetMapping("/page")
    @DefaultPermission(":read")
    public Object defaultPage(ModelMap modelMap, @RequestParam Map<String, Object> param) {
    	//业务增强,子类注入的service可重写【beforePage】方法实现查询参数的处理功能
        service.beforePage(param);
        //同样可重写【pageHandler】方法,可对查询后的结果进行增强处理。
        //【getPage】方法传入两个参数,第一个为查询条件;第二个为【Consumer】可遍历处理查询后的结果集
        return setSuccessModelMap(modelMap, service.getPage(param,service.pageHandler()));
    }
    ... ...
}



/**
 * 自定义shiro注解授权处理类
 */
public class DefaultAuthorizationAttributeSourceAdvisor extends AuthorizationAttributeSourceAdvisor{
    private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
            new Class[] {
            		//注入自定义注解
                    DefaultPermission.class,
                    RequiresPermissions.class, RequiresRoles.class,
                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
            };

    public DefaultAuthorizationAttributeSourceAdvisor() {
        setAdvice(new DefaultPermissionAnnotationAopInterceptor());
    }

    @Override
    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());
                return isAuthzAnnotationPresent(m) || isAuthzAnnotationPresent(targetClass);
            } catch (NoSuchMethodException ignored) {

            }
        }

        return false;
    }

    private boolean isAuthzAnnotationPresent(Class<?> targetClazz) {
        for( Class<? extends Annotation> annClass : AUTHZ_ANNOTATION_CLASSES ) {
            Annotation a = AnnotationUtils.findAnnotation(targetClazz, annClass);
            if ( a != null ) {
                return true;
            }
        }
        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;
    }
}

/**
 * 自定义shiro的AOP拦截器
 * 用以注入自定义的授权拦截器
 */
public class DefaultPermissionAnnotationAopInterceptor extends AopAllianceAnnotationsAuthorizingMethodInterceptor {
    public DefaultPermissionAnnotationAopInterceptor() {
		//注入原注解授权拦截器
        super();
        //注入自定义注解授权拦截器
        this.methodInterceptors.add(new DefaultPermissionAnnotationMethodInterceptor());
    }
}


/**
 * 自定义注解授权拦截器
 */
public class DefaultPermissionAnnotationMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
    public DefaultPermissionAnnotationMethodInterceptor() {
    	//注入自定义注解授权处理器
        super(new DefaultPermissionAnnotationHandler());
    }

    public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
        try {
        	//因为需要方法所在的类,就直接在拦截器处理了授权认证了
        	//自定义注解授权处理逻辑
            Annotation typeAnnotation=getAnnotation(mi);
            if (!(typeAnnotation instanceof DefaultPermission)) return;
            MController annotation= mi.getThis().getClass().getAnnotation(MController.class);
            if(annotation!=null){
                String method=mi.getMethod().getName();
                List<DefaultMethod> excludePermission=Arrays.asList(annotation.excludePermission());
                if(excludePermission.contains(DefaultMethod.ALL))return;
                if(!excludePermission.contains(DefaultMethod.getEnum(method))){
                    DefaultPermission a=(DefaultPermission)typeAnnotation;
                    String base= Stream.of(annotation.value()).collect(Collectors.joining()).substring(1);
                    ((DefaultPermissionAnnotationHandler)getHandler()).assertAuthorized(base+a.value());
                }
            }
        }catch(AuthorizationException ae) {
            if (ae.getCause() == null) ae.initCause(new AuthorizationException("Not authorized to invoke method: " + mi.getMethod()));
            throw ae;
        }
    }
}


/**
 * 自定义注解授权处理器
 * 授权逻辑已在拦截器,这里直接复制父类逻辑即可
 */
public class DefaultPermissionAnnotationHandler extends AuthorizingAnnotationHandler{
    public DefaultPermissionAnnotationHandler() {
        super(DefaultPermission.class);
    }

    public void assertAuthorized(String permission) throws AuthorizationException {
        this.getSubject().checkPermission(permission);
    }

    @Override
    public void assertAuthorized(Annotation a) throws AuthorizationException {

    }
}
//shiro启用注解授权,并注入自定义的注解授权处理类
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new DefaultAuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
}

/**
 * 案例controller
 * 会自动生成”/address/page“API,并且访问该API需要”address:read“权限
*/
@MController("/address")
public class AddressController extends BaseController<AddressService, Address> {
}

  这样可在父类创建通用的对外接口,以及接口的基础访问权限。子类只需规定业务类别即可。有自定义的业务可重写相应的增强方法。

你可能感兴趣的:(Spring,springboot,java,springmvc,shiro,spring,boot)