Spring Boot微服务中基于JWT的认证授权高效组织方案咨询
微服务认证架构落地指南(针对你的Spring Boot场景)
一、请求流向的最佳实践:先过认证,再到业务服务
别搞反了!绝对不能让请求先到业务服务再去校验认证,原因很简单:
- 每个业务服务都要重复写认证校验逻辑,代码冗余,维护成本高;
- 万一某个业务服务漏了校验,直接暴露业务接口,风险极大;
- 认证逻辑集中处理,才能统一控制权限规则、错误返回(比如401/403)。
正确的流向分两种,对应不同阶段的架构:
- 入门阶段(无API网关):客户端请求 → 业务服务 → 业务服务调用认证服务校验Token → 校验通过则处理业务
- 规范微服务阶段(有API网关):客户端请求 → API网关 → 网关调用认证服务校验Token → 校验通过转发到对应业务服务
二、无API网关的落地步骤(适合新手快速上手)
你已经有了认证服务,先从这个方式入手,门槛低:
1. 给认证服务加Token校验接口
在认证服务里新增一个接口,专门用来校验JWT有效性并返回用户信息:
@RestController @RequestMapping("/auth") public class AuthController { @Autowired private JwtTokenUtil jwtTokenUtil; @PostMapping("/validate") public ResponseEntity<AuthResponse> validateToken(@RequestBody TokenRequest request) { String token = request.getToken(); if (!jwtTokenUtil.validateToken(token)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } UserDetails userDetails = jwtTokenUtil.getUserFromToken(token); return ResponseEntity.ok(new AuthResponse(userDetails.getUsername(), userDetails.getAuthorities())); } }
注意:用户登出时,要把失效Token加入Redis黑名单,校验前先查黑名单,防止盗用。
2. 业务服务里加认证过滤器
在业务服务的Spring Security配置中,添加自定义Filter,在请求到达业务接口前完成认证:
@Component public class AuthValidationFilter extends OncePerRequestFilter { @Autowired private AuthFeignClient authFeignClient; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 从HttpOnly Cookie中提取Token String token = null; Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("accessToken".equals(cookie.getName())) { token = cookie.getValue(); break; } } } if (token == null) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing token"); return; } // 调用认证服务校验Token try { AuthResponse authResponse = authFeignClient.validateToken(new TokenRequest(token)); // 构造认证对象并放入SecurityContext List<GrantedAuthority> authorities = authResponse.getAuthorities().stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( authResponse.getUsername(), null, authorities); SecurityContextHolder.getContext().setAuthentication(authentication); } catch (FeignException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token"); return; } filterChain.doFilter(request, response); } }
然后在Spring Security配置中把这个Filter前置:
@Configuration @EnableWebSecurity public class WebSecurityConfig { @Autowired private AuthValidationFilter authValidationFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .addFilterBefore(authValidationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
3. 服务间调用工具:OpenFeign
用OpenFeign调用认证服务接口,比RestTemplate更符合Spring风格:
先在业务服务pom.xml加依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
然后写FeignClient接口:
@FeignClient(name = "auth-service", url = "http://localhost:8080") public interface AuthFeignClient { @PostMapping("/auth/validate") AuthResponse validateToken(@RequestBody TokenRequest request); }
记得在启动类加@EnableFeignClients注解。
三、API网关的落地拆解(从抽象到具体)
等熟悉无网关模式后,再升级到API网关,这是微服务的标准方案:
1. 新建Spring Cloud Gateway项目
加核心依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
2. 网关里加全局认证Filter
校验逻辑和业务服务类似,但校验完成后直接转发到业务服务,无需处理业务:
@Component public class GatewayAuthFilter implements GlobalFilter, Ordered { @Autowired private AuthFeignClient authFeignClient; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 从Cookie提取Token String token = null; HttpCookie cookie = request.getCookies().getFirst("accessToken"); if (cookie != null) { token = cookie.getValue(); } if (token == null) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } // 调用认证服务校验Token,校验通过后携带用户信息转发 return authFeignClient.validateToken(new TokenRequest(token)) .map(authResponse -> { ServerHttpRequest modifiedRequest = request.mutate() .header("X-User-Name", authResponse.getUsername()) .header("X-User-Authorities", authResponse.getAuthorities().toString()) .build(); return exchange.mutate().request(modifiedRequest).build(); }) .flatMap(chain::filter) .onErrorResume(e -> { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); }); } @Override public int getOrder() { return -100; // 确保该Filter优先执行 } }
3. 配置路由规则
在application.yml中设置转发规则:
spring: cloud: gateway: routes: - id: user-service-route uri: http://localhost:8081 predicates: - Path=/api/user/** - id: order-service-route uri: http://localhost:8082 predicates: - Path=/api/order/**
客户端请求http://网关地址/api/user/info时,网关会自动转发到用户业务服务,且已完成认证。
四、关键最佳实践提醒
- 认证服务单一职责:只做注册、登录、Token生成/校验、登出,不要掺杂业务逻辑,方便后续扩展。
- Token生命周期管理:accessToken设为15分钟左右,refreshToken设为7天,登录时返回两个HttpOnly Cookie,accessToken过期后用refreshToken换新的accessToken。
- 服务发现替换硬编码:后续服务增多后,用Eureka或Nacos做服务发现,FeignClient直接用服务名调用(比如
@FeignClient(name = "auth-service"))。
内容的提问来源于stack exchange,提问作者Georgi Babov
相关产品推荐
相关产品推荐

