版本
1.2.1
定制方法
默认用户信息Mapper只针对用户ID,电子邮件,电话,个人档案等字段进行处理,如需在用户信息端点返回自定义的字段可通过以下方法定制Mapper
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() {return (context) -> {if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {context.getClaims().claims((claims) -> {// 在令牌中添加了角色声明信息,默认用户信息端点Mapper不会进行处理Set<String> roles = AuthorityUtils.authorityListToSet(context.getPrincipal().getAuthorities()).stream().map(c -> c.replaceFirst("^ROLE_", "")).collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));claims.put("roles", roles);});}};
}@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http
) {...// 定制MapperFunction<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = (context) -> {OidcUserInfoAuthenticationToken authentication = context.getAuthentication();JwtAuthenticationToken principal = (JwtAuthenticationToken) authentication.getPrincipal();// 解析JWT令牌中的所有声明信息return new OidcUserInfo(principal.getToken().getClaims());};http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)// 启用OpenID Connect 1.0.oidc(oidc->{oidc.userInfoEndpoint(userInfo->{userInfo.userInfoMapper(userInfoMapper);});});
}
源码
userInfoMapper 默认配置
- OpenID Connect 用户信息认证器
org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationProvider
public final class OidcUserInfoAuthenticationProvider implements AuthenticationProvider {...// 用户信息Mapper默认值private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();...private static final class DefaultOidcUserInfoMapper implements Function<OidcUserInfoAuthenticationContext, OidcUserInfo> {// 电子邮件声明字段private static final List<String> EMAIL_CLAIMS = Arrays.asList(StandardClaimNames.EMAIL,StandardClaimNames.EMAIL_VERIFIED);// 电话声明字段private static final List<String> PHONE_CLAIMS = Arrays.asList(StandardClaimNames.PHONE_NUMBER,StandardClaimNames.PHONE_NUMBER_VERIFIED);// 个人档案声明字段private static final List<String> PROFILE_CLAIMS = Arrays.asList(StandardClaimNames.NAME,StandardClaimNames.FAMILY_NAME,StandardClaimNames.GIVEN_NAME,StandardClaimNames.MIDDLE_NAME,StandardClaimNames.NICKNAME,StandardClaimNames.PREFERRED_USERNAME,StandardClaimNames.PROFILE,StandardClaimNames.PICTURE,StandardClaimNames.WEBSITE,StandardClaimNames.GENDER,StandardClaimNames.BIRTHDATE,StandardClaimNames.ZONEINFO,StandardClaimNames.LOCALE,StandardClaimNames.UPDATED_AT);@Overridepublic OidcUserInfo apply(OidcUserInfoAuthenticationContext authenticationContext) {OAuth2Authorization authorization = authenticationContext.getAuthorization();OidcIdToken idToken = authorization.getToken(OidcIdToken.class).getToken();OAuth2AccessToken accessToken = authenticationContext.getAccessToken();// 获取scope请求的声明信息Map<String, Object> scopeRequestedClaims = getClaimsRequestedByScope(idToken.getClaims(),accessToken.getScopes());// 使用请求的声明信息创建用户信息对象return new OidcUserInfo(scopeRequestedClaims);}private static Map<String, Object> getClaimsRequestedByScope(Map<String, Object> claims, Set<String> requestedScopes) {Set<String> scopeRequestedClaimNames = new HashSet<>(32);// 用户IDscopeRequestedClaimNames.add(StandardClaimNames.SUB);// 根据请求的SCOPE添加字段if (requestedScopes.contains(OidcScopes.ADDRESS)) {scopeRequestedClaimNames.add(StandardClaimNames.ADDRESS);}if (requestedScopes.contains(OidcScopes.EMAIL)) {scopeRequestedClaimNames.addAll(EMAIL_CLAIMS);}if (requestedScopes.contains(OidcScopes.PHONE)) {scopeRequestedClaimNames.addAll(PHONE_CLAIMS);}if (requestedScopes.contains(OidcScopes.PROFILE)) {scopeRequestedClaimNames.addAll(PROFILE_CLAIMS);}// 根据请求的SCOPE字段过滤信息Map<String, Object> requestedClaims = new HashMap<>(claims);requestedClaims.keySet().removeIf(claimName -> !scopeRequestedClaimNames.contains(claimName));return requestedClaims;}}
}
- OpenID Connect 用户信息端点配置器
org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OidcUserInfoEndpointConfigurer
public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer {...// userInfoMapper配置入口public OidcUserInfoEndpointConfigurer userInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {this.userInfoMapper = userInfoMapper;return this;}... // 初始化安全配置@Overridevoid init(HttpSecurity httpSecurity) {// 应用用户信息端点URI安全配置AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);String userInfoEndpointUri = authorizationServerSettings.getOidcUserInfoEndpoint();this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.GET.name()),new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.POST.name()));// 应用用户认证器List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);if (!this.authenticationProviders.isEmpty()) {authenticationProviders.addAll(0, this.authenticationProviders);}this.authenticationProvidersConsumer.accept(authenticationProviders);authenticationProviders.forEach(authenticationProvider ->httpSecurity.authenticationProvider(postProcess(authenticationProvider)));}// 创建 OpenID Connect 用户信息认证器private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {List<AuthenticationProvider> authenticationProviders = new ArrayList<>();OidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider =new OidcUserInfoAuthenticationProvider(OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));// 如果存在定制的userInfoMapper,则替换默认值if (this.userInfoMapper != null) {oidcUserInfoAuthenticationProvider.setUserInfoMapper(this.userInfoMapper);}authenticationProviders.add(oidcUserInfoAuthenticationProvider);return authenticationProviders;}
}