API 安全实战!Spring Boot 接口权限控制的7种实现方案

Spring Boot 3实战案例锦集 》 PDF电子书已更新至130篇!

《 Spring Boot实战案例合集 》目前已更新 226个案例 ,我们将持续不断的更新。 目前就订阅合集

环境:Spring Boot 3.5.0

1. 简介

在Spring Boot开发中,接口权限控制是捍卫系统安全的核心防线。随着业务角色的日益复杂(如管理员、普通用户、访客等),不同身份必须被严格限制在特定的操作范围内。若缺乏精细化管控,极易引发越权访问与敏感数据泄露等严重的风险。

本文将结合实战场景,为你深度剖析7种控制Controller接口访问的主流实现方案。从轻量级的原生代码拦截,到强劲的企业级安全框架,助你在系统安全性与开发效率之间找到最佳平衡,精准完成架构选型。

2.实战案例

2.1 基于Spring Security

Spring Security 是 Spring 生态中的核心安全框架,全面拥抱组件化配置。

引入依赖:

org.springframework.bootgroupId>spring-boot-starter-securityartifactId>dependency>

配置文件(我们这里通过静态配置用户信息)

spring: security: user: name: pack password: 123123 roles: - USERS - MGR

权限开启及配置

@Configuration@EnableMethodSecuritypublic classSecurityConfig{@Bean SecurityFilterChain packSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf ->csrf.disable) ; http.authorizeHttpRequests(registry -> { registry.requestMatchers("/resources/**").permitAll ; registry.requestMatchers("/api/**").authenticated ; }) ; http.formLogin(withDefaults) ; http.exceptionHandling(ehc -> { ehc.accessDeniedPage("/resources/noauthority.html") ; }) ;return http.build ; }}

Controller接口定义

@GetMapping("/query")@PreAuthorize("hasRole('USERS')")publicResponseEntity query {returnResponseEntity.ok("query") ;}

2.2 基于Sa-Token

Sa-Token 是国内目前超级火爆的轻量级 Java 权限认证框架,专门针对后台系统的 RBAC设计,且完美支持 Spring Boot 2/3。

引入依赖

cn.dev33groupId>sa-token-spring-boot3-starterartifactId>1.45.0version>dependency>

模拟登录

@GetMapping("/login")publicResponseEntity login {if (!StpUtil.isLogin) {StpUtil.login("xxxooo", 15000) ; }returnResponseEntity.ok(StpUtil.getTokenValueByLoginId("xxxooo")) ;}

自定义StpInterface获取权限接口

@ComponentpublicclassSaPermissionStpInterfaceimplementsStpInterface {@OverridepublicList getPermissionList(Object loginId, String loginType) {returnList.of ; }@OverridepublicList getRoleList(Object loginId, String loginType) {returnList.of("ADMIN", "MGR") ; }}

配置拦截器,开启权限认证

@ConfigurationpublicclassSaTokenConfigureimplementsWebMvcConfigurer {@OverridepublicvoidaddInterceptors(InterceptorRegistry registry) { registry.addInterceptor(newSaInterceptor).addPathPatterns("/**"); }}

Controller接口定义

@GetMapping("/query")@SaCheckRole(value = {"USERS"})publicResponseEntity query {returnResponseEntity.ok("query") ;}

测试

第一,登录获取token

API 安全实战!Spring Boot 接口权限控制的7种实现方案

最后,访问接口并携带token值

API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案

这是没有权限的错误情况。有权限则正常访问。

2.3 自定义注解+AOP

如果不希望引入任何重量级安全框架,我们可以利用 AOP 自己写一套逻辑,这种方式在许多中小型项目中超级常见,而且这种方式超级的灵活。

定义注解

@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)public@interface Permission { String value ;}

定义切面

@Aspect@ComponentpublicclassPermissionAspect {@Before("@annotation(permission)")publicvoidcheckPermission(JoinPoint joinPoint, Permission permission) {String neededPerm = permission.value;// 1. 获取当前登录用户权限Set permissions = UserContext.getPermissions ;// 2. 校验if (Collections.disjoint(permissions, Set.copyOf(Arrays.asList(neededPerm)))) {thrownewAccessDeniedException("权限不足"); } }}// 模拟用户登录上下文信息publicclassUserContext {publicstaticSet getPermissions {returnSet.of("user.create", "user.update", "user.query") ; }}

Controller接口定义

@GetMapping("/query")@Permission({"api.query"})publicResponseEntity query {returnResponseEntity.ok("query") ;}

2.4 自定义注解+拦截器

与 AOP 类似,但它处于 Web 层(Spring MVC),实现 HandlerInterceptor 接口,在 preHandle 方法中获取当前请求映射的 Controller 方法( HandlerMethod ),读取注解并校验。

注解定义

我们直接复用上面的 @Permission 注解即可。

拦截器定义

@ComponentpublicclassPermissionInterceptorimplementsHandlerInterceptor {
private final ObjectMapper objectMapper ;publicPermissionInterceptor(ObjectMapper objectMapper) {this.objectMapper = objectMapper; }@OverridepublicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (!(handler instanceofHandlerMethod)) {returntrue; }HandlerMethod handlerMethod = (HandlerMethod) handler;Permission permission = handlerMethod.getMethodAnnotation(Permission.class);if (permission != ) {String neededPerm = permission.value;// 1. 获取当前登录用户权限Set permissions = UserContext.getPermissions ;// 2. 校验if (Collections.disjoint(permissions, Set.copyOf(Arrays.asList(neededPerm)))) { response.setContentType("application/json;charset=utf-8") ; response.getWriter.println(this.objectMapper.writeValueAsString(Map.of("code", -1, "msg", "无权限"))) ;returnfalse ; } }returntrue; }}

配置拦截器

@ConfigurationpublicclassPermissionMvcConfigurerimplementsWebMvcConfigurer {privatefinal PermissionInterceptor permissionInterceptor ;publicPermissionMvcConfigurer(PermissionInterceptor permissionInterceptor) {this.permissionInterceptor = permissionInterceptor; }
@OverridepublicvoidaddInterceptors(InterceptorRegistry registry) { registry.addInterceptor(permissionInterceptor).addPathPatterns("/api/**") ; }}

Controller接口定义

与上面的AOP方案使用的接口一致。

测试,无权限情况:

API 安全实战!Spring Boot 接口权限控制的7种实现方案

2.5 基于 URL 路径匹配的Filter

在Spring Boot中我们通过继承 OncePerRequestFilter ,拦截所有请求,提取 URI,去 Redis 或内存中的 “权限集合” 进行比对。

Filter定义

@ComponentpublicclassPermissionFilterextendsOncePerRequestFilter {// 模拟针对每个接口配置的权限privatestatic final Map> permissions = newHashMap<> ;static { permissions.put("/api/query", Set.of("user.query")) ; permissions.put("/api/create", Set.of("user.create")) ; }
private final ObjectMapper objectMapper ;publicPermissionFilter(ObjectMapper objectMapper) {this.objectMapper = objectMapper; }
@OverrideprotectedvoiddoFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String requestUri = request.getRequestURI ;Set userPerms = UserContext.getPermissions ;if (!Collections.disjoint(userPerms, permissions.get(requestUri))) { filterChain.doFilter(request, response) ; return ; } response.setContentType("application/json;charset=utf-8") ; response.getWriter.println(this.objectMapper.writeValueAsString(Map.of("code", -1, "msg", "无权限"))) ; }}

Controller接口定义

@GetMapping("/query")publicResponseEntity query {returnResponseEntity.ok("query");}@GetMapping("/create")publicResponseEntity create {returnResponseEntity.ok("create");}

模拟当前用户权限

publicclassUserContext {publicstaticSet getPermissions {returnSet.of("user.query") ; }}

测试结果

API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案

2.6 自定义HandlerMapping

该种实现方案整体性能是最高的,不过需要自定义底层MVC的核心组件,详细可以查看这篇文章:

碾压传统 AOP!Spring Boot 权限控制新方案,优雅又高效

2.7 基于 Gateway 权限校验

我们可以单独建立一个Spring Cloud Gateway服务,该网关服务专门用来进行权限校验。通过编写 GlobalFilter 。网关解析 Token,校验用户是否有权限访问目标路由的 API。

自 Spring Cloud Gateway 4.1.0 版本开始,官方提供了基于Web/Servlet技术栈的实现,这大大降低了我们的学习成本。

详细查看下面链接:

重磅!Spring Cloud Gateway 全面支持 Servlet 技术栈,简单多了

引入依赖

org.springframework.cloudgroupId>spring-cloud-starter-gateway-server-webfluxartifactId>dependency>

路由配置

spring: cloud: gateway: server: webflux: routes: - id: path_route uri: http://localhost:8080 predicates: - Path=/api-1/** filters: - StripPrefix=1

自定义GlobalFilter

@Component@Order(Ordered.HIGHEST_PRECEDENCE)publicclassPermissionGlobalFilterimplementsGlobalFilter {privatestaticfinal Map> permissions = newHashMap<>;privatestaticfinal Map> path_permissions = newHashMap<>;static {// 模拟不同 token 所拥有的权限 permissions.put("xxxooo", Set.of("user.query")); permissions.put("aaabbb", Set.of("user.create"));// 模拟不同路径需要具备的权限 path_permissions.put("/api-1/api/query", Set.of("user.query")); path_permissions.put("/api-1/api/create", Set.of("user.create")); }@Overridepublic Monofilter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequestrequest= exchange.getRequest;Stringpath= request.getURI.getRawPath;Stringtoken= request.getQueryParams.getFirst("token");// 1.检查 Token 是否为空if (!StringUtils.hasLength(token)) {return writeErrorResponse(exchange, "缺少Token,请先登录"); }// 2.获取该路径需要的权限(如果没有配置,默认放行) Set requiredPermission = path_permissions.get(path);if (requiredPermission.isEmpty) {return chain.filter(exchange); }// 3.校验当前 Token 是否拥有对应权限 Set userPermissions = permissions.get(token);if (Collections.disjoint(requiredPermission, userPermissions)) {return writeErrorResponse(exchange, "无权限"); }// 4.鉴权通过,放行请求return chain.filter(exchange); }private MonowriteErrorResponse(ServerWebExchange exchange, String message) { exchange.getResponse.getHeaders.setContentType(MediaType.APPLICATION_JSON);Stringresult=""" {"code": -1, "msg": %s} """.formatted(message);DataBufferbuffer= exchange.getResponse.bufferFactory.wrap(result.getBytes(StandardCharsets.UTF_8));return exchange.getResponse.writeWith(Mono.just(buffer)); }}

测试结果

API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案

以上是本篇文章的全部内容,如对你有协助帮忙 点赞+转发+收藏

7 个高阶 CLAUDE.md 技巧,让 Claude Code 战力翻倍

告别多源查询混乱!Spring Boot + Calcite 实现跨库查询

千万级数据查询零 OOM:Spring Boot 流式查询终极实战方案

Java Agent + ByteBuddy 实现 Spring Boot 运行时方法级监控

效率提升300%!Spring Boot 单接口动态实现6种数据格式

分布式场景下 @Scheduled 防重复执行的三大方案

Spring Boot 超大请求/响应 JSON(100M)如何避免OOM?

Spring Batch 处理大数据集 异步+虚拟线程,性能提升90倍

太强了!开源Java并行收集器,秒杀标准并行流

太好用了!Java方法返回多个值

高级开发!Spring Boot 深入底层优化接口调用,性能提升33%

API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案API 安全实战!Spring Boot 接口权限控制的7种实现方案

© 版权声明

相关文章

1 条评论

none
暂无评论...