spring security authorization server 定制令牌和用户信息

2024-01-05 08:47:03 浏览数 (1)

版本

1.2.1

定制方法

默认用户信息Mapper只针对用户ID,电子邮件,电话,个人档案等字段进行处理,如需在用户信息端点返回自定义的字段可通过以下方法定制Mapper

代码语言:javascript复制
@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
) {
	...
    // 定制Mapper
	Function<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
代码语言:javascript复制
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
		);
		
		@Override
		public 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);
			// 用户ID
			scopeRequestedClaimNames.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
代码语言:javascript复制
public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer {
	...
	// userInfoMapper配置入口
	public OidcUserInfoEndpointConfigurer userInfoMapper(
			Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {
		this.userInfoMapper = userInfoMapper;
		return this;
	}
	... 
	// 初始化安全配置
	@Override
	void 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;
	}
}

0 人点赞