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
相关产品推荐
相关产品推荐

