Spring Security OAuth2 资源服务器:构建安全的微服务架构

张开发
2026/4/18 23:38:53 15 分钟阅读

分享文章

Spring Security OAuth2 资源服务器:构建安全的微服务架构
Spring Security OAuth2 资源服务器构建安全的微服务架构别叫我大神叫我 Alex 就好。一、引言大家好我是 Alex。在微服务架构中服务间的安全通信是一个核心问题。Spring Security 6 对 OAuth2 资源服务器的支持有了重大改进。今天我想和大家分享一下如何使用 Spring Security OAuth2 构建安全的资源服务器。二、OAuth2 资源服务器基础1. 核心概念资源服务器托管受保护资源的服务授权服务器颁发和管理访问令牌客户端请求访问资源的应用程序资源所有者拥有资源的用户2. JWT 令牌结构{ header: { alg: RS256, typ: JWT }, payload: { sub: user123, iss: https://auth.example.com, aud: resource-server, exp: 1699999999, scope: read write, roles: [USER, ADMIN] } }三、资源服务器配置1. 基础配置Configuration EnableWebSecurity public class ResourceServerConfig { Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize - authorize .requestMatchers(/api/public/**).permitAll() .requestMatchers(/api/admin/**).hasRole(ADMIN) .requestMatchers(/api/user/**).hasAnyRole(USER, ADMIN) .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 - oauth2 .jwt(jwt - jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()) ) ); return http.build(); } Bean public JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter authoritiesConverter new JwtGrantedAuthoritiesConverter(); authoritiesConverter.setAuthorityPrefix(ROLE_); authoritiesConverter.setAuthoritiesClaimName(roles); JwtAuthenticationConverter converter new JwtAuthenticationConverter(); converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter); converter.setPrincipalClaimName(sub); return converter; } }2. JWT 解码器配置Configuration public class JwtConfig { Bean public JwtDecoder jwtDecoder() { // 使用 JWKS 端点 NimbusJwtDecoder jwtDecoder NimbusJwtDecoder.withJwkSetUri( https://auth.example.com/.well-known/jwks.json ).build(); // 配置验证器 OAuth2TokenValidatorJwt withIssuer JwtValidators.createDefaultWithIssuer( https://auth.example.com ); OAuth2TokenValidatorJwt withAudience audienceValidator(); jwtDecoder.setJwtValidator(withIssuer.and(withAudience)); return jwtDecoder; } private OAuth2TokenValidatorJwt audienceValidator() { return jwt - { ListString audience jwt.getAudience(); if (audience ! null audience.contains(resource-server)) { return OAuth2TokenValidatorResult.success(); } return OAuth2TokenValidatorResult.failure( new OAuth2Error(invalid_token, Invalid audience, null) ); }; } }四、高级安全配置1. 方法级安全Service public class OrderService { PreAuthorize(hasRole(USER)) public Order getOrder(Long orderId) { // 查询订单 return orderRepository.findById(orderId).orElse(null); } PreAuthorize(hasRole(ADMIN) or orderSecurity.isOwner(#orderId, authentication)) public void updateOrder(Long orderId, OrderUpdateRequest request) { // 更新订单 } PostAuthorize(returnObject null or returnObject.customerId authentication.name) public Order getOrderDetail(Long orderId) { // 查询订单详情 return orderRepository.findById(orderId).orElse(null); } } Component(orderSecurity) public class OrderSecurity { Autowired private OrderRepository orderRepository; public boolean isOwner(Long orderId, Authentication authentication) { Order order orderRepository.findById(orderId).orElse(null); return order ! null order.getCustomerId().equals(authentication.getName()); } }2. 自定义权限评估Component public class CustomPermissionEvaluator implements PermissionEvaluator { Override public boolean hasPermission(Authentication authentication, Object targetDomain, Object permission) { if (authentication null || !authentication.isAuthenticated()) { return false; } String targetType targetDomain.getClass().getSimpleName(); return checkPermission(authentication, targetType, permission.toString()); } Override public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) { // 实现逻辑 return false; } private boolean checkPermission(Authentication authentication, String targetType, String permission) { // 自定义权限检查逻辑 return authentication.getAuthorities().stream() .anyMatch(auth - auth.getAuthority().equals(targetType : permission)); } }五、多租户支持Component public class TenantContext { private static final ThreadLocalString CURRENT_TENANT new ThreadLocal(); public static void setCurrentTenant(String tenant) { CURRENT_TENANT.set(tenant); } public static String getCurrentTenant() { return CURRENT_TENANT.get(); } public static void clear() { CURRENT_TENANT.remove(); } } Component public class TenantJwtDecoder implements JwtDecoder { Autowired private TenantRepository tenantRepository; Override public Jwt decode(String token) throws JwtException { Jwt jwt JwtDecoders.fromIssuerLocation(https://auth.example.com).decode(token); String tenantId jwt.getClaimAsString(tenant_id); TenantContext.setCurrentTenant(tenantId); return jwt; } }六、测试配置SpringBootTest AutoConfigureMockMvc public class ResourceServerTest { Autowired private MockMvc mockMvc; Test void testPublicEndpoint() throws Exception { mockMvc.perform(get(/api/public/info)) .andExpect(status().isOk()); } Test void testProtectedEndpointWithoutToken() throws Exception { mockMvc.perform(get(/api/user/profile)) .andExpect(status().isUnauthorized()); } Test WithMockJwt(roles USER, subject user123) void testProtectedEndpointWithToken() throws Exception { mockMvc.perform(get(/api/user/profile)) .andExpect(status().isOk()); } Test WithMockJwt(roles USER) void testAdminEndpointWithUserRole() throws Exception { mockMvc.perform(get(/api/admin/users)) .andExpect(status().isForbidden()); } } Retention(RetentionPolicy.RUNTIME) WithSecurityContext(factory WithMockJwtSecurityContextFactory.class) public interface WithMockJwt { String[] roles() default {}; String subject() default user; String[] scopes() default {}; } public class WithMockJwtSecurityContextFactory implements WithSecurityContextFactoryWithMockJwt { Override public SecurityContext createSecurityContext(WithMockJwt annotation) { SecurityContext context SecurityContextHolder.createEmptyContext(); ListGrantedAuthority authorities new ArrayList(); for (String role : annotation.roles()) { authorities.add(new SimpleGrantedAuthority(ROLE_ role)); } for (String scope : annotation.scopes()) { authorities.add(new SimpleGrantedAuthority(SCOPE_ scope)); } Jwt jwt Jwt.withTokenValue(mock-token) .header(alg, none) .subject(annotation.subject()) .claim(roles, Arrays.asList(annotation.roles())) .claim(scope, String.join( , annotation.scopes())) .build(); JwtAuthenticationToken authentication new JwtAuthenticationToken(jwt, authorities); context.setAuthentication(authentication); return context; } }七、生产环境配置1. 安全配置spring: security: oauth2: resourceserver: jwt: issuer-uri: https://auth.example.com jwk-set-uri: https://auth.example.com/.well-known/jwks.json audiences: - resource-server2. 异常处理Component public class CustomJwtAuthenticationEntryPoint implements AuthenticationEntryPoint { Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); MapString, Object error new HashMap(); error.put(error, Unauthorized); error.put(message, authException.getMessage()); error.put(path, request.getRequestURI()); new ObjectMapper().writeValue(response.getOutputStream(), error); } } Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpServletResponse.SC_FORBIDDEN); MapString, Object error new HashMap(); error.put(error, Forbidden); error.put(message, You dont have permission to access this resource); error.put(path, request.getRequestURI()); new ObjectMapper().writeValue(response.getOutputStream(), error); } }八、总结Spring Security OAuth2 资源服务器为我们提供了强大的安全保护能力。通过合理配置我们可以构建出既安全又灵活的微服务架构。这其实可以更优雅一点。希望这篇文章能帮助大家更好地理解和使用 Spring Security OAuth2。如果你有任何问题欢迎在评论区留言。关于作者我是 Alex一个在 CSDN 写 Java 架构思考的暖男。喜欢手冲咖啡养了一只叫Java的拉布拉多。如果我的文章对你有帮助欢迎关注我一起探讨 Java 技术的优雅之道。

更多文章