将Cognito作为Spring Authorization Server的身份提供商(IDP)方案咨询
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
相关产品推荐
相关产品推荐

