如何为Spring Boot应用集成Amazon Cognito身份池实现Spring Security认证
嘿,刚好做过类似的场景,我来一步步给你讲清楚怎么在Spring Boot里验证这些AWS临时凭证,完成用户认证~
核心思路
我们要做的是验证客户端传来的临时AWS凭证的合法性,同时获取凭证对应的用户/角色身份信息,这样就能把它集成到Spring Boot的认证流程里,控制接口的访问权限。AWS的STS(Security Token Service)提供了GetCallerIdentity接口,专门用来干这件事——它能验证凭证是否有效,还能返回凭证关联的用户ID、ARN等关键信息。
具体步骤
1. 引入AWS SDK依赖
首先在你的Spring Boot项目里添加STS的SDK依赖,Maven的话在pom.xml里加:
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>sts</artifactId> <version>2.20.0</version> <!-- 建议用最新稳定版 --> </dependency>
Gradle的话对应配置:
implementation 'software.amazon.awssdk:sts:2.20.0'
2. 编写凭证验证工具类
创建一个工具类,用客户端传来的临时凭证初始化STS客户端,调用GetCallerIdentity接口做验证:
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.sts.StsClient; import software.amazon.awssdk.services.sts.model.GetCallerIdentityResponse; import org.springframework.stereotype.Component; @Component public class AwsCredentialValidator { public GetCallerIdentityResponse validateTempCredentials(String accessKeyId, String secretAccessKey, String sessionToken) { // 用客户端的临时凭证创建会话凭证对象 AwsSessionCredentials tempSessionCreds = AwsSessionCredentials.create( accessKeyId, secretAccessKey, sessionToken ); // 初始化STS客户端 try (StsClient stsClient = StsClient.builder() .credentialsProvider(StaticCredentialsProvider.create(tempSessionCreds)) .region(Region.US_EAST_1) // 换成你的AWS区域,也可以让客户端传区域 .build()) { // 调用GetCallerIdentity验证凭证,有效则返回身份信息 return stsClient.getCallerIdentity(); } } }
注意:如果凭证无效(比如过期、错误),STS客户端会抛出对应的异常(比如
InvalidSessionTokenException、SdkException),我们后面可以捕获这些异常来判定认证失败。
3. 集成到Spring Security认证流程
接下来把验证逻辑加到Spring Security的过滤器里,这样每个请求都会先验证凭证:
3.1 自定义认证过滤器
创建一个OncePerRequestFilter,从请求头里拿凭证,调用工具类验证,验证通过后把用户信息放到Security上下文:
import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; import software.amazon.awssdk.services.sts.model.GetCallerIdentityResponse; import software.amazon.awssdk.services.sts.model.StsException; import java.io.IOException; import java.util.Collections; public class AwsTempCredentialAuthFilter extends OncePerRequestFilter { private final AwsCredentialValidator credentialValidator; public AwsTempCredentialAuthFilter(AwsCredentialValidator credentialValidator) { this.credentialValidator = credentialValidator; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 从请求头获取凭证(可以自己约定头字段名) String accessKey = request.getHeader("X-AWS-Access-Key"); String secretKey = request.getHeader("X-AWS-Secret-Key"); String sessionToken = request.getHeader("X-AWS-Session-Token"); // 检查凭证是否存在,且当前没有已认证的用户 if (accessKey != null && secretKey != null && sessionToken != null && SecurityContextHolder.getContext().getAuthentication() == null) { try { // 验证凭证并获取身份信息 GetCallerIdentityResponse identity = credentialValidator.validateTempCredentials(accessKey, secretKey, sessionToken); // 把身份信息封装成UserDetails(这里可以自定义你的UserDetails实现,包含更多信息) UserDetails userDetails = User.withUsername(identity.userId()) .password("") // 临时凭证不需要密码,这里填空或者固定值 .authorities(Collections.emptyList()) // 可以根据ARN或角色配置权限 .build(); // 创建认证Token并放到Security上下文 UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities() ); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } catch (StsException e) { // 凭证验证失败,返回401 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write(String.format("Invalid AWS credentials: %s", e.getMessage())); return; } } // 继续执行后续过滤器 filterChain.doFilter(request, response); } }
3.2 配置Spring Security
把自定义过滤器加到Spring 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.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity public class SecurityConfig { private final AwsCredentialValidator credentialValidator; public SecurityConfig(AwsCredentialValidator credentialValidator) { this.credentialValidator = credentialValidator; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(csrf -> csrf.disable()) // REST接口通常关闭CSRF .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() // 所有请求都需要认证 ) // 把自定义过滤器加到UsernamePasswordAuthenticationFilter前面 .addFilterBefore(new AwsTempCredentialAuthFilter(credentialValidator), UsernamePasswordAuthenticationFilter.class); return http.build(); } }
4. 优化和注意事项
- 凭证传递方式:建议用请求头传递凭证,比URL参数更安全;如果是HTTPS的话,凭证会被加密传输,不用担心泄露。
- 异常处理:除了
StsException,还要考虑其他可能的异常(比如网络问题),可以统一捕获SdkException来处理所有AWS SDK相关的错误。 - 缓存优化:临时凭证有过期时间(可以从客户端的凭证里拿到
expiration字段),可以缓存验证后的用户信息,减少STS的调用次数,提升接口性能。 - 权限控制:你可以根据
GetCallerIdentityResponse返回的arn或userId,结合你的业务逻辑配置用户的权限(比如某些接口只允许特定角色的用户访问)。
内容的提问来源于stack exchange,提问作者CharanRoot
相关产品推荐
相关产品推荐

