浏览代码

Expose user name attribute name in `OAuth2UserAuthority`

Filip Hrisafov 1 年之前
父节点
当前提交
99aee99b34
共有 13 个文件被更改,包括 92 次插入13 次删除
  1. 8 3
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java
  2. 4 3
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java
  3. 1 1
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java
  4. 2 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java
  5. 2 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcReactiveOAuth2UserServiceTests.java
  6. 1 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java
  7. 2 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java
  8. 2 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java
  9. 29 1
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java
  10. 35 0
      oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthority.java
  11. 4 3
      oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/TestOAuth2Users.java
  12. 1 1
      test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java
  13. 1 1
      test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

+ 8 - 3
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java

@@ -78,13 +78,18 @@ final class OidcUserRequestUtils {
 
 	static OidcUser getUser(OidcUserRequest userRequest, OidcUserInfo userInfo) {
 		Set<GrantedAuthority> authorities = new LinkedHashSet<>();
-		authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
+		ClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
+		String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
+		if (StringUtils.hasLength(userNameAttributeName)) {
+			authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo, userNameAttributeName));
+		}
+		else {
+			authorities.add(new OidcUserAuthority(userRequest.getIdToken(), userInfo));
+		}
 		OAuth2AccessToken token = userRequest.getAccessToken();
 		for (String scope : token.getScopes()) {
 			authorities.add(new SimpleGrantedAuthority("SCOPE_" + scope));
 		}
-		ClientRegistration.ProviderDetails providerDetails = userRequest.getClientRegistration().getProviderDetails();
-		String userNameAttributeName = providerDetails.getUserInfoEndpoint().getUserNameAttributeName();
 		if (StringUtils.hasText(userNameAttributeName)) {
 			return new DefaultOidcUser(authorities, userRequest.getIdToken(), userInfo, userNameAttributeName);
 		}

+ 4 - 3
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserService.java

@@ -95,7 +95,7 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
 		ResponseEntity<Map<String, Object>> response = getResponse(userRequest, request);
 		OAuth2AccessToken token = userRequest.getAccessToken();
 		Map<String, Object> attributes = this.attributesConverter.convert(userRequest).convert(response.getBody());
-		Collection<GrantedAuthority> authorities = getAuthorities(token, attributes);
+		Collection<GrantedAuthority> authorities = getAuthorities(token, attributes, userNameAttributeName);
 		return new DefaultOAuth2User(authorities, attributes, userNameAttributeName);
 	}
 
@@ -187,9 +187,10 @@ public class DefaultOAuth2UserService implements OAuth2UserService<OAuth2UserReq
 		return userNameAttributeName;
 	}
 
-	private Collection<GrantedAuthority> getAuthorities(OAuth2AccessToken token, Map<String, Object> attributes) {
+	private Collection<GrantedAuthority> getAuthorities(OAuth2AccessToken token, Map<String, Object> attributes,
+			String userNameAttributeName) {
 		Collection<GrantedAuthority> authorities = new LinkedHashSet<>();
-		authorities.add(new OAuth2UserAuthority(attributes));
+		authorities.add(new OAuth2UserAuthority(attributes, userNameAttributeName));
 		for (String authority : token.getScopes()) {
 			authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
 		}

+ 1 - 1
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java

@@ -130,7 +130,7 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
 					.bodyToMono(DefaultReactiveOAuth2UserService.STRING_OBJECT_MAP)
 					.mapNotNull((attributes) -> this.attributesConverter.convert(userRequest).convert(attributes));
 			return userAttributes.map((attrs) -> {
-				GrantedAuthority authority = new OAuth2UserAuthority(attrs);
+				GrantedAuthority authority = new OAuth2UserAuthority(attrs, userNameAttributeName);
 				Set<GrantedAuthority> authorities = new HashSet<>();
 				authorities.add(authority);
 				OAuth2AccessToken token = userRequest.getAccessToken();

+ 2 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java

@@ -247,6 +247,7 @@ public class OAuth2AuthenticationTokenMixinTests {
 		return "{\n" +
 				"          \"@class\": \"org.springframework.security.oauth2.core.user.OAuth2UserAuthority\",\n" +
 				"          \"authority\": \"" + oauth2UserAuthority.getAuthority() + "\",\n" +
+				"          \"userNameAttributeName\": \"username\",\n" +
 				"          \"attributes\": {\n" +
 				"            \"@class\": \"java.util.Collections$UnmodifiableMap\",\n" +
 				"            \"username\": \"user\"\n" +
@@ -260,6 +261,7 @@ public class OAuth2AuthenticationTokenMixinTests {
 		return "{\n" +
 				"          \"@class\": \"org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority\",\n" +
 				"          \"authority\": \"" + oidcUserAuthority.getAuthority() + "\",\n" +
+				"          \"userNameAttributeName\": \"" + oidcUserAuthority.getUserNameAttributeName() + "\",\n" +
 				"          \"idToken\": " + asJson(oidcUserAuthority.getIdToken()) + ",\n" +
 				"          \"userInfo\": " + asJson(oidcUserAuthority.getUserInfo()) + "\n" +
 				"        }";

+ 2 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcReactiveOAuth2UserServiceTests.java

@@ -313,6 +313,7 @@ public class OidcReactiveOAuth2UserServiceTests {
 		OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 		assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER");
 		assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+		assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("id");
 	}
 
 	@Test
@@ -361,6 +362,7 @@ public class OidcReactiveOAuth2UserServiceTests {
 			OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 			assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER");
 			assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+			assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
 		}
 	}
 

+ 1 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java

@@ -616,6 +616,7 @@ public class OidcUserServiceTests {
 		OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 		assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER");
 		assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+		assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
 	}
 
 	private MockResponse jsonResponse(String json) {

+ 2 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java

@@ -156,6 +156,7 @@ public class DefaultOAuth2UserServiceTests {
 		OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 		assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
 		assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+		assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
 	}
 
 	@Test
@@ -196,6 +197,7 @@ public class DefaultOAuth2UserServiceTests {
 		OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 		assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
 		assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+		assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
 	}
 
 	@Test

+ 2 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java

@@ -144,6 +144,7 @@ public class DefaultReactiveOAuth2UserServiceTests {
 		OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 		assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
 		assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+		assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("id");
 	}
 
 	// gh-9336
@@ -203,6 +204,7 @@ public class DefaultReactiveOAuth2UserServiceTests {
 		OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next();
 		assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER");
 		assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
+		assertThat(userAuthority.getUserNameAttributeName()).isEqualTo("user-name");
 	}
 
 	// gh-5500

+ 29 - 1
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/OidcUserAuthority.java

@@ -20,6 +20,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
 import org.springframework.security.oauth2.core.oidc.OidcIdToken;
 import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
 import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
@@ -57,6 +58,19 @@ public class OidcUserAuthority extends OAuth2UserAuthority {
 		this("OIDC_USER", idToken, userInfo);
 	}
 
+	/**
+	 * Constructs a {@code OidcUserAuthority} using the provided parameters and defaults
+	 * {@link #getAuthority()} to {@code OIDC_USER}.
+	 * @param idToken the {@link OidcIdToken ID Token} containing claims about the user
+	 * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,
+	 * may be {@code null}
+	 * @param userNameAttributeName the attribute name used to access the user's name from
+	 * the attributes
+	 */
+	public OidcUserAuthority(OidcIdToken idToken, OidcUserInfo userInfo, String userNameAttributeName) {
+		this("OIDC_USER", idToken, userInfo, userNameAttributeName);
+	}
+
 	/**
 	 * Constructs a {@code OidcUserAuthority} using the provided parameters.
 	 * @param authority the authority granted to the user
@@ -65,7 +79,21 @@ public class OidcUserAuthority extends OAuth2UserAuthority {
 	 * may be {@code null}
 	 */
 	public OidcUserAuthority(String authority, OidcIdToken idToken, OidcUserInfo userInfo) {
-		super(authority, collectClaims(idToken, userInfo));
+		this(authority, idToken, userInfo, IdTokenClaimNames.SUB);
+	}
+
+	/**
+	 * Constructs a {@code OidcUserAuthority} using the provided parameters.
+	 * @param authority the authority granted to the user
+	 * @param idToken the {@link OidcIdToken ID Token} containing claims about the user
+	 * @param userInfo the {@link OidcUserInfo UserInfo} containing claims about the user,
+	 * may be {@code null}
+	 * @param userNameAttributeName the attribute name used to access the user's name from
+	 * the attributes
+	 */
+	public OidcUserAuthority(String authority, OidcIdToken idToken, OidcUserInfo userInfo,
+			String userNameAttributeName) {
+		super(authority, collectClaims(idToken, userInfo), userNameAttributeName);
 		this.idToken = idToken;
 		this.userInfo = userInfo;
 	}

+ 35 - 0
oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/OAuth2UserAuthority.java

@@ -22,6 +22,7 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
 
+import org.springframework.lang.Nullable;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.SpringSecurityCoreVersion;
 import org.springframework.util.Assert;
@@ -41,6 +42,8 @@ public class OAuth2UserAuthority implements GrantedAuthority {
 
 	private final Map<String, Object> attributes;
 
+	private final String userNameAttributeName;
+
 	/**
 	 * Constructs a {@code OAuth2UserAuthority} using the provided parameters and defaults
 	 * {@link #getAuthority()} to {@code OAUTH2_USER}.
@@ -50,16 +53,39 @@ public class OAuth2UserAuthority implements GrantedAuthority {
 		this("OAUTH2_USER", attributes);
 	}
 
+	/**
+	 * Constructs a {@code OAuth2UserAuthority} using the provided parameters and defaults
+	 * {@link #getAuthority()} to {@code OAUTH2_USER}.
+	 * @param attributes the attributes about the user
+	 * @param userNameAttributeName the attribute name used to access the user's name from
+	 * the attributes
+	 */
+	public OAuth2UserAuthority(Map<String, Object> attributes, @Nullable String userNameAttributeName) {
+		this("OAUTH2_USER", attributes, userNameAttributeName);
+	}
+
 	/**
 	 * Constructs a {@code OAuth2UserAuthority} using the provided parameters.
 	 * @param authority the authority granted to the user
 	 * @param attributes the attributes about the user
 	 */
 	public OAuth2UserAuthority(String authority, Map<String, Object> attributes) {
+		this(authority, attributes, null);
+	}
+
+	/**
+	 * Constructs a {@code OAuth2UserAuthority} using the provided parameters.
+	 * @param authority the authority granted to the user
+	 * @param attributes the attributes about the user
+	 * @param userNameAttributeName the attribute name used to access the user's name from
+	 * the attributes
+	 */
+	public OAuth2UserAuthority(String authority, Map<String, Object> attributes, String userNameAttributeName) {
 		Assert.hasText(authority, "authority cannot be empty");
 		Assert.notEmpty(attributes, "attributes cannot be empty");
 		this.authority = authority;
 		this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes));
+		this.userNameAttributeName = userNameAttributeName;
 	}
 
 	@Override
@@ -75,6 +101,15 @@ public class OAuth2UserAuthority implements GrantedAuthority {
 		return this.attributes;
 	}
 
+	/**
+	 * Returns the attribute name used to access the user's name from the attributes.
+	 * @return the attribute name used to access the user's name from the attributes
+	 */
+	@Nullable
+	public String getUserNameAttributeName() {
+		return this.userNameAttributeName;
+	}
+
 	@Override
 	public boolean equals(Object obj) {
 		if (this == obj) {

+ 4 - 3
oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/TestOAuth2Users.java

@@ -37,12 +37,13 @@ public final class TestOAuth2Users {
 		String nameAttributeKey = "username";
 		Map<String, Object> attributes = new HashMap<>();
 		attributes.put(nameAttributeKey, "user");
-		Collection<GrantedAuthority> authorities = authorities(attributes);
+		Collection<GrantedAuthority> authorities = authorities(attributes, nameAttributeKey);
 		return new DefaultOAuth2User(authorities, attributes, nameAttributeKey);
 	}
 
-	private static Collection<GrantedAuthority> authorities(Map<String, Object> attributes) {
-		return new LinkedHashSet<>(Arrays.asList(new OAuth2UserAuthority(attributes),
+	private static Collection<GrantedAuthority> authorities(Map<String, Object> attributes,
+			String userNameAttributeName) {
+		return new LinkedHashSet<>(Arrays.asList(new OAuth2UserAuthority(attributes, userNameAttributeName),
 				new SimpleGrantedAuthority("SCOPE_read"), new SimpleGrantedAuthority("SCOPE_write")));
 	}
 

+ 1 - 1
test/src/main/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurers.java

@@ -834,7 +834,7 @@ public final class SecurityMockServerConfigurers {
 
 		private Collection<GrantedAuthority> defaultAuthorities() {
 			Set<GrantedAuthority> authorities = new LinkedHashSet<>();
-			authorities.add(new OAuth2UserAuthority(this.attributes.get()));
+			authorities.add(new OAuth2UserAuthority(this.attributes.get(), this.nameAttributeKey));
 			for (String authority : this.accessToken.getScopes()) {
 				authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
 			}

+ 1 - 1
test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

@@ -1376,7 +1376,7 @@ public final class SecurityMockMvcRequestPostProcessors {
 
 		private Collection<GrantedAuthority> defaultAuthorities() {
 			Set<GrantedAuthority> authorities = new LinkedHashSet<>();
-			authorities.add(new OAuth2UserAuthority(this.attributes.get()));
+			authorities.add(new OAuth2UserAuthority(this.attributes.get(), this.nameAttributeKey));
 			for (String authority : this.accessToken.getScopes()) {
 				authorities.add(new SimpleGrantedAuthority("SCOPE_" + authority));
 			}