如何通过Spring OIDC/OAuth2从AWS Cognito自定义声明获取权限角色
解决Spring Boot OAuth2/OIDC中Cognito用户角色映射问题
问题背景
在Spring Boot中完成基础OAuth2/OIDC认证配置后,认证功能运行稳定,但获取用户授权权限时仅能看到OIDC_USER这一个角色。已知Cognito返回的ID Token中包含cognito:groups自定义声明,其中存储了用户的角色列表(如USER、ADMIN等),希望直接将该声明中的角色映射为Spring Security的GrantedAuthority,且无需手动编写大量JWT/OAuth相关类,同时不破坏原有OIDC认证流程。
可行解决方案
方案:自定义OidcUserService(推荐,不破坏原有自动配置)
Spring Security OAuth2客户端默认使用OidcUserService处理OIDC用户信息,通过重写该类的loadUser方法,可以轻松从ID Token中提取cognito:groups并转换为权限,且无需修改安全过滤器链。
代码实现
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.stereotype.Service; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @Service public class CustomOidcUserService extends OidcUserService { @Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { // 调用父类方法获取默认OIDC用户信息 OidcUser oidcUser = super.loadUser(userRequest); // 从ID Token中提取cognito:groups声明 List<String> userGroups = oidcUser.getClaim("cognito:groups"); // 将角色转换为Spring Security标准权限(添加ROLE_前缀符合规范) Collection<GrantedAuthority> groupAuthorities = userGroups.stream() .map(group -> new SimpleGrantedAuthority("ROLE_" + group)) .collect(Collectors.toList()); // 合并原有权限(如OIDC_USER)与新角色权限 groupAuthorities.addAll(oidcUser.getAuthorities()); // 返回包含完整权限的自定义OIDC用户对象 return new DefaultOidcUser(groupAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo()); } }
方案说明
- 该类会被Spring Security自动识别并替换默认的
OidcUserService,完全兼容原有OIDC认证流程 - 自动保留原有
OIDC_USER权限,同时添加cognito:groups中的角色(带ROLE_前缀,符合Spring Security权限命名规范) - 无需手动配置过滤器链或其他复杂Bean,仅需添加该Service类即可生效
验证
配置完成后,用户登录成功后,可通过以下代码获取完整权限集合:
import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); // 输出权限列表,包含ROLE_USER、ROLE_ADMIN、OIDC_USER等 authentication.getAuthorities().forEach(auth -> System.out.println(auth.getAuthority()));
附:原配置文件与ID Token示例
原配置文件
spring.security.oauth2.client.registration.cognito.client-id=xxxxx spring.security.oauth2.client.registration.cognito.client-secret=123xxxx spring.security.oauth2.client.registration.cognito.redirect-uri=${app.url}/login/oauth2/code/cognito #spring.security.oauth2.client.registration.cognito.redirect-uri=${app.url}/dashboard spring.security.oauth2.client.registration.cognito.client-name=myclient #spring.security.oauth2.client.registration.cognito.scope=openid+email+profile spring.security.oauth2.client.registration.cognito.scope=openid spring.security.oauth2.client.registration.cognito.authorization-grant-type=authorization_code spring.security.oauth2.client.provider.cognito.user-name-attribute=email spring.security.oauth2.client.provider.cognito.jwk-set-uri: https://cognito-idp.us-east-1.amazonaws.com/aaaaaaa/.well-known/jwks.json spring.security.oauth2.client.provider.cognito.authorization-uri=https://bbbbbbbbbb.auth.us-east-1.amazoncognito.com/oauth2/authorize spring.security.oauth2.client.provider.cognito.token-uri=https://bbbbbbbbb.auth.us-east-1.amazonaws.com/oauth2/token
ID Token示例
{ "claims": { "at_hash": "111111aaaaaabbbbbbbccccc", "sub": "5a8417c7-7875-4ac5-9d7f-b0ffd1b3490f", "cognito:groups": [ "USER", "DEVELOPER", "ADMIN", "CLIENT" ], "birthdate": "01/01/1901", "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_abc123", "updated_at": { "seconds": 1675049091, "nanos": 0 }, "auth_time": { "seconds": 1679280866, "nanos": 0 }, "nickname": "First", "exp": { "seconds": 1679367266, "nanos": 0 }, "iat": { "seconds": 1679280866, "nanos": 0 }, "jti": "4f53349b-0e78-4c84-ac21-dd69ad3c9ea4", "email": "myuser@email.com", "email_verified": true, "address": { "formatted": "123 N Main St" }, "profile": "firstName", "phone_number_verified": true, "cognito:username": "myuser", "given_name": "First", "nonce": "yyyyyyyyyyyyyyyyyyyyyyyy", "picture": "https://vectorified.com/images/generic-avatar-icon-24.png", "origin_jti": "8ae68e89-19b2-47a3-a2cb-34fb513ce9d0", "aud": [ "1111aaaabbb" ], "token_use": "id", "name": "First Last", "phone_number": "+11112221234", "family_name": "Last" }, "tokenValue": "xxx...", "issuedAt": { "seconds": 1679280866, "nanos": 0 }, "expiresAt": { "seconds": 1679367266, "nanos": 0 } }
内容的提问来源于stack exchange,提问作者user9221321
相关产品推荐
相关产品推荐

