Spring中自定义权限注解
实现功能:
在方法上加上注解@PreAuth(“xxx”), 根据xxx不同内容,可以判断当前请求是否有访问该接口的权限,如果有则放行,反之则返回无权限.
其中@PreAuth(“xxx”)中 “xxx” 可以填写角色或者资源等来校验(xxx是个表达式,解析后,然后调用不同的方法去执行返回布尔值)
1. 自定义注解
import java.lang.annotation.*;
/**
* 权限注解 用于检查权限 规定访问权限
*
* @author Chill
* @example @PreAuth("#userVO.id<10")
* @example @PreAuth("hasRole(#test, #test1)")
* @example @PreAuth("hasPermission(#test) and @PreAuth.hasPermission(#test)")
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface PreAuth {
String value();
}
2. 编写处理@PreAuth(“xxx”)中处理"xxx"的方法
/**
* 权限判断
*
* @author Chill
*/
public class AuthFun {
/**
* 权限校验处理器
*/
private static IPermissionHandler permissionHandler;
private static IPermissionHandler getPermissionHandler() {
if (permissionHandler == null) {
permissionHandler = SpringUtil.getBean(IPermissionHandler.class);
}
return permissionHandler;
}
/**
* 判断角色是否具有接口权限
* 判断登陆用户是否拥有这个接口的权限
* @return {boolean}
*/
public boolean permissionAll() {
return getPermissionHandler().permissionAll();
}
/**
* 判断角色是否具有接口权限
*
* @param permission 权限编号
* @return {boolean}
*/
public boolean hasPermission(String permission) {
return getPermissionHandler().hasPermission(permission);
}
/**
* 放行所有请求
*
* @return {boolean}
*/
public boolean permitAll() {
return true;
}
/**
* 只有超管角色才可访问
*
* @return {boolean}
*/
public boolean denyAll() {
return hasRole(RoleConstant.ADMIN);
}
/**
* 是否已授权
*
* @return {boolean}
*/
public boolean hasAuth() {
return Func.isNotEmpty(AuthUtil.getUser());
}
/**
* 是否有时间授权
*
* @param start 开始时间
* @param end 结束时间
* @return {boolean}
*/
public boolean hasTimeAuth(Integer start, Integer end) {
Integer hour = DateUtil.hour();
return hour >= start && hour <= end;
}
/**
* 判断是否有该角色权限
*
* @param role 单角色
* @return {boolean}
*/
public boolean hasRole(String role) {
return hasAnyRole(role);
}
/**
* 判断是否具有所有角色权限
*
* @param role 角色集合
* @return {boolean}
*/
public boolean hasAllRole(String... role) {
for (String r : role) {
if (!hasRole(r)) {
return false;
}
}
return true;
}
/**
* 判断是否有该角色权限
*
* @param role 角色集合
* @return {boolean}
*/
public boolean hasAnyRole(String... role) {
BladeUser user = AuthUtil.getUser();
if (user == null) {
return false;
}
String userRole = user.getRoleName();
if (StringUtil.isBlank(userRole)) {
return false;
}
String[] roles = Func.toStrArray(userRole);
for (String r : role) {
if (CollectionUtil.contains(roles, r)) {
return true;
}
}
return false;
}
}
3. 自定义注解
配置切面 继承Spring ApplicationContextAware
/**
* AOP 鉴权
*解析自定义的注解@PreAuth
* @author Chill
*/
@Aspect
public class AuthAspect implements ApplicationContextAware {
/**
* 表达式处理
*/
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
/**
* 切 方法 和 类上的 @PreAuth 注解
*扫描方法上的 @annotation
*扫描类上 @within
* @param point 切点
* @return Object
* @throws Throwable 没有权限的异常
*/
@Around(
"@annotation(org.springblade.core.secure.annotation.PreAuth) || " +
"@within(org.springblade.core.secure.annotation.PreAuth)"
)
public Object preAuth(ProceedingJoinPoint point) throws Throwable {
if (handleAuth(point)) {
return point.proceed();
}
throw new SecureException(ResultCode.UN_AUTHORIZED);
}
/**
* 处理权限
*
* @param point 切点
*/
private boolean handleAuth(ProceedingJoinPoint point) {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
// 读取权限注解,优先方法上,没有则读取类
PreAuth preAuth = ClassUtil.getAnnotation(method, PreAuth.class);
// 判断表达式
String condition = preAuth.value();
if (StringUtil.isNotBlank(condition)) {
Expression expression = EXPRESSION_PARSER.parseExpression(condition);
// 方法参数值
Object[] args = point.getArgs();
StandardEvaluationContext context = getEvaluationContext(method, args);
return expression.getValue(context, Boolean.class);
}
return false;
}
/**
* 获取方法上的参数
*
* @param method 方法
* @param args 变量
* @return {SimpleEvaluationContext}
*/
private StandardEvaluationContext getEvaluationContext(Method method, Object[] args) {
// 初始化Sp el表达式上下文,并设置 AuthFun
StandardEvaluationContext context = new StandardEvaluationContext(new AuthFun());
// 设置表达式支持spring bean
context.setBeanResolver(new BeanFactoryResolver(applicationContext));
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = ClassUtil.getMethodParameter(method, i);
// 设置方法 参数名和值 为sp el变量
context.setVariable(methodParam.getParameterName(), args[i]);
}
return context;
}
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
4. 使用示例
只有拥有administrator角色或者admin角色的人才能访问该接口
"hasAnyRole(‘administrator’, ‘admin’)"解析后会调用AuthFun.hasAnyRole(String … role)方法来解析该注解
可以自定义AuthFun中的方法来来拓展注解的功能
@GetMapping("/list")
@PreAuth("hasAnyRole('administrator', 'admin')")
public R<List<MenuVO>> list(@ApiIgnore @RequestParam Map<String, Object> menu) {
List<Menu> list = menuService.list(Condition.getQueryWrapper(menu, Menu.class).lambda().orderByAsc(Menu::getSort));
return R.data(MenuWrapper.build().listNodeVO(list));
}
在类上使用
只有拥有administrator角色或者admin角色的人才能访问该Controller的接口
@PreAuth(RoleConstant.HAS_ROLE_ADMIN)
public class AuthClientController extends BladeController {
}