Spring Boot 3与JWT认证架构设计实践
1. Spring Boot 3与JWT认证架构设计在分布式系统中认证与授权是保障应用安全的核心机制。Spring Boot 3作为当前Java生态中最主流的应用开发框架与JWTJSON Web Token的结合能够完美解决分布式环境下的认证难题。JWT本质上是一种基于Token的无状态认证方案其核心优势在于服务端无需维护会话状态天然适合水平扩展令牌自包含用户信息减少数据库查询压力采用数字签名保证完整性防止篡改标准化格式跨语言、跨平台兼容性好典型的JWT由三部分组成通过点号(.)连接Header.Payload.Signature其中Header包含算法类型和令牌类型Payload携带用户声明信息Signature用于验证消息完整性。这种结构设计使得JWT既能够安全传输信息又便于客户端存储通常使用localStorage或Cookie。2. 环境搭建与基础配置2.1 项目初始化与依赖管理使用Spring Initializr创建项目时需要选择以下核心依赖dependencies !-- Spring Boot基础starter -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Security核心 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency !-- JWT处理库 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency !-- 数据持久化 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-jpa/artifactId /dependency dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency /dependencies2.2 安全配置基础类创建安全配置类SecurityConfig这是整个认证体系的控制中心Configuration EnableWebSecurity RequiredArgsConstructor public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthFilter; Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth - auth .requestMatchers(/api/auth/**).permitAll() .anyRequest().authenticated() ) .sessionManagement(session - session .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } }关键配置解析csrf().disable()JWT不需要CSRF保护STATELESS会话策略确保无状态密码编码器采用BCrypt强哈希算法认证管理器处理用户名密码验证3. JWT核心服务实现3.1 令牌生成与验证创建JwtService处理JWT的核心操作Service RequiredArgsConstructor public class JwtService { Value(${jwt.secret}) private String secretKey; Value(${jwt.expiration}) private long jwtExpiration; public String generateToken(UserDetails userDetails) { return buildToken(new HashMap(), userDetails, jwtExpiration); } private String buildToken( MapString, Object extraClaims, UserDetails userDetails, long expiration ) { return Jwts.builder() .claims(extraClaims) .subject(userDetails.getUsername()) .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() expiration)) .signWith(getSignInKey(), JWT_SIG.HS256) .compact(); } public boolean isTokenValid(String token, UserDetails userDetails) { final String username extractUsername(token); return (username.equals(userDetails.getUsername())) !isTokenExpired(token); } private boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } private Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } private T T extractClaim(String token, FunctionClaims, T claimsResolver) { final Claims claims extractAllClaims(token); return claimsResolver.apply(claims); } private Claims extractAllClaims(String token) { return Jwts.parser() .verifyWith(getSignInKey()) .build() .parseSignedClaims(token) .getPayload(); } private SecretKey getSignInKey() { byte[] keyBytes Decoders.BASE64.decode(secretKey); return Keys.hmacShaKeyFor(keyBytes); } }3.2 认证过滤器实现创建JWT认证过滤器处理每个请求的认证Component RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; private final UserDetailsService userDetailsService; Override protected void doFilterInternal( NonNull HttpServletRequest request, NonNull HttpServletResponse response, NonNull FilterChain filterChain ) throws ServletException, IOException { final String authHeader request.getHeader(Authorization); if (authHeader null || !authHeader.startsWith(Bearer )) { filterChain.doFilter(request, response); return; } final String jwt authHeader.substring(7); final String userEmail jwtService.extractUsername(jwt); if (userEmail ! null SecurityContextHolder.getContext().getAuthentication() null) { UserDetails userDetails this.userDetailsService.loadUserByUsername(userEmail); if (jwtService.isTokenValid(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails( new WebAuthenticationDetailsSource().buildDetails(request) ); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } }4. 分布式系统增强方案4.1 用户信息缓存优化在分布式环境下频繁查询用户信息会影响性能。使用Redis缓存用户数据Configuration EnableCaching public class CacheConfig { Bean public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration config RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)) .serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(connectionFactory) .cacheDefaults(config) .build(); } } Service CacheConfig(cacheNames users) public class UserDetailsServiceImpl implements UserDetailsService { private final UserRepository userRepository; Override Cacheable(key #username) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user userRepository.findByEmail(username) .orElseThrow(() - new UsernameNotFoundException(User not found)); return new org.springframework.security.core.userdetails.User( user.getEmail(), user.getPassword(), getAuthorities(user.getRoles()) ); } private Collection? extends GrantedAuthority getAuthorities(SetRole roles) { return roles.stream() .map(role - new SimpleGrantedAuthority(role.getName())) .collect(Collectors.toList()); } }4.2 令牌黑名单机制实现令牌主动失效的黑名单机制Service RequiredArgsConstructor public class TokenBlacklistService { private final RedisTemplateString, String redisTemplate; public void addToBlacklist(String token, long expirationMs) { String key blacklist: token; redisTemplate.opsForValue().set( key, invalid, expirationMs, TimeUnit.MILLISECONDS ); } public boolean isBlacklisted(String token) { return Boolean.TRUE.equals( redisTemplate.hasKey(blacklist: token) ); } } // 在JwtService中增加黑名单检查 public boolean isTokenValid(String token, UserDetails userDetails) { if (tokenBlacklistService.isBlacklisted(token)) { return false; } // 原有验证逻辑... }4.3 密钥轮换策略定期更换签名密钥提升安全性Component public class KeyRotationScheduler { private final JwtService jwtService; Scheduled(cron 0 0 0 * * ?) // 每天午夜执行 public void rotateKey() { jwtService.rotateSecretKey(); } } // JwtService中增加密钥轮换方法 private ListSecretKey historicalKeys new ArrayList(); public void rotateSecretKey() { historicalKeys.add(0, getCurrentKey()); // 将当前密钥存入历史 if (historicalKeys.size() 3) { // 保留最近3个密钥 historicalKeys historicalKeys.subList(0, 3); } // 生成新密钥 this.currentKey Keys.secretKeyFor(SignatureAlgorithm.HS256); } private Claims extractAllClaims(String token) { // 先尝试用当前密钥解析 try { return tryParseWithKey(token, currentKey); } catch (JwtException e) { // 尝试用历史密钥解析 for (SecretKey key : historicalKeys) { try { return tryParseWithKey(token, key); } catch (JwtException ignored) {} } throw new JwtException(Invalid token); } }5. 认证接口与权限控制5.1 认证接口实现创建认证控制器处理登录/注册RestController RequestMapping(/api/auth) RequiredArgsConstructor public class AuthenticationController { private final AuthenticationManager authenticationManager; private final JwtService jwtService; private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; PostMapping(/register) public ResponseEntityAuthenticationResponse register( RequestBody RegisterRequest request ) { var user User.builder() .email(request.getEmail()) .password(passwordEncoder.encode(request.getPassword())) .roles(Set.of(Role.USER)) .build(); userRepository.save(user); var jwtToken jwtService.generateToken(user); return ResponseEntity.ok( AuthenticationResponse.builder() .token(jwtToken) .build() ); } PostMapping(/authenticate) public ResponseEntityAuthenticationResponse authenticate( RequestBody AuthenticationRequest request ) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( request.getEmail(), request.getPassword() ) ); var user userRepository.findByEmail(request.getEmail()) .orElseThrow(); var jwtToken jwtService.generateToken(user); return ResponseEntity.ok( AuthenticationResponse.builder() .token(jwtToken) .build() ); } }5.2 基于角色的权限控制实现方法级和URL级的权限控制Configuration EnableMethodSecurity public class MethodSecurityConfig { Bean public MethodSecurityExpressionHandler methodSecurityExpressionHandler() { DefaultMethodSecurityExpressionHandler handler new DefaultMethodSecurityExpressionHandler(); handler.setPermissionEvaluator(new CustomPermissionEvaluator()); return handler; } } // URL级权限配置 Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests(auth - auth .requestMatchers(/api/admin/**).hasRole(ADMIN) .requestMatchers(/api/user/**).hasAnyRole(USER, ADMIN) // 其他配置... ); return http.build(); } // 方法级权限控制示例 Service public class ProductService { PreAuthorize(hasRole(ADMIN) or #product.owner authentication.name) public void deleteProduct(Product product) { // 实现删除逻辑 } PostAuthorize(returnObject.owner authentication.name) public Product getProduct(Long id) { // 查询并返回产品 } }6. 生产环境最佳实践6.1 安全加固措施HTTPS强制配置Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.requiresChannel(channel - channel.anyRequest().requiresSecure()); // 其他配置... }敏感信息保护使用环境变量或密钥管理服务存储JWT密钥避免在日志中输出完整令牌令牌安全策略// 在JWT生成时设置安全相关声明 private String buildToken(UserDetails userDetails) { return Jwts.builder() // ...其他声明 .claim(iss, your-issuer) // 签发者 .claim(aud, your-audience) // 受众 .claim(jti, UUID.randomUUID().toString()) // 唯一标识 // ...签名等 }6.2 性能优化建议异步认证处理public class AsyncJwtFilter extends OncePerRequestFilter { private final Executor asyncExecutor Executors.newFixedThreadPool(4); Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { CompletableFuture.runAsync(() - { // 认证处理逻辑 }, asyncExecutor).thenRun(() - { filterChain.doFilter(request, response); }); } }令牌压缩// 使用DEFLATE压缩算法减小令牌体积 public String generateCompressedToken(UserDetails userDetails) { String token buildToken(userDetails); byte[] compressed DeflaterUtils.deflate(token.getBytes()); return Base64.getUrlEncoder().encodeToString(compressed); } // 客户端需要解压缩 public Claims parseCompressedToken(String compressedToken) { byte[] decoded Base64.getUrlDecoder().decode(compressedToken); String token InflaterUtils.inflateToString(decoded); return parseToken(token); }分布式会话一致性// 使用Redis发布订阅机制同步令牌状态 Bean public MessageListenerAdapter tokenInvalidationListener() { return new MessageListenerAdapter( new MessageListener() { Override public void onMessage(Message message, byte[] pattern) { String token new String(message.getBody()); tokenBlacklistService.addToBlacklist(token); } } ); }7. 常见问题排查指南7.1 认证失败场景处理令牌过期客户端应捕获401错误实现令牌刷新机制PostMapping(/refresh-token) public ResponseEntityAuthenticationResponse refreshToken( HttpServletRequest request ) { final String authHeader request.getHeader(Authorization); if (authHeader null || !authHeader.startsWith(Bearer )) { throw new AuthenticationException(Invalid refresh token); } String refreshToken authHeader.substring(7); String username jwtService.extractUsername(refreshToken); if (username ! null) { UserDetails user userDetailsService.loadUserByUsername(username); if (jwtService.isTokenValid(refreshToken, user)) { String accessToken jwtService.generateToken(user); return ResponseEntity.ok( AuthenticationResponse.builder() .token(accessToken) .refreshToken(refreshToken) .build()); } } throw new AuthenticationException(Refresh token expired); }签名验证失败检查密钥是否一致验证密钥轮换逻辑是否正确确认令牌未被篡改跨域问题Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); CorsConfiguration config new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); }7.2 性能问题排查Redis连接池配置spring: redis: lettuce: pool: max-active: 20 max-idle: 10 min-idle: 5 max-wait: 2000JWT解析性能监控Aspect Component RequiredArgsConstructor public class JwtPerformanceAspect { private final MeterRegistry meterRegistry; Around(execution(* com.example.jwt.JwtService.parseToken(..))) public Object measureParseTime(ProceedingJoinPoint pjp) throws Throwable { long start System.currentTimeMillis(); try { return pjp.proceed(); } finally { long duration System.currentTimeMillis() - start; meterRegistry.timer(jwt.parse.time).record(duration, TimeUnit.MILLISECONDS); } } }缓存命中率优化Cacheable(value users, key #username, unless #result null) public UserDetails loadUserByUsername(String username) { // 查询实现 }8. 进阶扩展方案8.1 多因素认证集成Service RequiredArgsConstructor public class MfaService { private final UserRepository userRepository; private final JavaMailSender mailSender; public void sendVerificationCode(String email) { User user userRepository.findByEmail(email) .orElseThrow(); String code generateRandomCode(); user.setMfaCode(code); userRepository.save(user); sendEmail(email, Your verification code, code); } Transactional public boolean verifyCode(String email, String code) { return userRepository.findByEmail(email) .map(user - user.getMfaCode().equals(code)) .orElse(false); } private String generateRandomCode() { return String.format(%06d, new Random().nextInt(999999)); } } // 在认证流程中集成MFA PostMapping(/login) public ResponseEntity? login( RequestBody AuthenticationRequest request, RequestParam(required false) String mfaCode) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( request.getEmail(), request.getPassword() ) ); User user userRepository.findByEmail(request.getEmail()) .orElseThrow(); if (user.isMfaEnabled()) { if (mfaCode null) { mfaService.sendVerificationCode(user.getEmail()); return ResponseEntity.status(HttpStatus.ACCEPTED) .body(MFA code required); } if (!mfaService.verifyCode(user.getEmail(), mfaCode)) { throw new BadCredentialsException(Invalid MFA code); } } String jwtToken jwtService.generateToken(user); return ResponseEntity.ok( AuthenticationResponse.builder() .token(jwtToken) .build() ); }8.2 微服务间认证实现服务间JWT传递验证FeignClient(name inventory-service, configuration FeignClientConfig.class) public interface InventoryClient { GetMapping(/api/inventory/{productId}) Inventory getInventory(PathVariable Long productId); } Configuration public class FeignClientConfig { Bean public RequestInterceptor requestInterceptor() { return requestTemplate - { String token Optional.ofNullable(SecurityContextHolder.getContext()) .map(SecurityContext::getAuthentication) .filter(authentication - authentication.getCredentials() instanceof String) .map(authentication - (String) authentication.getCredentials()) .orElseThrow(() - new IllegalStateException(No JWT token found)); requestTemplate.header(Authorization, Bearer token); }; } }8.3 实时权限更新实现动态权限刷新Service public class DynamicPermissionService { private final MapString, ListGrantedAuthority permissionCache new ConcurrentHashMap(); Scheduled(fixedRate 300000) // 每5分钟刷新一次 public void refreshPermissions() { // 从数据库或配置中心加载最新权限 // 更新permissionCache } public Collection? extends GrantedAuthority getAuthorities(String username) { return permissionCache.getOrDefault(username, Collections.emptyList()); } } // 在认证过滤器中应用动态权限 public class JwtAuthenticationFilter extends OncePerRequestFilter { // ...其他代码 Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { // ...令牌验证 if (jwtService.isTokenValid(jwt, userDetails)) { // 获取最新权限 Collection? extends GrantedAuthority authorities dynamicPermissionService.getAuthorities(username); UsernamePasswordAuthenticationToken authToken new UsernamePasswordAuthenticationToken( userDetails, null, authorities // 使用动态权限 ); // ...设置认证 } } }9. 监控与日志记录9.1 认证指标收集集成Micrometer收集认证指标Configuration public class MetricsConfig { Bean public MeterRegistryCustomizerMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags( application, auth-service ); } } Service public class AuthMetrics { private final Counter authSuccessCounter; private final Counter authFailureCounter; private final Timer authProcessingTimer; public AuthMetrics(MeterRegistry registry) { authSuccessCounter registry.counter(auth.attempt, result, success); authFailureCounter registry.counter(auth.attempt, result, failure); authProcessingTimer registry.timer(auth.processing.time); } public void recordSuccess() { authSuccessCounter.increment(); } public void recordFailure() { authFailureCounter.increment(); } public Timer.Sample startTimer() { return Timer.start(); } public void stopTimer(Timer.Sample sample) { sample.stop(authProcessingTimer); } }9.2 审计日志实现记录关键安全事件Entity Table(name audit_logs) Data NoArgsConstructor AllArgsConstructor Builder public class AuditLog { Id GeneratedValue(strategy GenerationType.IDENTITY) private Long id; private String username; private String action; private String ipAddress; private String userAgent; private LocalDateTime timestamp; private boolean success; private String details; } Aspect Component RequiredArgsConstructor public class AuditLogAspect { private final AuditLogRepository auditLogRepository; AfterReturning(pointcut annotation(auditable), returning result) public void logSuccessfulOperation(JoinPoint jp, Auditable auditable, Object result) { saveLog(jp, auditable.value(), true, null); } AfterThrowing(pointcut annotation(auditable), throwing ex) public void logFailedOperation(JoinPoint jp, Auditable auditable, Exception ex) { saveLog(jp, auditable.value(), false, ex.getMessage()); } private void saveLog(JoinPoint jp, String action, boolean success, String error) { HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) .getRequest(); Authentication auth SecurityContextHolder.getContext().getAuthentication(); String username auth ! null ? auth.getName() : anonymous; AuditLog log AuditLog.builder() .username(username) .action(action) .ipAddress(request.getRemoteAddr()) .userAgent(request.getHeader(User-Agent)) .timestamp(LocalDateTime.now()) .success(success) .details(error) .build(); auditLogRepository.save(log); } } // 使用示例 RestController public class AdminController { Auditable(DELETE_USER) DeleteMapping(/users/{id}) public void deleteUser(PathVariable Long id) { // 删除逻辑 } }10. 测试策略与实施10.1 单元测试示例测试JWT服务核心功能ExtendWith(MockitoExtension.class) class JwtServiceTest { Mock private UserDetailsService userDetailsService; InjectMocks private JwtService jwtService; Test void shouldGenerateValidToken() { UserDetails user User.withUsername(testexample.com) .password(password) .authorities(ROLE_USER) .build(); String token jwtService.generateToken(user); assertNotNull(token); Claims claims jwtService.extractAllClaims(token); assertEquals(testexample.com, claims.getSubject()); } Test void shouldDetectExpiredToken() { UserDetails user User.withUsername(testexample.com) .password(password) .authorities(ROLE_USER) .build(); // 生成一个立即过期的令牌 String token Jwts.builder() .subject(user.getUsername()) .issuedAt(new Date(System.currentTimeMillis() - 10000)) .expiration(new Date(System.currentTimeMillis() - 5000)) .signWith(jwtService.getSignInKey()) .compact(); assertFalse(jwtService.isTokenValid(token, user)); } }10.2 集成测试方案测试完整认证流程SpringBootTest(webEnvironment WebEnvironment.RANDOM_PORT) AutoConfigureMockMvc class AuthenticationIntegrationTest { Autowired private MockMvc mockMvc; Autowired private UserRepository userRepository; Autowired private PasswordEncoder passwordEncoder; BeforeEach void setup() { userRepository.deleteAll(); User user User.builder() .email(testexample.com) .password(passwordEncoder.encode(password)) .roles(Set.of(Role.USER)) .build(); userRepository.save(user); } Test void shouldAuthenticateWithValidCredentials() throws Exception { mockMvc.perform(post(/api/auth/authenticate) .contentType(MediaType.APPLICATION_JSON) .content({\email\:\testexample.com\,\password\:\password\})) .andExpect(status().isOk()) .andExpect(jsonPath($.token).exists()); } Test void shouldRejectInvalidCredentials() throws Exception { mockMvc.perform(post(/api/auth/authenticate) .contentType(MediaType.APPLICATION_JSON) .content({\email\:\testexample.com\,\password\:\wrong\})) .andExpect(status().isUnauthorized()); } Test void shouldAllowAccessWithValidToken() throws Exception { String token obtainAccessToken(testexample.com, password); mockMvc.perform(get(/api/protected) .header(Authorization, Bearer token)) .andExpect(status().isOk()); } private String obtainAccessToken(String username, String password) throws Exception { String response mockMvc.perform(post(/api/auth/authenticate) .contentType(MediaType.APPLICATION_JSON) .content({\email\:\ username \,\password\:\ password \})) .andReturn().getResponse().getContentAsString(); return new ObjectMapper().readTree(response).path(token).asText(); } }10.3 性能测试建议使用JMeter进行负载测试测试场景设计模拟100/500/1000并发用户登录测试令牌验证接口的吞吐量测量Redis缓存命中率对性能的影响关键指标监控平均响应时间错误率系统资源使用率CPU、内存JVM垃圾回收情况优化方向调整JWT签名算法如从HS256改为HS512优化Redis连接池配置增加JWT解析的并行处理能力11. 部署与运维指南11.1 容器化部署Dockerfile示例FROM eclipse-temurin:17-jdk-jammy WORKDIR /app COPY target/auth-service.jar app.jar ENV SPRING_PROFILES_ACTIVEprod ENV JAVA_OPTS-Xms512m -Xmx1024m -XX:UseG1GC EXPOSE 8080 ENTRYPOINT [sh, -c, java ${JAVA_OPTS} -jar app.jar]Docker Compose配置version: 3.8 services: auth-service: image: auth-service:latest ports: - 8080:8080 environment: - SPRING_DATASOURCE_URLjdbc:postgresql://db:5432/auth - SPRING_DATASOURCE_USERNAMEpostgres - SPRING_DATASOURCE_PASSWORDpostgres - SPRING_REDIS_HOSTredis - JWT_SECRET${JWT_SECRET} depends_on: - db - redis db: image: postgres:15 environment: POSTGRES_PASSWORD: postgres volumes: - pg_data:/var/lib/postgresql/data redis: image: redis:7 volumes: - redis_data:/data volumes: pg_data: redis_data:11.2 Kubernetes部署Deployment配置示例apiVersion: apps/v1 kind: Deployment metadata: name: auth-service spec: replicas: 3 selector: matchLabels: app: auth-service template: metadata: labels: app: auth-service spec: containers: - name: auth-service image: auth-service:latest ports: - containerPort: 8080 envFrom: - secretRef: name: auth-secrets resources: requests: memory: 1Gi cpu: 500m limits: memory: 2Gi cpu: 1 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 20 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: auth-service spec: selector: app: auth-service ports: - protocol: TCP port: 80 targetPort: 808011.3 密钥管理方案Kubernetes Secretskubectl create secret generic auth-secrets \ --from-literalJWT_SECRETyour-256-bit-secret \ --from-literalDB_PASSWORDpostgresHashiCorp Vault集成Configuration public class VaultConfig { Value(${vault.uri}) private String vaultUri; Bean public VaultTemplate vaultTemplate() { TokenAuthentication token new TokenAuthentication(System.getenv(VAULT_TOKEN)); VaultEndpoint endpoint VaultEndpoint.from(URI.create(vaultUri)); return new VaultTemplate(endpoint, token); } } Service RequiredArgsConstructor public class SecretService { private final VaultTemplate vaultTemplate; public String getJwtSecret() { VaultResponse response vaultTemplate.read(secret/data/auth/jwt); return response.getData().get(secret).toString(); } }12. 升级与迁移策略12.1 Spring Boot 2.x到3.x迁移关键变更处理Jakarta EE 9更新所有javax包引用为jakarta包检查第三方库兼容性Spring Security 6变更// 旧版配置 http .authorizeRequests() .antMatchers(/public/**).permitAll() .anyRequest().authenticated(); // 新版配置 http .authorizeHttpRequests(auth - auth .requestMatchers(/public/**).permitAll() .anyRequest().authenticated() );JWT依赖更新!-- 旧版 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency !-- 新版 -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency12.2 令牌格式迁移实现新旧令牌格式兼容public Claims extractAllClaims(String token) { try { // 尝试用新格式解析 return parseNewFormatToken(token); } catch (JwtException e) { try { // 尝试用旧格式解析 return parseLegacyToken(token); } catch (J

相关新闻

DDR3软件调平实战:原理、流程与TI平台调试指南

DDR3软件调平实战:原理、流程与TI平台调试指南

1. 项目概述:为什么DDR3初始化需要“软件调平”?在嵌入式系统开发,尤其是基于TI DaVinci或Sitara这类高性能SoC的项目里,DDR3内存的初始化从来都不是一个简单的“上电、写几个寄存器、然后就能跑”的过程。如果你手头的板子是官方…

2026/10/18 14:27:55 阅读更多 →
杰理之配置SDPG为SD卡供电脚【篇】

杰理之配置SDPG为SD卡供电脚【篇】

升级时无法维持

2026/10/17 22:51:12 阅读更多 →
内生增长导向下 智圣新创一表通解决方案破解全国高校信息化建设落地困局

内生增长导向下 智圣新创一表通解决方案破解全国高校信息化建设落地困局

随着教育数字化战略行动进入深水区,全国高校的信息化建设已经从单点系统部署转向全域数据治理的新阶段,表单填报类工具作为打通数据源头的核心载体,已经成为绝大多数院校数字化建设的标配模块。智圣新创一表通解决方案针对行业普遍存在的“越…

2026/10/18 6:59:13 阅读更多 →

最新新闻

智能体接入工程:大模型在金融交易中的安全可控实践

智能体接入工程:大模型在金融交易中的安全可控实践

实际上手之前我们都觉得,把大模型智能体接进金融交易流程,最难的应该是模型效果。结果等项目真正推下去才发现,模型本身的推理能力反而不是瓶颈,周围那圈工程才是真正决定生死的东西。这圈工程,在这个项目里我们内部叫…

2026/10/18 18:13:54 阅读更多 →
KGCN知识图谱推荐系统实战:TensorFlow手写图卷积层

KGCN知识图谱推荐系统实战:TensorFlow手写图卷积层

简介:本资源是一个基于Python实现的高分毕业设计级电影推荐系统,融合知识图谱(KG)与图神经网络(KGCN),面向人工智能、推荐系统方向的本科生与研究生,解决传统协同过滤在冷启动与可解…

2026/10/18 18:13:54 阅读更多 →
Octop任务编排框架:中心调度与分布式执行架构解析

Octop任务编排框架:中心调度与分布式执行架构解析

1. 从“Octop”这个名字说起:它到底是什么第一次看到“Octop”这个词,我脑子里蹦出来的第一反应是“Octopus”——章鱼。八条腕足、分布式神经系统、极强的环境适应能力,这几个特征凑在一起,恰好能解释为什么有人会拿它来命名一个…

2026/10/18 18:13:54 阅读更多 →
生成对抗网络原始论文精读:目标函数、理论证明与稳定训练实战

生成对抗网络原始论文精读:目标函数、理论证明与稳定训练实战

生成对抗网络这个方向,我在第一次读到《Generative Adversarial Nets》的时候并没有立刻觉得它“惊艳”。当时最直观的感觉是:原来生成模型还能这样定义——不直接去逼近数据分布,而是让两个网络互相“逼”着对方提高。这篇论文我前后读了三遍…

2026/10/18 18:13:54 阅读更多 →
CAD/CAM核心原理与数控加工落地实践:从建模、刀轨到G代码全流程解析

CAD/CAM核心原理与数控加工落地实践:从建模、刀轨到G代码全流程解析

做机械加工这行,绕不开的一对搭档就是CAD(计算机辅助设计)和CAM(计算机辅助制造)。搞懂这对搭档,基本就拿到了现代数字化加工的入场券。很多刚入行的朋友容易把CAD/CAM当成一个工具软件的名字,其…

2026/10/18 18:13:54 阅读更多 →
华为ONT固件编辑GUI工具:支持头部重写、CRC校验与分区表可视化

华为ONT固件编辑GUI工具:支持头部重写、CRC校验与分区表可视化

简介:HWFW_GUI是一款面向通信设备开发与逆向分析工程师的华为ONT固件可视化编辑工具,基于C开发,专用于解析、浏览、修改及导出华为光猫(ONT)固件镜像中的子项目(如uboot、kernel、rootfs等)&…

2026/10/18 18:12:53 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →