You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Spring Boot中从Onelogin的id_token获取用户详情的正确方式

问题分析与优化方案

当前实现的核心问题

  1. 严重安全漏洞:自定义过滤器仅手动解码JWT payload,未做签名验证和过期校验——任何人都可以伪造结构合法的id_token绕过认证,后端完全失去安全性。
  2. 未利用Spring Security内置能力:已配置.oauth2ResourceServer().jwt(),但自定义过滤器绕开了官方JWT验证逻辑,导致Spring Security上下文未正确构建,因此控制器的Principal无用户详情。
  3. 不规范的用户信息传递:用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:37:21