如何基于Google OAuth2实现API服务的JWT请求认证
解决方案:Spring Security OAuth2 转 JWT 实现API认证
问题根源
你当前的配置基于会话认证:浏览器登录后通过Cookie维护会话状态,所以能正常访问;但Postman用Google的Bearer Token请求时,服务端不识别该Token——既没配置OAuth2资源服务器验证第三方Token,也没生成自有JWT返回给客户端,导致401未授权。
实现步骤
1. 添加核心依赖
以Maven为例,引入OAuth2资源服务器和JWT相关依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-jose</artifactId> </dependency>
2. 配置资源服务器验证Google Token
在application.yml中补充资源服务器配置,让服务端能校验Google签发的Bearer Token:
spring: security: oauth2: client: registration: google: client-id: ${GOOGLE_CLIENT_ID} client-secret: ${GOOGLE_CLIENT_SECRET} resourceserver: jwt: issuer-uri: https://accounts.google.com
3. 自定义认证成功处理器生成自有JWT
OAuth2登录成功后,生成服务端自有JWT返回给客户端,替代会话机制:
@Component public class CustomAuthSuccessHandler implements OAuth2AuthenticationSuccessHandler { private final JwtEncoder jwtEncoder; public CustomAuthSuccessHandler(JwtEncoder jwtEncoder) { this.jwtEncoder = jwtEncoder; } @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, OAuth2AuthenticationToken authToken) throws IOException { // 从Google认证信息中获取用户邮箱,查询数据库中已创建的用户 String userEmail = authToken.getPrincipal().getName(); User dbUser = getUserByEmail(userEmail); // 构建JWT声明 Instant now = Instant.now(); JWTClaimsSet claims = JWTClaimsSet.builder() .issuer("your-api-service") .subject(dbUser.getId().toString()) .issuedAt(now) .expiresAt(now.plusSeconds(36000)) // 10小时有效期 .claim("email", dbUser.getEmail()) .build(); // 生成JWT Token JwtEncoderParameters params = JwtEncoderParameters.from(JWSHeader.with(Algorithm.HS256).build(), claims); String jwtToken = jwtEncoder.encode(params).getTokenValue(); // 返回Token给客户端(JSON格式) response.setContentType(MediaType.APPLICATION_JSON_VALUE); new ObjectMapper().writeValue(response.getWriter(), Map.of("access_token", jwtToken)); } // 替换为你实际的数据库查询逻辑 private User getUserByEmail(String email) { // 示例:从数据库查询用户 return new User(); } }
4. 配置JWT编码器
配置签名JWT的编码器(生产环境建议改用非对称加密,如RSA):
@Configuration public class JwtConfig { @Value("${jwt.secret}") private String secretKey; @Bean public JwtEncoder jwtEncoder() { SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256"); return new NimbusJwtEncoder(new ImmutableSecret<>(secretSpec)); } }
在application.yml中添加JWT密钥:
jwt: secret: your-strong-32-character-secret-key-here # 生产环境需用安全方式管理密钥
5. 更新SecurityFilterChain配置
禁用会话,启用无状态JWT认证,并绑定自定义成功处理器:
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomAuthSuccessHandler authSuccessHandler; public SecurityConfig(CustomAuthSuccessHandler authSuccessHandler) { this.authSuccessHandler = authSuccessHandler; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) // API服务通常禁用CSRF .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 无状态,不依赖会话 .authorizeHttpRequests(auth -> auth .requestMatchers("/login/**").permitAll() .anyRequest().authenticated() ) .oauth2Login(oauth2 -> oauth2 .successHandler(authSuccessHandler) // 登录成功返回JWT ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter())) // 转换JWT为认证信息 ); return http.build(); } // 自定义JWT转换器,将声明转为权限信息 private JwtAuthenticationConverter jwtConverter() { JwtGrantedAuthoritiesConverter authorityConverter = new JwtGrantedAuthoritiesConverter(); authorityConverter.setAuthorityPrefix("ROLE_"); JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); converter.setJwtGrantedAuthoritiesConverter(authorityConverter); return converter; } }
6. 客户端API请求方式
用户通过/login完成Google登录后,会收到服务端返回的JWT Token。后续请求API时,在请求头中携带:
Authorization: Bearer <your-api-jwt-token>
服务端将验证该Token并完成认证。
内容的提问来源于stack exchange,提问作者bosowski
相关产品推荐
相关产品推荐

