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

Spring Boot资源服务器对接Azure AD的JWT验证问题求助

解决Azure AD JWT验证的Spring Boot资源服务器问题

第一个错误:无法解析Issuer配置

你之前用的issuer-uri是WS-Federation协议的地址(带wsfed后缀),这根本不是OIDC/JWT的Issuer端点,Spring Security无法通过这个地址获取JWT验证所需的OIDC元数据(比如公钥地址、token格式等),所以才会报错Unable to resolve the Configuration with the provided Issuer。

正确的Issuer URI

Azure AD的JWT Issuer分v1和v2两个版本,必须和前端获取的JWT里的iss字段完全匹配:

  • v2.0端点:https://login.microsoftonline.com/{你的租户ID}/v2.0(对应v2版本的应用注册)
  • v1.0端点:https://login.microsoftonline.com/{你的租户ID}/(对应v1版本的应用注册)

你已经改成v2的端点,这步是对的,但出现401需要排查以下几点:


401 Unauthorized 问题排查步骤

1. 验证JWT的核心字段

用jwt.io解析前端获取的token,检查:

  • iss字段:必须和你配置的issuer-uri完全一致(注意v2的iss末尾带/v2.0,v1不带)
  • scp字段:确认是否包含user.read——你的配置要求hasAnyAuthority("SCOPE_user.read"),Spring Security会自动给token里的scope加上SCOPE_前缀,所以token里必须有user.read这个scope
  • exp字段:确保token未过期
  • aud字段:必须是你的后端资源服务器的客户端ID(Azure AD应用注册里的"应用程序(客户端) ID")

2. 检查请求头是否正确

前端发起请求时,必须把JWT放在Authorization头里,格式为:

Bearer <你的JWT令牌>

可以用浏览器开发者工具或Postman确认请求头是否正确携带。

3. 调整权限配置(如果需要)

如果你的权限是Azure AD的应用角色(不是用户委托的scope),那么token里的权限会在roles字段中,此时需要修改配置:

.authorizeHttpRequests{
    authorize -> authorize.antMatchers("/**").hasAnyAuthority("user.read") // 去掉SCOPE_前缀,或者用hasRole("user.read")(Spring会自动加ROLE_前缀,需对应)
    .anyRequest().authenticated()
}

4. 手动配置JwtDecoder(解决自动发现问题)

如果Spring Security自动通过Issuer URI获取元数据失败,可以手动指定公钥地址:

@Bean
fun jwtDecoder(): JwtDecoder {
    val jwkSetUri = "https://login.microsoftonline.com/{你的租户ID}/discovery/v2.0/keys"
    return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build()
}

此时可以在application.yml中去掉issuer-uri配置,或者保留以启用issuer验证。

5. 确认租户ID和应用配置

  • 确保{你的租户ID}已经替换成实际的Azure AD租户ID(不是占位符)
  • 后端的应用注册(资源服务器)必须在Azure AD中配置正确的"公开API"范围,且前端应用已经获得该范围的授权

最终配置示例

application.yml(v2版本)

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://login.microsoftonline.com/3d1f9219-b455-xxxx-xxxx-xxxxxxxxxxxx/v2.0

Kotlin FilterChain(保持原有逻辑,确保权限匹配)

@Bean
open fun filterChain(http: HttpSecurity) : SecurityFilterChain{
    http
        .authorizeHttpRequests{ authorize ->
            authorize
                .antMatchers("/**").hasAnyAuthority("SCOPE_user.read")
                .anyRequest().authenticated()
        }
        .oauth2ResourceServer{ oauth2 ->
            oauth2.jwt(Customizer.withDefaults())
        }
    return http.build()
}

内容的提问来源于stack exchange,提问作者PlaeschZ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:35:19