Spring Security中@PreAuthorize的其他校验方式:方法内如何实现?
在方法内部实现Spring Security权限校验的几种方式
你当前通过@PreAuthorize注解实现权限校验的写法如下:
@PreAuthorize("hasRole('ADMIN') or hasAuthority('GET_BOOK')") @PostMapping("/find") public Pagination<BookDTO> find(@Valid @RequestBody QueryCriteria queryCriteria) { return bookService.find(queryCriteria); }
如果需要在方法内部完成权限校验,以下是几种可行方案:
方案一:通过SecurityContextHolder手动校验权限
直接获取当前用户的认证信息,遍历权限集合判断是否符合要求:
@PostMapping("/find") public Pagination<BookDTO> find(@Valid @RequestBody QueryCriteria queryCriteria) { // 获取当前用户的认证信息 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 校验是否拥有ADMIN角色(注意Spring Security中hasRole对应权限前缀为ROLE_)或GET_BOOK权限 boolean hasPermission = authentication.getAuthorities().stream() .anyMatch(auth -> "ROLE_ADMIN".equals(auth.getAuthority()) || "GET_BOOK".equals(auth.getAuthority())); if (!hasPermission) { throw new AccessDeniedException("无权限访问该接口"); } return bookService.find(queryCriteria); }
方案二:使用AuthorizationManager(Spring Security 6+推荐)
借助框架提供的AuthorizationManager组件,更规范地实现权限校验:
@Autowired private AuthorizationManager<AuthorizationContext> authorizationManager; @PostMapping("/find") public Pagination<BookDTO> find(@Valid @RequestBody QueryCriteria queryCriteria) { // 构建权限校验规则:匹配ROLE_ADMIN或GET_BOOK权限 AuthorizationDecision decision = authorizationManager.check( () -> SecurityContextHolder.getContext().getAuthentication(), AuthorityAuthorizationManager.hasAnyAuthority("ROLE_ADMIN", "GET_BOOK") ); if (!decision.isGranted()) { throw new AccessDeniedException("无权限访问该接口"); } return bookService.find(queryCriteria); }
方案三:复用SecurityExpressionRoot的逻辑
通过表达式处理器获取SecurityExpressionRoot,直接调用和@PreAuthorize中一致的方法:
@Autowired private SecurityExpressionHandler<FilterInvocation> expressionHandler; @Autowired private HttpServletRequest request; @Autowired private HttpServletResponse response; @PostMapping("/find") public Pagination<BookDTO> find(@Valid @RequestBody QueryCriteria queryCriteria) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); FilterInvocation filterInvocation = new FilterInvocation(request, response, null); SecurityExpressionRoot expressionRoot = expressionHandler.createSecurityExpressionRoot(authentication, filterInvocation); // 和注解中完全一致的校验逻辑 boolean hasPermission = expressionRoot.hasRole("ADMIN") || expressionRoot.hasAuthority("GET_BOOK"); if (!hasPermission) { throw new AccessDeniedException("无权限访问该接口"); } return bookService.find(queryCriteria); }
各方案说明
- 方案一:最轻量化,无需额外注入组件,但需要手动处理权限字符串的前缀规则,适合简单场景。
- 方案二:Spring Security 6+官方推荐的权限校验方式,支持复杂规则组合,扩展性更好。
- 方案三:完全复用注解中的SpEL表达式逻辑,减少代码差异,但需要依赖请求响应对象,相对繁琐。
内容的提问来源于stack exchange,提问作者Tuấn Nguyễn
相关产品推荐
相关产品推荐

