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

将Cognito作为Spring Authorization Server的身份提供商(IDP)方案咨询

整合Spring Authorization Server与AWS Cognito实现自定义授权流程

1. 配置Spring Security对接Cognito OIDC认证

先通过OIDC协议让Spring Security对接Cognito,完成用户登录认证:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .clientRegistrationRepository(clientRegistrationRepository())
                .authorizedClientService(authorizedClientService())
                .loginPage("/oauth2/authorization/cognito")
            );
        return http.build();
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(cognitoClientRegistration());
    }

    private ClientRegistration cognitoClientRegistration() {
        return ClientRegistration.withRegistrationId("cognito")
            .clientId("YOUR_COGNITO_APP_CLIENT_ID")
            .clientSecret("YOUR_COGNITO_APP_CLIENT_SECRET")
            .authorizationUri("https://YOUR_COGNITO_DOMAIN/oauth2/authorize")
            .tokenUri("https://YOUR_COGNITO_DOMAIN/oauth2/token")
            .userInfoUri("https://YOUR_COGNITO_DOMAIN/oauth2/userInfo")
            .redirectUri("{baseUrl}/login/oauth2/code/cognito")
            .scope("openid", "email", "profile")
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .clientName("Cognito")
            .userNameAttributeName(IdTokenClaimNames.SUB)
            .build();
    }

    @Bean
    public OAuth2AuthorizedClientService authorizedClientService() {
        return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository());
    }
}

2. 自定义客户端存储,从Cognito用户池加载客户端

替换Spring Authorization Server默认的客户端存储逻辑,通过Cognito API拉取用户池内的客户端信息:

@Configuration
public class AuthorizationServerConfig {

    private final AWSCognitoIdentityProvider cognitoIdentityProvider;
    private final String userPoolId = "YOUR_COGNITO_USER_POOL_ID";

    public AuthorizationServerConfig(AWSCognitoIdentityProvider cognitoIdentityProvider) {
        this.cognitoIdentityProvider = cognitoIdentityProvider;
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        return new CognitoRegisteredClientRepository(cognitoIdentityProvider, userPoolId);
    }

    static class CognitoRegisteredClientRepository implements RegisteredClientRepository {

        private final AWSCognitoIdentityProvider cognitoClient;
        private final String userPoolId;

        public CognitoRegisteredClientRepository(AWSCognitoIdentityProvider cognitoClient, String userPoolId) {
            this.cognitoClient = cognitoClient;
            this.userPoolId = userPoolId;
        }

        @Override
        public RegisteredClient findById(String id) {
            DescribeUserPoolClientRequest request = new DescribeUserPoolClientRequest()
                .withUserPoolId(userPoolId)
                .withClientId(id);
            UserPoolClientDescription clientDesc = cognitoClient.describeUserPoolClient(request).getUserPoolClient();

            return RegisteredClient.withId(clientDesc.getClientId())
                .clientId(clientDesc.getClientId())
                .clientSecret(clientDesc.getClientSecret())
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri(clientDesc.getCallbackURLs().get(0))
                .scope("openid")
                .scope("email")
                .clientSettings(ClientSettings.builder()
                    .requireAuthorizationConsent(false)
                    .build())
                .tokenSettings(TokenSettings.builder()
                    .accessTokenTimeToLive(Duration.ofMinutes(60))
                    .refreshTokenTimeToLive(Duration.ofDays(7))
                    .build())
                .build();
        }

        @Override
        public RegisteredClient findByClientId(String clientId) {
            return findById(clientId);
        }

        @Override
        public void save(RegisteredClient registeredClient) {
            throw new UnsupportedOperationException("Cognito客户端需在用户池控制台创建");
        }
    }
}

3. 配置Spring Authorization Server核心授权流程

启用授权码流程,并关联Cognito的认证结果:

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
        .authorizationEndpoint(auth -> auth
            .authenticationProviders(authenticationProviders())
        )
        .tokenEndpoint(token -> token
            .accessTokenResponseHandler(cognitoTokenResponseHandler())
        );

    http.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
    return http.build();
}

@Bean
public AuthenticationProvider authenticationProvider() {
    return new OAuth2AuthenticationProvider();
}

private List<AuthenticationProvider> authenticationProviders() {
    return Collections.singletonList(authenticationProvider());
}

4. 自定义令牌响应,返回Cognito原生令牌

当客户端用授权码兑换令牌时,代理请求Cognito的token端点,直接返回Cognito生成的令牌:

@Bean
public OAuth2AccessTokenResponseHandler cognitoTokenResponseHandler() {
    return (request, responseContext) -> {
        String authorizationCode = request.getAuthorizationCode().getTokenValue();
        RegisteredClient registeredClient = responseContext.getRegisteredClient();

        MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
        params.add("grant_type", "authorization_code");
        params.add("code", authorizationCode);
        params.add("client_id", registeredClient.getClientId());
        params.add("client_secret", registeredClient.getClientSecret());
        params.add("redirect_uri", request.getRedirectUri());

        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(params, headers);

        ResponseEntity<CognitoTokenResponse> cognitoResponse = restTemplate.postForEntity(
            "https://YOUR_COGNITO_DOMAIN/oauth2/token",
            entity,
            CognitoTokenResponse.class
        );

        CognitoTokenResponse tokenResponse = cognitoResponse.getBody();
        return OAuth2AccessTokenResponse.withToken(tokenResponse.access_token())
            .tokenType(OAuth2TokenType.BEARER)
            .expiresIn(tokenResponse.expires_in())
            .refreshToken(tokenResponse.refresh_token())
            .additionalParameters(Map.of(
                "id_token", tokenResponse.id_token(),
                "token_type", tokenResponse.token_type()
            ))
            .build();
    };
}

record CognitoTokenResponse(
    String access_token,
    String id_token,
    String refresh_token,
    String token_type,
    long expires_in
) {}

关键注意事项

  • 确保Cognito用户池客户端的回调URL包含Spring Authorization Server的OIDC回调地址(如http://localhost:8080/login/oauth2/code/cognito)以及业务客户端的回调地址。
  • 配置AWS SDK凭证,保证能正常调用Cognito用户池API(可通过环境变量、IAM角色或本地配置文件设置)。
  • 若需自定义授权页面,可通过authorizationEndpoint().authorizationRequestResolver()实现,识别发起授权请求的客户端。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:54:54