在Spring框架中,可以使用面向切面编程(AOP)来实现注解鉴权。这通常涉及到定义一个切面(Aspect),该切面会在方法执行前进行拦截,并根据注解value值来决定是否允许执行该方法。
简单思路:
权限标识如:"business:project:list" 字段保存在菜单表,用户表与菜单表关联。如果自定义注解中的参数值@RequiresPermissions("business:project:list") 存在于当前用户所拥有的权限中,则该请求允许访问该方法,否则拒绝。
首先,需要定义一个注解 RequiresPermissions
/**
* 权限认证:必须具有指定权限才能进入该方法
*
* @author digipower
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresPermissions {
/**
* 需要校验的权限码
*/
String[] value() default {};
}
然后,你需要定义一个切面,该切面会拦截带有 @RequiresPermissions 注解的方法
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import com.digipower.component.security.annotation.RequiresLogin;
import com.digipower.component.security.annotation.RequiresPermissions;
import com.digipower.component.security.annotation.RequiresRoles;
import com.digipower.component.security.auth.AuthUtil;
/**
* 基于 Spring Aop 的注解鉴权
*/
@Aspect
@Component
public class AuthAspect {
/** 权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/**
* 构建
*/
public AuthAspect() {}
/**
* 声明AOP签名
*/
@Pointcut(@annotation(com.bibo.test.RequiresPermissions))
public void pointcut() {}
/**
* 环绕切入
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取签名
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 校验 @RequiresPermissions 注解
RequiresPermissions requiresPermissions = signature.getMethod().getAnnotation(RequiresPermissions.class);
if (requiresPermissions != null) {
// 获取当前用户的全部权限,可以从redis、Spring Security安全上下文等
Set<String> permissionSet = getXX();
for (String permission : requiresPermissions.value()) {
if (!hasPermission(permissionSet, permission)) {
throw new NotPermissionException(permission);
}
}
}
try {
// 执行原有逻辑
Object obj = joinPoint.proceed();
return obj;
} catch (Throwable e) {
throw e;
}
}
/**
* 判断是否包含权限
*
* @param authorities
* 权限列表
* @param permission
* 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermission(Collection<String> authorities, String permission) {
return authorities.stream().filter()
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
}
}
最后,在你的服务或控制器中,你可以使用
@RequiresPermissions注解来标记需要进行鉴权的方法
/**
* 查询列表
*/
@RequiresPermissions("business:project:list")
@GetMapping("/list")
public PageDataResult list(BizInfo bizInfo) {
...
return ..
}
这样,每当 list 方法被调用时,AuthAspect 会进行鉴权检查。如果鉴权失败,将抛出异常或返回错误信息,阻止方法的执行。