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

Spring Authorization Server无法通过JSON请求体获取令牌如何解决

解决方案

1. 实现JSON请求体传客户端凭据的配置

Spring Authorization Server 默认仅支持Basic认证和application/x-www-form-urlencoded格式的POST参数传递客户端凭据,不原生支持JSON请求体格式,需要自定义认证逻辑实现,步骤如下:

步骤1:自定义JSON客户端认证转换器

实现AuthenticationConverter接口,用于从JSON请求体中解析client_id和client_secret参数,构造客户端认证令牌:

import javax.servlet.http.HttpServletRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.authentication.ClientSecretAuthenticationToken;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.StreamUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Map;

public class JsonClientAuthenticationConverter implements AuthenticationConverter {
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public Authentication convert(HttpServletRequest request) {
        if (!"application/json".equals(request.getContentType())) {
            return null;
        }
        try {
            byte[] body = StreamUtils.copyToByteArray(request.getInputStream());
            Map<String, String> requestBody = objectMapper.readValue(body, Map.class);
            String clientId = requestBody.get(OAuth2ParameterNames.CLIENT_ID);
            String clientSecret = requestBody.get(OAuth2ParameterNames.CLIENT_SECRET);
            if (clientId == null || clientSecret == null) {
                return null;
            }
            return new ClientSecretAuthenticationToken(clientId, ClientAuthenticationMethod.CLIENT_SECRET_POST, clientSecret, null);
        } catch (IOException e) {
            throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
        }
    }
}

步骤2:调整授权服务器配置

替换原有@Import的默认配置,自定义授权服务器安全链,加入自定义的JSON转换器,同时关闭查询参数传凭据的特性:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretPostAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import java.util.List;

@Configuration
public class AuthorizationServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        OAuth2AuthorizationServerConfigurer configurer = http.getConfigurer(OAuth2AuthorizationServerConfigurer.class);
        
        // 注册JSON格式的客户端认证转换器
        configurer.clientAuthentication(clientAuth -> 
            clientAuth.authenticationConverter(
                new DelegatingAuthenticationConverter(List.of(
                    new ClientSecretBasicAuthenticationConverter(),
                    new ClientSecretPostAuthenticationConverter(),
                    new JsonClientAuthenticationConverter()
                ))
            )
        );

        // 禁用查询参数传递客户端凭据,符合OAuth2.1规范要求
        configurer.tokenEndpoint(tokenEndpoint -> tokenEndpoint.allowQueryParameters(false));

        return http.build();
    }

    @Bean
    @Primary
    public RegisteredClientRepository registeredClientRepository(UserRepository repository) {
        return repository;
    }
}

注:如果你使用的是2021年对应的早期0.x版本Spring Authorization Server,allowQueryParameters方法可能尚未提供,可通过自定义过滤器拦截令牌端点请求,检测到查询参数中存在client_id或client_secret时直接返回错误即可。

2. 关于Spring Authorization Server默认支持URI参数传凭据的说明

Spring Authorization Server 初始版本设计时优先兼容OAuth 2.0 RFC6749规范,该规范仅不推荐在查询参数中传递敏感信息,未做强制禁止,因此默认开启了该特性。OAuth 2.1 规范后续才明确要求禁止在查询参数中传递客户端凭据,你可以通过上述配置中的tokenEndpoint.allowQueryParameters(false)关闭该特性,完全对齐OAuth 2.1的安全要求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 12:06:01