Spring Reactive OAuth2资源服务器:将令牌声明与Cognito用户信息整合进Authentication
在Reactive Spring Security中整合Cognito令牌与用户额外信息
完全可以通过自定义JWT认证转换器的方式,在令牌验证阶段自动获取Cognito的用户额外信息,将其整合到自定义User对象并注入Authentication中,替代手动调用userInfo接口的方式。具体实现步骤如下:
1. 定义自定义用户对象
创建实现UserDetails的自定义用户类,用来承载令牌声明和额外获取的用户信息:
public class CustomUser implements UserDetails { private String username; private String email; private String avatar; private Collection<? extends GrantedAuthority> authorities; // 构造器、Getter/Setter @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
2. 实现Cognito用户信息获取服务
封装调用Cognito userInfo接口的逻辑:
@Service public class CognitoUserInfoService { private final WebClient webClient; public CognitoUserInfoService(@Value("${cognito.user-info-uri}") String userInfoUri) { this.webClient = WebClient.builder().baseUrl(userInfoUri).build(); } public Mono<CognitoUserInfo> fetchUserInfo(String accessToken) { return webClient.get() .headers(headers -> headers.setBearerAuth(accessToken)) .retrieve() .bodyToMono(CognitoUserInfo.class); } // 内部类接收Cognito返回的用户信息 public static class CognitoUserInfo { private String email; private String picture; // 对应头像字段 // Getter/Setter } }
注意:在配置文件中添加cognito.user-info-uri=https://my-congito-pool.auth.us-east-1.amazoncognito.com/oauth2/userInfo。
3. 自定义JWT认证转换器
实现Converter<Jwt, Mono<Authentication>>,在转换JWT令牌时自动调用用户信息服务,组装自定义用户对象:
@Component public class CustomJwtAuthenticationConverter implements Converter<Jwt, Mono<Authentication>> { private final CognitoUserInfoService userInfoService; private final JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter(); public CustomJwtAuthenticationConverter(CognitoUserInfoService userInfoService) { this.userInfoService = userInfoService; } @Override public Mono<Authentication> convert(Jwt jwt) { // 从JWT中提取权限信息 Collection<GrantedAuthority> authorities = authoritiesConverter.convert(jwt); // 获取原始访问令牌 String accessToken = jwt.getTokenValue(); // 调用Cognito获取额外用户信息,组装自定义User并生成Authentication return userInfoService.fetchUserInfo(accessToken) .map(userInfo -> { CustomUser customUser = new CustomUser(); customUser.setUsername(jwt.getSubject()); customUser.setEmail(userInfo.getEmail()); customUser.setAvatar(userInfo.getPicture()); customUser.setAuthorities(authorities); return new UsernamePasswordAuthenticationToken(customUser, jwt, authorities); }); } }
4. 配置Reactive Security链
将自定义转换器注入到OAuth2资源服务器配置中:
@Configuration @EnableWebFluxSecurity public class SecurityConfig { private final CustomJwtAuthenticationConverter customJwtAuthenticationConverter; public SecurityConfig(CustomJwtAuthenticationConverter customJwtAuthenticationConverter) { this.customJwtAuthenticationConverter = customJwtAuthenticationConverter; } @Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchanges -> exchanges .anyExchange().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(customJwtAuthenticationConverter) ) ); return http.build(); } }
优化建议
- 添加缓存:对
fetchUserInfo方法添加缓存(比如用@Cacheable或Reactor的cache()操作),避免每次请求都调用Cognito接口,减少延迟和开销。 - 异常处理:在转换器中添加异常处理逻辑,比如调用
userInfo接口失败时,可降级使用JWT中已有的信息,避免认证流程中断。 - 权限自定义:如果需要自定义权限转换逻辑,可以扩展
JwtGrantedAuthoritiesConverter或直接在转换器中手动生成权限集合。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

