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

Spring Security中OpenID/SAML如何传递初始请求参数至最终重定向

Spring Security 传递初始请求查询参数到最终重定向地址的实现方案

Spring Security 完全支持在服务发起式认证流(OpenID/SAML)中保留初始请求的查询参数,核心思路是在发起授权请求时保存参数,认证成功后取出并拼接回最终重定向URL,以下是两种场景的简便实现方式:

一、OpenID Connect 场景实现

1. 自定义授权请求解析器,保存初始查询参数

通过自定义OAuth2AuthorizationRequestResolver,将初始请求的查询参数添加到授权请求的额外参数中并存入会话:

@Bean
public OAuth2AuthorizationRequestResolver customOidcAuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
    DefaultOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(
            clientRegistrationRepository,
            OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI
    );
    
    resolver.setAuthorizationRequestCustomizer(request -> {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return;
        
        HttpServletRequest servletRequest = attributes.getRequest();
        // 提取初始请求的所有查询参数
        Map<String, String> queryParams = servletRequest.getParameterMap().entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue()[0]));
        
        // 将参数添加到授权请求的额外参数中
        request.additionalParameters(queryParams);
    });
    
    return resolver;
}

2. 自定义认证成功处理器,拼接参数到重定向URL

重写SimpleUrlAuthenticationSuccessHandler,从会话中取出保存的参数,拼接回最终重定向地址:

@Bean
public AuthenticationSuccessHandler customOidcSuccessHandler() {
    return new SimpleUrlAuthenticationSuccessHandler() {
        @Override
        protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
            String targetUrl = super.determineTargetUrl(request, response, authentication);
            
            if (authentication instanceof OAuth2AuthenticationToken oauthToken) {
                // 从会话中获取之前保存的授权请求
                OAuth2AuthorizationRequest authRequest = (OAuth2AuthorizationRequest) request.getSession()
                        .getAttribute(OAuth2AuthorizationRequestRepository.DEFAULT_AUTHORIZATION_REQUEST_ATTR_NAME);
                
                if (authRequest != null && !authRequest.getAdditionalParameters().isEmpty()) {
                    // 拼接参数到目标URL
                    UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(targetUrl);
                    authRequest.getAdditionalParameters().forEach(uriBuilder::queryParam);
                    targetUrl = uriBuilder.build().toUriString();
                }
            }
            
            return targetUrl;
        }
    };
}

3. 配置SecurityFilterChain,注册自定义组件

将上述组件配置到Spring Security的过滤器链中:

@Bean
public SecurityFilterChain oidcSecurityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .oauth2Login(oauth2 -> oauth2
            .authorizationEndpoint(endpoint -> endpoint
                .authorizationRequestResolver(customOidcAuthorizationRequestResolver(clientRegistrationRepository())))
            .successHandler(customOidcSuccessHandler())
        );
    
    return http.build();
}

二、SAML2 场景实现

SAML2 可利用relayState参数传递自定义数据,实现逻辑类似:

1. 自定义SAML认证请求解析器,保存参数到relayState

@Bean
public Saml2AuthenticationRequestResolver customSamlAuthenticationRequestResolver(RelyingPartyRegistrationRepository repo) {
    DefaultSaml2AuthenticationRequestResolver resolver = new DefaultSaml2AuthenticationRequestResolver(repo);
    
    resolver.setAuthenticationRequestCustomizer(request -> {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return;
        
        HttpServletRequest servletRequest = attributes.getRequest();
        Map<String, String> queryParams = servletRequest.getParameterMap().entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue()[0]));
        
        // 将参数序列化为JSON后存入relayState
        try {
            String relayState = new ObjectMapper().writeValueAsString(queryParams);
            request.setRelayState(relayState);
        } catch (JsonProcessingException e) {
            // 异常处理逻辑
        }
    });
    
    return resolver;
}

2. 自定义认证成功处理器,从relayState取出参数拼接URL

@Bean
public AuthenticationSuccessHandler customSamlSuccessHandler() {
    return new SimpleUrlAuthenticationSuccessHandler() {
        @Override
        protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
            String targetUrl = super.determineTargetUrl(request, response, authentication);
            
            if (authentication instanceof Saml2AuthenticationToken samlToken) {
                String relayState = samlToken.getRelayState();
                if (relayState == null) return targetUrl;
                
                try {
                    // 反序列化relayState中的参数
                    Map<String, String> queryParams = new ObjectMapper()
                            .readValue(relayState, new TypeReference<Map<String, String>>() {});
                    
                    // 拼接参数到目标URL
                    UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(targetUrl);
                    queryParams.forEach(uriBuilder::queryParam);
                    targetUrl = uriBuilder.build().toUriString();
                } catch (JsonProcessingException e) {
                    // 异常处理逻辑
                }
            }
            
            return targetUrl;
        }
    };
}

3. 配置SAML2的SecurityFilterChain

@Bean
public SecurityFilterChain samlSecurityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .saml2Login(saml2 -> saml2
            .authenticationRequestResolver(customSamlAuthenticationRequestResolver(relyingPartyRegistrationRepository()))
            .successHandler(customSamlSuccessHandler())
        );
    
    return http.build();
}

核心原理说明

  1. 参数保存阶段:在跳转至身份提供商(IdP)前,将初始请求的查询参数存入授权请求的额外参数(OpenID)或SAML的relayState中,Spring Security会自动处理这些数据的传递(OpenID通过state参数加密传递,SAML直接通过RelayState参数传递)。
  2. 参数恢复阶段:认证成功后,从认证令牌或会话中取出保存的参数,拼接回最终的重定向URL,实现参数的全程传递。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 05:04:51