React+Spring Boot中从Onelogin的id_token获取用户详情的正确方式
问题分析与优化方案
当前实现的核心问题
- 严重安全漏洞:自定义过滤器仅手动解码JWT payload,未做签名验证和过期校验——任何人都可以伪造结构合法的id_token绕过认证,后端完全失去安全性。
- 未利用Spring Security内置能力:已配置
.oauth2ResourceServer().jwt(),但自定义过滤器绕开了官方JWT验证逻辑,导致Spring Security上下文未正确构建,因此控制器的Principal无用户详情。 - 不规范的用户信息传递:用
request.setAttribute传递用户信息不符合Spring Security的标准流程,不利于统一管理和后续扩展。
优化后的正确实现
1. 依赖配置(Maven)
确保引入Spring Security OAuth2资源服务器依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
2. 修正Spring Security配置
利用官方JWT验证能力,配置OneLogin的OIDC元数据地址,自动处理签名验证、过期检查:
@Configuration @EnableWebSecurity public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter { private final Logger logger = LoggerFactory.getLogger(this.getClass()); // 从配置文件读取OneLogin的OIDC issuer地址(可在OneLogin控制台应用设置中获取) @Value("${onelogin.issuer-uri}") private String issuerUri; @Override protected void configure(HttpSecurity http) throws Exception { logger.info("In JwtSecurityConfig"); http.csrf() .disable() .authorizeRequests() .antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/security", "/swagger-ui.html", "/swagger-ui/**", "/webjars/**").permitAll() .anyRequest().authenticated() .and() .oauth2ResourceServer() .jwt() .jwtAuthenticationConverter(jwtAuthenticationConverter()); } // 自动从OneLogin的JWKS端点获取公钥,验证JWT签名 @Bean public JwtDecoder jwtDecoder() { return JwtDecoders.fromIssuerLocation(issuerUri); } // 自定义JWT声明到Authentication的映射逻辑 private JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter(); // 如需从JWT字段获取权限,可在此配置,例如:authoritiesConverter.setAuthorityPrefix("ROLE_"); JwtAuthenticationConverter converter = new JwtAuthenticationConverter(); converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter); // 设置principal为JWT中的email字段,也可使用sub(用户唯一ID)或name converter.setPrincipalClaimName("email"); return converter; } }
3. 移除自定义过滤器
删除JwtTokenAuthorizationOncePerRequestFilter,所有JWT验证、用户信息解析由Spring Security自动完成。
4. 控制器中获取用户详情
通过@AuthenticationPrincipal注解直接获取JWT对象,或强转Principal为JwtAuthenticationToken:
import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/user") public class UserController { @GetMapping public UserInfo getUserInfo(@AuthenticationPrincipal Jwt jwt) { // 直接从JWT声明中提取所需信息 String email = jwt.getClaimAsString("email"); String name = jwt.getClaimAsString("name"); String userId = jwt.getClaimAsString("sub"); return new UserInfo(email, name, userId); } // 自定义返回的用户信息DTO static class UserInfo { private String email; private String name; private String userId; public UserInfo(String email, String name, String userId) { this.email = email; this.name = name; this.userId = userId; } // getter/setter 省略 } }
或者使用Principal的方式:
@GetMapping public Principal getUser(Principal principal) { JwtAuthenticationToken jwtToken = (JwtAuthenticationToken) principal; Jwt jwt = jwtToken.getToken(); // 提取所需信息 String email = jwt.getClaimAsString("email"); System.out.println("当前用户邮箱:" + email); return principal; }
5. 前端注意事项
保持原有逻辑,将id_token放在请求头Authorization: Bearer <id_token>中即可,后端会自动验证令牌合法性。
内容的提问来源于stack exchange,提问作者Manish
相关产品推荐
相关产品推荐

