浏览代码

Add BearerTokenAuthentication

Fixes gh-7343
Josh Cummings 6 年之前
父节点
当前提交
c019507770

+ 67 - 0
oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthentication.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.oauth2.server.resource.authentication;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.SpringSecurityCoreVersion;
+import org.springframework.security.core.Transient;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
+import org.springframework.util.Assert;
+
+/**
+ * An {@link org.springframework.security.core.Authentication} token that represents a successful authentication as
+ * obtained through a bearer token.
+ *
+ * @author Josh Cummings
+ * @since 5.2
+ */
+@Transient
+public class BearerTokenAuthentication extends AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken> {
+
+	private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
+
+	private Map<String, Object> attributes;
+
+	/**
+	 * Constructs a {@link BearerTokenAuthentication} with the provided arguments
+	 *
+	 * @param principal The OAuth 2.0 attributes
+	 * @param credentials The verified token
+	 * @param authorities The authorities associated with the given token
+	 */
+	public BearerTokenAuthentication(OAuth2AuthenticatedPrincipal principal, OAuth2AccessToken credentials,
+			Collection<? extends GrantedAuthority> authorities) {
+
+		super(credentials, principal, credentials, authorities);
+		Assert.isTrue(credentials.getTokenType() == OAuth2AccessToken.TokenType.BEARER, "credentials must be a bearer token");
+		this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(principal.getAttributes()));
+		setAuthenticated(true);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public Map<String, Object> getTokenAttributes() {
+		return this.attributes;
+	}
+}

+ 143 - 0
oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/BearerTokenAuthenticationTests.java

@@ -0,0 +1,143 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.oauth2.server.resource.authentication;
+
+import java.net.URL;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.minidev.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.CLIENT_ID;
+import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.SUBJECT;
+import static org.springframework.security.oauth2.server.resource.introspection.OAuth2IntrospectionClaimNames.USERNAME;
+
+/**
+ * Tests for {@link BearerTokenAuthentication}
+ *
+ * @author Josh Cummings
+ */
+public class BearerTokenAuthenticationTests {
+	private final OAuth2AccessToken token =
+			new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
+				"token", Instant.now(), Instant.now().plusSeconds(3600));
+	private final String name = "sub";
+	private Map<String, Object> attributesMap = new HashMap<>();
+	private DefaultOAuth2AuthenticatedPrincipal principal;
+	private final Collection<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("USER");
+
+	@Before
+	public void setUp() {
+		this.attributesMap.put(SUBJECT, this.name);
+		this.attributesMap.put(CLIENT_ID, "client_id");
+		this.attributesMap.put(USERNAME, "username");
+		this.principal = new DefaultOAuth2AuthenticatedPrincipal(this.attributesMap, null);
+	}
+
+	@Test
+	public void getNameWhenConfiguredInConstructorThenReturnsName() {
+		OAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(this.name, this.attributesMap, this.authorities);
+		BearerTokenAuthentication authenticated = new BearerTokenAuthentication(principal, this.token, this.authorities);
+		assertThat(authenticated.getName()).isEqualTo(this.name);
+	}
+
+	@Test
+	public void getNameWhenHasNoSubjectThenReturnsNull() {
+		OAuth2AuthenticatedPrincipal principal =
+				new DefaultOAuth2AuthenticatedPrincipal(Collections.singletonMap("claim", "value"), null);
+		BearerTokenAuthentication authenticated = new BearerTokenAuthentication(principal, this.token, null);
+		assertThat(authenticated.getName()).isNull();
+	}
+
+	@Test
+	public void getNameWhenTokenHasUsernameThenReturnsUsernameAttribute() {
+		BearerTokenAuthentication authenticated = new BearerTokenAuthentication(this.principal, this.token, null);
+		assertThat(authenticated.getName()).isEqualTo(this.principal.getAttribute(SUBJECT));
+	}
+
+	@Test
+	public void constructorWhenTokenIsNullThenThrowsException() {
+		assertThatCode(() -> new BearerTokenAuthentication(this.principal, null, null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessageContaining("token cannot be null");
+	}
+
+	@Test
+	public void constructorWhenCredentialIsNullThenThrowsException() {
+		assertThatCode(() -> new BearerTokenAuthentication(null, this.token, null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessageContaining("principal cannot be null");
+	}
+
+	@Test
+	public void constructorWhenPassingAllAttributesThenTokenIsAuthenticated() {
+		OAuth2AuthenticatedPrincipal principal =
+				new DefaultOAuth2AuthenticatedPrincipal("harris", Collections.singletonMap("claim", "value"), null);
+		BearerTokenAuthentication authenticated = new BearerTokenAuthentication(principal, this.token, null);
+		assertThat(authenticated.isAuthenticated()).isTrue();
+	}
+
+	@Test
+	public void getTokenAttributesWhenHasTokenThenReturnsThem() {
+		BearerTokenAuthentication authenticated =
+				new BearerTokenAuthentication(this.principal, this.token, Collections.emptyList());
+		assertThat(authenticated.getTokenAttributes()).isEqualTo(this.principal.getAttributes());
+	}
+
+	@Test
+	public void getAuthoritiesWhenHasAuthoritiesThenReturnsThem() {
+		List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("USER");
+		BearerTokenAuthentication authenticated =
+				new BearerTokenAuthentication(this.principal, this.token, authorities);
+		assertThat(authenticated.getAuthorities()).isEqualTo(authorities);
+	}
+
+	// gh-6843
+	@Test
+	public void constructorWhenDefaultParametersThenSetsPrincipalToAttributesCopy() {
+		JSONObject attributes = new JSONObject();
+		attributes.put("active", true);
+		OAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(attributes, null);
+		BearerTokenAuthentication token = new BearerTokenAuthentication(principal, this.token, null);
+		assertThat(token.getPrincipal()).isNotSameAs(attributes);
+		assertThat(token.getTokenAttributes()).isNotSameAs(attributes);
+	}
+
+	// gh-6843
+	@Test
+	public void toStringWhenAttributesContainsURLThenDoesNotFail() throws Exception {
+		JSONObject attributes = new JSONObject(Collections.singletonMap("iss", new URL("https://idp.example.com")));
+		OAuth2AuthenticatedPrincipal principal = new DefaultOAuth2AuthenticatedPrincipal(attributes, null);
+		BearerTokenAuthentication token = new BearerTokenAuthentication(principal, this.token, null);
+		assertThatCode(token::toString)
+				.doesNotThrowAnyException();
+	}
+}