Spring Security OAuth2 授权码模式自定义 Redirect URL 重定向路径和 AuthorizationRequestBaseUri 跳转登录页的处理 URL

Spring Security OAuth2 Spring Boot About 6,894 words

说明

本文以spring-boot-starter-oauth2-client 3.1.5Keycloak为例。

默认跳转到登录页路径

OAuth2AuthorizationRequestRedirectFilter过滤器中的DEFAULT_AUTHORIZATION_REQUEST_BASE_URI常量

public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilter {

    public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";

}

默认登录成功跳转路径

OAuth2LoginAuthenticationFilter过滤器中的DEFAULT_FILTER_PROCESSES_URI常量。

public class OAuth2LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";

}

自定义路径

配置文件

配置文件中指定redirect-uri,默认的认证成功后重定向的地址是{baseUrl}/{action}/oauth2/code/{registrationId}

spring:
  security:
    oauth2:
      client:
        registration:
          keycloak:
            provider: keycloak
            client-id: my-client
            client-secret: 
            redirect-uri: "{baseUrl}/api/oauth2/code/{registrationId}"
            scope:
              - openid

配置规则

DefaultOAuth2AuthorizationRequestResolver中配置的baseUriSpring Security OAuth2 Client处理跳转到Keycloak登录页面时生成参数的URL链接。

@Slf4j
@Configuration
@EnableWebSecurity
public class WebConfig {

    @Bean
    @Order(0)
    SecurityFilterChain securityFilterChain0(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
        http
                .securityMatcher( "/api/**", "/api/oauth2/**")
                .formLogin(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .anonymous(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(authorizeHttpRequests -> {
                    authorizeHttpRequests.anyRequest().authenticated();
                })
                .oauth2Login(login -> {
                    login.authorizationEndpoint(authorizationEndpoint -> {
                        DefaultOAuth2AuthorizationRequestResolver resolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, "/api/oauth2/authorization");
                        resolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce());
                        authorizationEndpoint.authorizationRequestResolver(resolver);
                        authorizationEndpoint.baseUri("/api/oauth2/authorization");
                    });
                    login.loginProcessingUrl("/api/oauth2/code/keycloak");
                })
        ;
        return http.build();
    }

}

源码

从源码中可以到{action}会被替换为默认的login,这样就是DEFAULT_FILTER_PROCESSES_URI中的/login/oauth2/code/*路径了。

private static ClientRegistration.Builder withProviderConfiguration(AuthorizationServerMetadata metadata, String issuer) {
    String metadataIssuer = metadata.getIssuer().getValue();
    String name = URI.create(issuer).getHost();
    ClientAuthenticationMethod method = getClientAuthenticationMethod(metadata.getTokenEndpointAuthMethods());
    Map<String, Object> configurationMetadata = new LinkedHashMap<>(metadata.toJSONObject());
    // @formatter:off
    return ClientRegistration.withRegistrationId(name)
            .userNameAttributeName(IdTokenClaimNames.SUB)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .clientAuthenticationMethod(method)
            .redirectUri("{baseUrl}/{action}/oauth2/code/{registrationId}") // 默认配置
            .authorizationUri((metadata.getAuthorizationEndpointURI() != null) ? metadata.getAuthorizationEndpointURI().toASCIIString() : null)
            .providerConfigurationMetadata(configurationMetadata)
            .tokenUri(metadata.getTokenEndpointURI().toASCIIString())
            .issuerUri(issuer)
            .clientName(issuer);
    // @formatter:on
}

public final class DefaultOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {

    @Override
    public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
        String registrationId = resolveRegistrationId(request);
        String redirectUriAction = getAction(request, "login");
        return resolve(request, registrationId, redirectUriAction);
    }

    private String getAction(HttpServletRequest request, String defaultAction) {
        String action = request.getParameter("action");
        if (action == null) {
            return defaultAction;
        }
        return action;
    }

    private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) {
        ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
        
        OAuth2AuthorizationRequest.Builder builder = getBuilder(clientRegistration);

        String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);

        // @formatter:off
        builder.clientId(clientRegistration.getClientId())
                .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
                .redirectUri(redirectUriStr)
                .scopes(clientRegistration.getScopes())
                .state(DEFAULT_STATE_GENERATOR.generateKey());
        // @formatter:on

        this.authorizationRequestCustomizer.accept(builder);

        return builder.build();
    }

    private static String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) {
        Map<String, String> uriVariables = new HashMap<>();
        uriVariables.put("registrationId", clientRegistration.getRegistrationId());
        // @formatter:off
        UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
                .replacePath(request.getContextPath())
                .replaceQuery(null)
                .fragment(null)
                .build();
        // @formatter:on
        String scheme = uriComponents.getScheme();
        uriVariables.put("baseScheme", (scheme != null) ? scheme : "");
        String host = uriComponents.getHost();
        uriVariables.put("baseHost", (host != null) ? host : "");
        // following logic is based on HierarchicalUriComponents#toUriString()
        int port = uriComponents.getPort();
        uriVariables.put("basePort", (port == -1) ? "" : ":" + port);
        String path = uriComponents.getPath();
        if (StringUtils.hasLength(path)) {
            if (path.charAt(0) != PATH_DELIMITER) {
                path = PATH_DELIMITER + path;
            }
        }
        uriVariables.put("basePath", (path != null) ? path : "");
        uriVariables.put("baseUrl", uriComponents.toUriString());
        uriVariables.put("action", (action != null) ? action : "");
        return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri())
            .buildAndExpand(uriVariables)
            .toUriString();
    }

}
Views: 208 · Posted: 2024-05-15

————        END        ————

Give me a Star, Thanks:)

https://github.com/fendoudebb/LiteNote

扫描下方二维码关注公众号和小程序↓↓↓

扫描下方二维码关注公众号和小程序↓↓↓


Today On History
Browsing Refresh