java-安全层 - 权限控制
·
安全层 - 权限控制完全指南 🔒
一、Java EE 标准安全注解(JSR-250)
1. @PermitAll(允许所有人)
/**
* 允许所有用户访问,包括未登录用户
* 通常用于:公开接口、登录接口、注册接口等
*/
@PermitAll
@GetMapping("/public/info")
public Result getPublicInfo() {
return Result.success("公开信息");
}
// 类级别使用(所有方法都允许访问)
@RestController
@PermitAll
public class PublicController {
// 所有方法都是公开的
}
2. @DenyAll(拒绝所有人)
/**
* 拒绝所有用户访问,包括管理员
* 通常用于:废弃的接口、维护中的功能
*/
@DenyAll
@GetMapping("/deprecated")
public Result deprecatedApi() {
// 任何人调用都会返回 403 Forbidden
}
3. @RolesAllowed(角色限制)
/**
* 只允许指定角色访问
* 注意:这里的角色名不需要 ROLE_ 前缀
*/
// 1. 单个角色
@RolesAllowed("ADMIN")
@GetMapping("/admin/users")
public List<User> getUsers() {
// 只有 ADMIN 角色可以访问
}
// 2. 多个角色(满足其中之一即可)
@RolesAllowed({"ADMIN", "MANAGER"})
@GetMapping("/management/reports")
public Report getReport() {
// ADMIN 或 MANAGER 都可以访问
}
// 3. 类级别使用
@RestController
@RolesAllowed("ADMIN")
public class AdminController {
// 所有方法都需要 ADMIN 角色
// 方法级别可以覆盖类级别
@RolesAllowed({"ADMIN", "MANAGER"})
@GetMapping("/reports")
public Report getReport() { }
}
4. 启用 JSR-250 注解
@Configuration
@EnableGlobalMethodSecurity(
jsr250Enabled = true // 启用 JSR-250 注解
)
public class SecurityConfig {
// 配置内容...
}
二、Spring Security 安全注解
1. @Secured(简单角色控制)
/**
* Spring Security 的角色注解
* 注意:必须使用 ROLE_ 前缀
*/
// 1. 单个角色
@Secured("ROLE_ADMIN")
@GetMapping("/admin/dashboard")
public Dashboard getDashboard() { }
// 2. 多个角色
@Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
@GetMapping("/management/data")
public Data getData() { }
// 3. 启用配置
@Configuration
@EnableGlobalMethodSecurity(
securedEnabled = true // 启用 @Secured 注解
)
public class SecurityConfig { }
2. @PreAuthorize(方法执行前鉴权)⭐
/**
* 最强大灵活的权限注解
* 支持 SpEL 表达式
* 在方法执行前进行权限检查
*/
// 1. 基础角色检查
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public List<User> getUsers() { }
// 2. 多角色检查(OR)
@PreAuthorize("hasRole('ADMIN') or hasRole('MANAGER')")
@GetMapping("/data")
public Data getData() { }
// 3. 多角色检查(AND)
@PreAuthorize("hasRole('ADMIN') and hasRole('SUPER_USER')")
@GetMapping("/sensitive")
public Data getSensitiveData() { }
// 4. 权限检查(不是角色)
@PreAuthorize("hasAuthority('USER_READ')")
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { }
// 5. 多权限检查
@PreAuthorize("hasAnyAuthority('USER_READ', 'USER_WRITE')")
@GetMapping("/users")
public List<User> getUsers() { }
// 6. 检查所有权限
@PreAuthorize("hasAuthority('USER_READ') and hasAuthority('USER_WRITE')")
@PostMapping("/users")
public User createUser(@RequestBody User user) { }
// 7. 访问方法参数
@PreAuthorize("#userId == authentication.principal.id")
@GetMapping("/users/{userId}/profile")
public Profile getProfile(@PathVariable Long userId) {
// 只能查看自己的资料
}
// 8. 复杂表达式
@PreAuthorize("hasRole('ADMIN') or (#userId == authentication.principal.id and hasRole('USER'))")
@PutMapping("/users/{userId}")
public User updateUser(@PathVariable Long userId, @RequestBody User user) {
// ADMIN 可以修改任何人,普通用户只能修改自己
}
// 9. 自定义表达式
@PreAuthorize("@permissionService.canAccess(#resourceId)")
@GetMapping("/resources/{resourceId}")
public Resource getResource(@PathVariable Long resourceId) { }
// 10. 匿名访问
@PreAuthorize("isAnonymous()")
@GetMapping("/login-page")
public String loginPage() { }
// 11. 已认证用户
@PreAuthorize("isAuthenticated()")
@GetMapping("/profile")
public Profile getProfile() { }
// 12. 完全认证(非 remember-me)
@PreAuthorize("isFullyAuthenticated()")
@PostMapping("/transfer")
public Result transfer(@RequestBody TransferRequest request) { }
// 13. IP 地址限制
@PreAuthorize("hasIpAddress('192.168.1.0/24')")
@GetMapping("/internal")
public Data getInternalData() { }
3. @PostAuthorize(方法执行后鉴权)
/**
* 方法执行后进行权限检查
* 可以访问返回值
* 如果检查失败,抛出异常
*/
// 1. 检查返回对象的属性
@PostAuthorize("returnObject.owner == authentication.name")
@GetMapping("/documents/{id}")
public Document getDocument(@PathVariable Long id) {
// 只能查看自己的文档
// 如果返回的文档不属于当前用户,抛出异常
}
// 2. 检查返回对象是否为空
@PostAuthorize("returnObject != null")
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { }
// 3. 复杂检查
@PostAuthorize("returnObject.status == 'PUBLISHED' or hasRole('ADMIN')")
@GetMapping("/articles/{id}")
public Article getArticle(@PathVariable Long id) {
// 文章必须是已发布,或者用户是管理员
}
// 4. 集合过滤(配合 @PostFilter 使用更好)
@PostAuthorize("returnObject.createdBy == authentication.name or hasRole('ADMIN')")
@GetMapping("/my-orders/{id}")
public Order getOrder(@PathVariable Long id) { }
4. @PreFilter(过滤方法参数)
/**
* 在方法执行前过滤参数集合
* 只能用于集合类型的参数
*/
// 1. 过滤列表参数
@PreFilter("filterObject.owner == authentication.name")
@PostMapping("/orders/batch")
public List<Order> processOrders(@RequestBody List<Order> orders) {
// 只处理属于当前用户的订单
// 其他订单会被自动过滤掉
}
// 2. 指定过滤目标(多个集合参数时)
@PreFilter(value = "filterObject.status == 'ACTIVE'",
filterTarget = "users")
@PostMapping("/process")
public Result process(@RequestBody List<User> users,
@RequestBody List<Order> orders) {
// 只过滤 users 集合
}
5. @PostFilter(过滤返回结果)
/**
* 在方法执行后过滤返回的集合
* 自动移除不符合条件的元素
*/
// 1. 过滤返回列表
@PostFilter("filterObject.owner == authentication.name or hasRole('ADMIN')")
@GetMapping("/orders")
public List<Order> getOrders() {
// 返回所有订单,但自动过滤掉不属于当前用户的订单
// 除非用户是 ADMIN
}
// 2. 复杂过滤条件
@PostFilter("filterObject.status == 'PUBLISHED' or filterObject.author == authentication.name")
@GetMapping("/articles")
public List<Article> getArticles() {
// 只返回已发布的文章或自己创建的文章
}
// 3. 使用自定义方法
@PostFilter("@permissionService.canView(filterObject, authentication)")
@GetMapping("/documents")
public List<Document> getDocuments() { }
6. 启用 Spring Security 注解
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true, // 启用 @PreAuthorize, @PostAuthorize
securedEnabled = true, // 启用 @Secured
jsr250Enabled = true // 启用 @RolesAllowed, @PermitAll, @DenyAll
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 配置内容...
}
三、SpEL 表达式完整列表
1. 内置表达式
// 角色检查
hasRole('ROLE_NAME') // 是否有指定角色
hasAnyRole('ROLE1', 'ROLE2') // 是否有任意角色
hasRole('ADMIN') and hasRole('USER') // 同时拥有多个角色
// 权限检查
hasAuthority('PERMISSION') // 是否有指定权限
hasAnyAuthority('P1', 'P2') // 是否有任意权限
hasAuthority('READ') and hasAuthority('WRITE') // 同时拥有多个权限
// 认证状态
isAuthenticated() // 是否已认证
isAnonymous() // 是否匿名
isFullyAuthenticated() // 是否完全认证(非 remember-me)
isRememberMe() // 是否通过 remember-me 认证
// IP 限制
hasIpAddress('192.168.1.1') // 指定 IP
hasIpAddress('192.168.1.0/24') // IP 段
// 组合使用
permitAll // 允许所有人
denyAll // 拒绝所有人
2. 访问对象属性
// 访问方法参数
@PreAuthorize("#userId == authentication.principal.id")
public User getUser(@PathVariable Long userId) { }
// 访问返回值
@PostAuthorize("returnObject.owner == authentication.name")
public Document getDocument(@PathVariable Long id) { }
// 访问认证对象
authentication.principal // 当前用户对象
authentication.name // 用户名
authentication.authorities // 权限列表
authentication.credentials // 凭证
authentication.details // 详细信息
3. 自定义表达式
// 调用 Spring Bean 的方法
@PreAuthorize("@permissionService.canAccess(#resourceId)")
public Resource getResource(@PathVariable Long resourceId) { }
@PreAuthorize("@authService.hasPermission(authentication, #id, 'READ')")
public Data getData(@PathVariable Long id) { }
四、自定义权限控制
1. 自定义权限服务
@Service("permissionService")
public class PermissionService {
/**
* 检查用户是否可以访问资源
*/
public boolean canAccess(Long resourceId) {
// 获取当前用户
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
// 自定义业务逻辑
// 例如:检查数据库中的权限记录
return checkUserResourcePermission(username, resourceId);
}
/**
* 检查用户是否可以编辑资源
*/
public boolean canEdit(Long resourceId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
// 检查是否是资源所有者
Resource resource = resourceRepository.findById(resourceId);
if (resource.getOwner().equals(username)) {
return true;
}
// 或者检查是否有编辑权限
return auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("RESOURCE_EDIT"));
}
/**
* 检查用户是否可以查看对象
*/
public boolean canView(Object obj, Authentication authentication) {
if (obj instanceof Document) {
Document doc = (Document) obj;
String username = authentication.getName();
// 文档是公开的,或者是文档所有者,或者是管理员
return doc.isPublic()
|| doc.getAuthor().equals(username)
|| authentication.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
}
return false;
}
/**
* 检查用户是否拥有指定权限
*/
public boolean hasPermission(Authentication auth, Long targetId, String permission) {
String username = auth.getName();
// 从数据库查询用户对该资源的权限
List<String> permissions = permissionRepository
.findByUsernameAndResourceId(username, targetId);
return permissions.contains(permission);
}
}
2. 使用自定义权限服务
@RestController
@RequestMapping("/api/resources")
public class ResourceController {
// 1. 简单权限检查
@PreAuthorize("@permissionService.canAccess(#id)")
@GetMapping("/{id}")
public Resource getResource(@PathVariable Long id) {
return resourceService.findById(id);
}
// 2. 编辑权限检查
@PreAuthorize("@permissionService.canEdit(#id)")
@PutMapping("/{id}")
public Resource updateResource(
@PathVariable Long id,
@RequestBody Resource resource) {
return resourceService.update(id, resource);
}
// 3. 复杂权限检查
@PreAuthorize("@permissionService.hasPermission(authentication, #id, 'DELETE')")
@DeleteMapping("/{id}")
public void deleteResource(@PathVariable Long id) {
resourceService.delete(id);
}
// 4. 结合内置表达式
@PreAuthorize("hasRole('ADMIN') or @permissionService.canEdit(#id)")
@PutMapping("/{id}/publish")
public Resource publishResource(@PathVariable Long id) {
return resourceService.publish(id);
}
}
3. 自定义权限评估器
/**
* 实现 PermissionEvaluator 接口
*/
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Autowired
private PermissionRepository permissionRepository;
/**
* 检查权限
* @param authentication 认证对象
* @param targetDomainObject 目标对象
* @param permission 权限
*/
@Override
public boolean hasPermission(Authentication authentication,
Object targetDomainObject,
Object permission) {
if (authentication == null || targetDomainObject == null || permission == null) {
return false;
}
String username = authentication.getName();
String targetType = targetDomainObject.getClass().getSimpleName();
return hasPrivilege(username, targetType, permission.toString());
}
/**
* 通过 ID 检查权限
*/
@Override
public boolean hasPermission(Authentication authentication,
Serializable targetId,
String targetType,
Object permission) {
if (authentication == null || targetId == null || permission == null) {
return false;
}
String username = authentication.getName();
return hasPrivilege(username, targetType, permission.toString(), targetId);
}
private boolean hasPrivilege(String username, String targetType,
String permission) {
// 查询数据库或缓存
return permissionRepository.userHasPermission(username, targetType, permission);
}
private boolean hasPrivilege(String username, String targetType,
String permission, Serializable targetId) {
// 查询数据库或缓存
return permissionRepository.userHasPermissionOnResource(
username, targetType, targetId, permission);
}
}
/**
* 配置 PermissionEvaluator
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Autowired
private CustomPermissionEvaluator permissionEvaluator;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(permissionEvaluator);
return expressionHandler;
}
}
4. 使用自定义权限评估器
@RestController
@RequestMapping("/api/documents")
public class DocumentController {
// 1. 检查对象权限
@PreAuthorize("hasPermission(#document, 'WRITE')")
@PutMapping
public Document updateDocument(@RequestBody Document document) {
return documentService.update(document);
}
// 2. 通过 ID 检查权限
@PreAuthorize("hasPermission(#id, 'Document', 'DELETE')")
@DeleteMapping("/{id}")
public void deleteDocument(@PathVariable Long id) {
documentService.delete(id);
}
// 3. 结合其他表达式
@PreAuthorize("hasRole('ADMIN') or hasPermission(#id, 'Document', 'READ')")
@GetMapping("/{id}")
public Document getDocument(@PathVariable Long id) {
return documentService.findById(id);
}
}
五、编程式权限控制
1. 在代码中检查权限
@Service
public class UserService {
/**
* 方式 1: 使用 SecurityContextHolder
*/
public void checkPermission() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// 检查角色
boolean isAdmin = auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
if (!isAdmin) {
throw new AccessDeniedException("需要管理员权限");
}
}
/**
* 方式 2: 注入 Authentication
*/
public void processData(Authentication authentication) {
String username = authentication.getName();
boolean hasPermission = authentication.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("DATA_PROCESS"));
if (!hasPermission) {
throw new AccessDeniedException("没有数据处理权限");
}
}
/**
* 方式 3: 使用 @AuthenticationPrincipal
*/
@GetMapping("/profile")
public Profile getProfile(
@AuthenticationPrincipal UserDetails userDetails) {
String username = userDetails.getUsername();
// 处理逻辑...
}
/**
* 方式 4: 使用 @AuthenticationPrincipal 自定义用户对象
*/
@GetMapping("/info")
public UserInfo getInfo(
@AuthenticationPrincipal CustomUser user) {
Long userId = user.getId();
String email = user.getEmail();
// 处理逻辑...
}
}
2. 在 Service 层检查权限
@Service
public class DocumentService {
@Autowired
private SecurityContextHolder securityContextHolder;
/**
* 检查文档访问权限
*/
public Document getDocument(Long id) {
Document document = documentRepository.findById(id)
.orElseThrow(() -> new NotFoundException("文档不存在"));
// 获取当前用户
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String currentUsername = auth.getName();
// 检查权限
if (!document.getAuthor().equals(currentUsername)
&& !hasRole(auth, "ROLE_ADMIN")) {
throw new AccessDeniedException("无权访问此文档");
}
return document;
}
/**
* 检查是否有指定角色
*/
private boolean hasRole(Authentication auth, String role) {
return auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals(role));
}
/**
* 检查是否有指定权限
*/
private boolean hasAuthority(Authentication auth, String authority) {
return auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals(authority));
}
}
六、完整实战案例
案例 1: 多级权限系统
/**
* 权限系统设计:
* 1. 角色:ADMIN, MANAGER, USER
* 2. 权限:READ, WRITE, DELETE, EXECUTE
* 3. 资源:Document, Project, Report
*/
@RestController
@RequestMapping("/api/documents")
public class DocumentController {
// 1. 公开接口
@PermitAll
@GetMapping("/public")
public List<Document> getPublicDocuments() {
return documentService.findPublicDocuments();
}
// 2. 需要登录
@PreAuthorize("isAuthenticated()")
@GetMapping("/my")
public List<Document> getMyDocuments() {
return documentService.findByCurrentUser();
}
// 3. 需要特定角色
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/all")
public List<Document> getAllDocuments() {
return documentService.findAll();
}
// 4. 需要特定权限
@PreAuthorize("hasAuthority('DOCUMENT_READ')")
@GetMapping("/{id}")
public Document getDocument(@PathVariable Long id) {
return documentService.findById(id);
}
// 5. 只能访问自己的资源
@PreAuthorize("#userId == authentication.principal.id")
@GetMapping("/user/{userId}")
public List<Document> getUserDocuments(@PathVariable Long userId) {
return documentService.findByUserId(userId);
}
// 6. 管理员或资源所有者
@PreAuthorize("hasRole('ADMIN') or @documentService.isOwner(#id, authentication.name)")
@PutMapping("/{id}")
public Document updateDocument(
@PathVariable Long id,
@RequestBody Document document) {
return documentService.update(id, document);
}
// 7. 复杂权限组合
@PreAuthorize("(hasRole('MANAGER') and hasAuthority('DOCUMENT_DELETE')) or hasRole('ADMIN')")
@DeleteMapping("/{id}")
public void deleteDocument(@PathVariable Long id) {
documentService.delete(id);
}
// 8. 使用自定义权限检查
@PreAuthorize("@permissionService.canAccess(#id, 'DOCUMENT', 'WRITE')")
@PostMapping("/{id}/publish")
public Document publishDocument(@PathVariable Long id) {
return documentService.publish(id);
}
// 9. 过滤返回结果
@PostFilter("hasRole('ADMIN') or filterObject.author == authentication.name")
@GetMapping("/draft")
public List<Document> getDraftDocuments() {
return documentService.findDraftDocuments();
}
// 10. 方法执行后检查
@PostAuthorize("returnObject.isPublic or returnObject.author == authentication.name or hasRole('ADMIN')")
@GetMapping("/{id}/detail")
public Document getDocumentDetail(@PathVariable Long id) {
return documentService.findByIdWithDetails(id);
}
}
案例 2: 数据权限控制
/**
* 数据权限服务
*/
@Service
public class DataPermissionService {
@Autowired
private UserRoleRepository userRoleRepository;
@Autowired
private DepartmentRepository departmentRepository;
/**
* 检查用户是否可以访问部门数据
*/
public boolean canAccessDepartment(Long deptId) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
// 管理员可以访问所有部门
if (hasRole(auth, "ROLE_ADMIN")) {
return true;
}
// 检查用户所在部门
User user = userRepository.findByUsername(username);
Long userDeptId = user.getDepartmentId();
// 可以访问自己部门
if (userDeptId.equals(deptId)) {
return true;
}
// 经理可以访问下级部门
if (hasRole(auth, "ROLE_MANAGER")) {
return isSubDepartment(userDeptId, deptId);
}
return false;
}
/**
* 获取用户可访问的部门列表
*/
public List<Long> getAccessibleDepartments() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();
// 管理员可以访问所有部门
if (hasRole(auth, "ROLE_ADMIN")) {
return departmentRepository.findAll().stream()
.map(Department::getId)
.collect(Collectors.toList());
}
User user = userRepository.findByUsername(username);
Long userDeptId = user.getDepartmentId();
// 经理可以访问自己部门及下级部门
if (hasRole(auth, "ROLE_MANAGER")) {
return departmentRepository.findByParentIdOrId(userDeptId, userDeptId)
.stream()
.map(Department::getId)
.collect(Collectors.toList());
}
// 普通用户只能访问自己部门
return Collections.singletonList(userDeptId);
}
/**
* 过滤数据(在查询时自动添加数据权限条件)
*/
public <T> List<T> filterByDataPermission(List<T> dataList) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// 管理员不过滤
if (hasRole(auth, "ROLE_ADMIN")) {
return dataList;
}
List<Long> accessibleDepts = getAccessibleDepartments();
return dataList.stream()
.filter(data -> {
if (data instanceof DepartmentAware) {
DepartmentAware deptData = (DepartmentAware) data;
return accessibleDepts.contains(deptData.getDepartmentId());
}
return true;
})
.collect(Collectors.toList());
}
private boolean hasRole(Authentication auth, String role) {
return auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals(role));
}
private boolean isSubDepartment(Long parentId, Long childId) {
// 递归检查是否是下级部门
Department dept = departmentRepository.findById(childId).orElse(null);
if (dept == null) {
return false;
}
if (dept.getParentId() == null) {
return false;
}
if (dept.getParentId().equals(parentId)) {
return true;
}
return isSubDepartment(parentId, dept.getParentId());
}
}
/**
* 使用数据权限
*/
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private DataPermissionService dataPermissionService;
// 只能查看有权限的部门用户
@PreAuthorize("@dataPermissionService.canAccessDepartment(#deptId)")
@GetMapping("/department/{deptId}")
public List<User> getUsersByDepartment(@PathVariable Long deptId) {
return userService.findByDepartmentId(deptId);
}
// 自动过滤返回结果
@GetMapping("/all")
public List<User> getAllUsers() {
List<User> allUsers = userService.findAll();
return dataPermissionService.filterByDataPermission(allUsers);
}
}
案例 3: 动态权限配置
/**
* 权限配置实体
*/
@Entity
@Table(name = "sys_permission")
public class Permission {
@Id
private Long id;
private String resourceType; // 资源类型:URL, BUTTON, DATA
private String resourceCode; // 资源编码
private String permissionCode; // 权限编码
private String expression; // 权限表达式
}
/**
* 动态权限服务
*/
@Service
public class DynamicPermissionService {
@Autowired
private PermissionRepository permissionRepository;
/**
* 检查动态权限
*/
public boolean checkPermission(String resourceCode, String permissionCode) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// 查询权限配置
Permission permission = permissionRepository
.findByResourceCodeAndPermissionCode(resourceCode, permissionCode);
if (permission == null) {
return false;
}
// 评估权限表达式
return evaluateExpression(permission.getExpression(), auth);
}
/**
* 评估权限表达式
*/
private boolean evaluateExpression(String expression, Authentication auth) {
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
// 设置变量
context.setVariable("authentication", auth);
context.setVariable("username", auth.getName());
context.setVariable("authorities", auth.getAuthorities());
// 解析并评估表达式
Expression exp = parser.parseExpression(expression);
return Boolean.TRUE.equals(exp.getValue(context, Boolean.class));
}
/**
* 获取用户的所有权限
*/
public Set<String> getUserPermissions(String username) {
// 从数据库查询用户权限
return permissionRepository.findByUsername(username)
.stream()
.map(Permission::getPermissionCode)
.collect(Collectors.toSet());
}
}
/**
* 使用动态权限
*/
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private DynamicPermissionService dynamicPermissionService;
@GetMapping("/{id}")
public Product getProduct(@PathVariable Long id) {
// 动态检查权限
if (!dynamicPermissionService.checkPermission("PRODUCT", "READ")) {
throw new AccessDeniedException("无权查看产品");
}
return productService.findById(id);
}
@PostMapping
public Product createProduct(@RequestBody Product product) {
if (!dynamicPermissionService.checkPermission("PRODUCT", "CREATE")) {
throw new AccessDeniedException("无权创建产品");
}
return productService.create(product);
}
}
七、最佳实践建议 ✨
1. 注解选择建议
// ✅ 推荐:使用 @PreAuthorize(最灵活)
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
// ✅ 推荐:简单场景使用 @RolesAllowed
@RolesAllowed("ADMIN")
// ⚠️ 可用:@Secured(需要 ROLE_ 前缀,不够灵活)
@Secured("ROLE_ADMIN")
// ✅ 推荐:公开接口使用 @PermitAll
@PermitAll
2. 权限粒度设计
// 粗粒度(角色级别)
@PreAuthorize("hasRole('ADMIN')")
// 中粒度(功能级别)
@PreAuthorize("hasAuthority('USER_MANAGEMENT')")
// 细粒度(操作级别)
@PreAuthorize("hasAuthority('USER_READ')")
// 数据级别(行级权限)
@PreAuthorize("@permissionService.canAccessData(#id)")
3. 性能优化
// ❌ 避免:在循环中检查权限
for (Document doc : documents) {
if (hasPermission(doc)) {
// 处理...
}
}
// ✅ 推荐:批量检查或使用 @PostFilter
@PostFilter("@permissionService.canView(filterObject)")
public List<Document> getDocuments() {
return documentService.findAll();
}
4. 错误处理
@ControllerAdvice
public class SecurityExceptionHandler {
// 处理访问拒绝异常
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponse> handleAccessDenied(
AccessDeniedException ex) {
return ResponseEntity
.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse("无权访问", ex.getMessage()));
}
// 处理认证异常
@ExceptionHandler(AuthenticationException.class)
public ResponseEntity<ErrorResponse> handleAuthentication(
AuthenticationException ex) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(new ErrorResponse("认证失败", ex.getMessage()));
}
}
八、总结对比表
| 注解 | 来源 | 灵活性 | 使用场景 | 推荐指数 |
|---|---|---|---|---|
| @PreAuthorize | Spring Security | ⭐⭐⭐⭐⭐ | 复杂权限控制 | ⭐⭐⭐⭐⭐ |
| @PostAuthorize | Spring Security | ⭐⭐⭐⭐ | 返回值检查 | ⭐⭐⭐⭐ |
| @PreFilter | Spring Security | ⭐⭐⭐ | 参数过滤 | ⭐⭐⭐ |
| @PostFilter | Spring Security | ⭐⭐⭐⭐ | 结果过滤 | ⭐⭐⭐⭐ |
| @RolesAllowed | JSR-250 | ⭐⭐ | 简单角色控制 | ⭐⭐⭐⭐ |
| @PermitAll | JSR-250 | ⭐ | 公开接口 | ⭐⭐⭐⭐⭐ |
| @DenyAll | JSR-250 | ⭐ | 禁止访问 | ⭐⭐ |
| @Secured | Spring Security | ⭐⭐ | 简单角色控制 | ⭐⭐ |
更多推荐


所有评论(0)