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

Spring Boot 2.7.6中OpenID Redirect URI编码问题及解决方案咨询

Spring Boot 2.7.6 OAuth2客户端Redirect URI编码问题及解决方案

问题场景

我在配置使用OpenID的Spring Boot应用时,遇到了Redirect URI未正确URL编码的问题。当前使用Java 11,只能基于Spring Boot 2.7.6开发。

我的application.yml配置如下:

spring:
  security:
    oauth2:
      client:
        registration:
          xxxx:
            client-id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
            client-secret: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
            authorization-grant-type: authorization_code
            redirect-uri: https://www.example.com/home
            scope:
              - openid
              - profile
              - email
        provider:
          xxxx:
            authorizationUri: https:...
            ...

但生成的授权URL中,redirect_uri参数未进行URL编码:

https://.....com/authorize
    ?response_type=code
    &client_id=...
    &scope=...
    &state=...D
    &redirect_uri=https://www.example.com/home   <---- 未URL编码
    &nonce=...

期望的编码后结果应为:

&redirect_uri=https%3A%2F%2Fwww.example.com%2Fhome   <---- 已编码

尝试过的无效操作

  • 手动在配置中对redirect-uri进行URL编码,结果Spring对其进行了二次编码,生成非法格式(%25是%的URL编码,导致整体为二次编码):
https://.....com/authorize
    ?response_type=code
    &client_id=...
    &scope=...
    &state=...D
    &redirect_uri=https%253A%252F%252Fexample.com%252Fhome   <---- 二次编码导致格式非法
    &nonce=...
  • 省略redirect-uri配置并尝试在授权URI中硬编码,Spring直接提示该参数为必填项。
  • 身份提供商不支持自动发现,无法通过OIDC自动配置解决。

针对Spring Boot 2.7.6的解决方案

可以通过自定义OAuth2AuthorizationRequestResolver手动处理redirect_uri的编码逻辑,避免框架的编码异常:

  1. 创建自定义授权请求解析器类:
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public class CustomAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {

    private final OAuth2AuthorizationRequestResolver delegate;

    public CustomAuthorizationRequestResolver(OAuth2AuthorizationRequestResolver delegate) {
        this.delegate = delegate;
    }

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
        OAuth2AuthorizationRequest authorizationRequest = this.delegate.resolve(request);
        return customizeAuthorizationRequest(authorizationRequest);
    }

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
        OAuth2AuthorizationRequest authorizationRequest = this.delegate.resolve(request, clientRegistrationId);
        return customizeAuthorizationRequest(authorizationRequest);
    }

    private OAuth2AuthorizationRequest customizeAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {
        if (authorizationRequest == null) {
            return null;
        }

        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(authorizationRequest.getAuthorizationRequestUri());
        // 移除原未编码的redirect_uri参数
        uriBuilder.replaceQueryParam("redirect_uri");
        // 添加正确编码后的redirect_uri
        String encodedRedirectUri = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri())
                .build().encode().toUriString();
        uriBuilder.queryParam("redirect_uri", encodedRedirectUri);

        return OAuth2AuthorizationRequest.from(authorizationRequest)
                .authorizationRequestUri(uriBuilder.build().toUriString())
                .build();
    }
}
  1. 在Security配置类中注册自定义解析器:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated()
                )
                .oauth2Login(oauth2 -> oauth2
                        .authorizationEndpoint(authEndpoint -> authEndpoint
                                .authorizationRequestResolver(customAuthorizationRequestResolver(clientRegistrationRepository))
                        )
                );
        return http.build();
    }

    private OAuth2AuthorizationRequestResolver customAuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
        DefaultOAuth2AuthorizationRequestResolver defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(
                clientRegistrationRepository, "/oauth2/authorization");
        return new CustomAuthorizationRequestResolver(defaultResolver);
    }
}

该方案会在授权请求生成后,替换掉未编码的redirect_uri参数,用正确编码后的URL替代,彻底避免二次编码问题。

Spring 3.x中的修复情况

在Spring Boot 3.x(对应Spring Security 6.x)中,这个问题已经得到官方修复。框架会自动正确处理redirect_uri的URL编码,无需手动干预:配置原始的redirect-uri值后,框架会自动生成符合规范的编码后的参数,不会出现未编码或二次编码的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:07:20