Repository navigation
Expand file tree
/
Copy pathPermissionAspect.java
More file actions
82 lines (72 loc) · 2.78 KB
/
Copy pathPermissionAspect.java
File metadata and controls
82 lines (72 loc) · 2.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
* 示例代码(示意)——为说明设计思路重新编写,与真实生产代码无关,不可直接使用。
*
* 按钮级权限切面。
*/
package sample.security;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Set;
/**
* 按钮级权限切面:声明 + 拦截。
*
* 设计要点:
* 1. 权限集合从缓存读取,避免每次请求都查库;
* 2. 未标注注解的方法直接放行,把切面开销控制在必要范围;
* 3. 超级管理员走旁路,避免被误配的权限锁死自己;
* 4. 角色 / 权限变更时必须主动失效缓存,否则会出现"改了不生效"。
*/
@Aspect
@Component
public class PermissionAspect {
/** 标注在需要按钮级权限校验的方法上。 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiresPermission {
/** 权限标识,例如 deposit:use:submit */
String value();
}
private final PermissionCache permissionCache;
public PermissionAspect(PermissionCache permissionCache) {
this.permissionCache = permissionCache;
}
@Around("@annotation(sample.security.PermissionAspect.RequiresPermission)")
public Object check(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
RequiresPermission anno = signature.getMethod().getAnnotation(RequiresPermission.class);
if (anno == null) {
return point.proceed();
}
CurrentUser user = SecurityContext.current();
if (user == null) {
throw new UnauthorizedException("未登录或会话已过期");
}
if (user.isSuperAdmin()) {
return point.proceed();
}
// 读缓存,不查库
Set<String> granted = permissionCache.permissionsOf(user.getId());
if (granted == null || !granted.contains(anno.value())) {
throw new ForbiddenException("无操作权限:" + anno.value());
}
return point.proceed();
}
}
/*
* 使用方式:
*
* @RequiresPermission("deposit:use:submit")
* public void submitUseApplication(UseApplicationDTO dto) { ... }
*
* 注意:
* - 注解只是入口校验;涉及状态的业务规则(如"已退付的单不允许再变更")
* 仍然要在业务层再校验一次,二者职责不同。
* - 前端隐藏按钮只是体验优化,服务端校验不可省。
*/