Spring WebClient调用OAuth2令牌接口返回401 UNAUTHORIZED问题排查
问题根因汇总
你遇到的所有报错都是三个核心问题导致的:
- 最初的401错误:你将
x-www-form-urlencoded格式的身份参数放在了请求头中,而OAuth2令牌接口要求参数必须放在请求体中,服务端读取不到参数直接返回鉴权失败。 - 不支持
application/x-www-form-urlencoded媒体类型错误:Spring WebClient默认没有提供将自定义POJO序列化为表单格式的编码器,直接传自定义对象会触发序列化失败。 - 反序列化错误:你定义的
TokenResponse类和接口实际返回的JSON字段不匹配,OAuth2标准令牌响应没有result字段,Kotlin非空类型要求字段必须有值,找不到对应字段就会抛出空参数异常。
修复步骤
1. 修正表单参数传递逻辑
WebClient提交x-www-form-urlencoded格式表单有两种正确写法,任选一种即可:
写法1:使用FormInserter直接构造参数
import org.springframework.web.reactive.function.BodyInserters import org.springframework.http.MediaType @Service class TokenService(private val webClient: WebClient) { fun postAsynchronous(): Mono<TokenResponse> = webClient .post() .uri("https://mycomp.url/api/oauth/token") .contentType(MediaType.APPLICATION_FORM_URLENCODED) .body( BodyInserters.fromFormData("grant_type", "client_credentials") .with("client_id", "xxx") .with("client_secret", "yyy") ) .retrieve() .onStatus(HttpStatus::is4xxClientError) { Mono.error(RuntimeException("4XX Error ${it.statusCode()}")) } .onStatus(HttpStatus::is5xxServerError) { Mono.error(RuntimeException("5XX Error ${it.statusCode()}")) } .bodyToMono(TokenResponse::class.java) }
写法2:使用MultiValueMap传递参数
import org.springframework.util.LinkedMultiValueMap import org.springframework.util.MultiValueMap val formData: MultiValueMap<String, String> = LinkedMultiValueMap() formData.add("grant_type", "client_credentials") formData.add("client_id", "xxx") formData.add("client_secret", "yyy") // 传入body .bodyValue(formData)
2. 修正TokenResponse字段映射
首先确认接口实际返回的JSON结构,OAuth2标准令牌返回字段为access_token、token_type、expires_in等,你需要根据实际返回调整类定义:
方案A:和返回字段直接匹配
data class TokenResponse ( val access_token: String, val token_type: String, val expires_in: Long, // 不确定是否返回的字段设为可空类型,避免反序列化失败 val scope: String? = null )
方案B:用@JsonProperty映射自定义字段名
如果你要保留result作为字段名,添加注解映射实际返回的字段即可:
import com.fasterxml.jackson.annotation.JsonProperty data class TokenResponse ( @JsonProperty("access_token") val result: String )
验证顺序
修改完成后先调用接口打印完整返回结果,确认字段匹配后再做反序列化,避免反复调试。
内容的提问来源于stack exchange,提问作者Jim C
相关产品推荐
相关产品推荐

