mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 09:12:14 +00:00
Introduce ReactiveJwtAuthenticationConverter
Some changes based on PR comments Fixes gh-6273
This commit is contained in:
parent
cb0ea0241b
commit
0f7dff3774
@ -16,17 +16,13 @@
|
|||||||
|
|
||||||
package org.springframework.security.oauth2.server.resource.authentication;
|
package org.springframework.security.oauth2.server.resource.authentication;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
@ -34,39 +30,39 @@ import org.springframework.util.StringUtils;
|
|||||||
* @since 5.1
|
* @since 5.1
|
||||||
*/
|
*/
|
||||||
public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
||||||
private static final String SCOPE_AUTHORITY_PREFIX = "SCOPE_";
|
private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter
|
||||||
|
= new JwtGrantedAuthoritiesConverter();
|
||||||
private static final Collection<String> WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES =
|
|
||||||
Arrays.asList("scope", "scp");
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public final AbstractAuthenticationToken convert(Jwt jwt) {
|
public final AbstractAuthenticationToken convert(Jwt jwt) {
|
||||||
Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
|
Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
|
||||||
return new JwtAuthenticationToken(jwt, authorities);
|
return new JwtAuthenticationToken(jwt, authorities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the {@link GrantedAuthority}s from scope attributes typically found in a {@link Jwt}
|
||||||
|
*
|
||||||
|
* @param jwt The token
|
||||||
|
* @return The collection of {@link GrantedAuthority}s found on the token
|
||||||
|
* @deprecated Since 5.2. Use your own custom converter instead
|
||||||
|
* @see JwtGrantedAuthoritiesConverter
|
||||||
|
* @see #setJwtGrantedAuthoritiesConverter(Converter)
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
|
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
|
||||||
return this.getScopes(jwt)
|
return this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
.stream()
|
|
||||||
.map(authority -> SCOPE_AUTHORITY_PREFIX + authority)
|
|
||||||
.map(SimpleGrantedAuthority::new)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<String> getScopes(Jwt jwt) {
|
/**
|
||||||
for ( String attributeName : WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES ) {
|
* Sets the {@link Converter Converter<Jwt, Collection<GrantedAuthority>>} to use.
|
||||||
Object scopes = jwt.getClaims().get(attributeName);
|
* Defaults to {@link JwtGrantedAuthoritiesConverter}.
|
||||||
if (scopes instanceof String) {
|
*
|
||||||
if (StringUtils.hasText((String) scopes)) {
|
* @param jwtGrantedAuthoritiesConverter The converter
|
||||||
return Arrays.asList(((String) scopes).split(" "));
|
* @since 5.2
|
||||||
} else {
|
* @see JwtGrantedAuthoritiesConverter
|
||||||
return Collections.emptyList();
|
*/
|
||||||
}
|
public void setJwtGrantedAuthoritiesConverter(Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {
|
||||||
} else if (scopes instanceof Collection) {
|
Assert.notNull(jwtGrantedAuthoritiesConverter, "jwtGrantedAuthoritiesConverter cannot be null");
|
||||||
return (Collection<String>) scopes;
|
this.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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
|
||||||
|
*
|
||||||
|
* http://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.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the {@link GrantedAuthority}s from scope attributes typically found in a
|
||||||
|
* {@link Jwt}.
|
||||||
|
*
|
||||||
|
* @author Eric Deandrea
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public final class JwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
|
||||||
|
private static final String SCOPE_AUTHORITY_PREFIX = "SCOPE_";
|
||||||
|
|
||||||
|
private static final Collection<String> WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES =
|
||||||
|
Arrays.asList("scope", "scp");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the authorities
|
||||||
|
* @param jwt The {@link Jwt} token
|
||||||
|
* @return The {@link GrantedAuthority authorities} read from the token scopes
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<GrantedAuthority> convert(Jwt jwt) {
|
||||||
|
return getScopes(jwt)
|
||||||
|
.stream()
|
||||||
|
.map(authority -> SCOPE_AUTHORITY_PREFIX + authority)
|
||||||
|
.map(SimpleGrantedAuthority::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the scopes from a {@link Jwt} token
|
||||||
|
* @param jwt The {@link Jwt} token
|
||||||
|
* @return The scopes from the token
|
||||||
|
*/
|
||||||
|
private Collection<String> getScopes(Jwt jwt) {
|
||||||
|
for ( String attributeName : WELL_KNOWN_SCOPE_ATTRIBUTE_NAMES ) {
|
||||||
|
Object scopes = jwt.getClaims().get(attributeName);
|
||||||
|
if (scopes instanceof String) {
|
||||||
|
if (StringUtils.hasText((String) scopes)) {
|
||||||
|
return Arrays.asList(((String) scopes).split(" "));
|
||||||
|
} else {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
} else if (scopes instanceof Collection) {
|
||||||
|
return (Collection<String>) scopes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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
|
||||||
|
*
|
||||||
|
* http://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 reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reactive version of {@link JwtAuthenticationConverter} for converting a {@link Jwt}
|
||||||
|
* to a {@link AbstractAuthenticationToken Mono<AbstractAuthenticationToken>}.
|
||||||
|
*
|
||||||
|
* @author Eric Deandrea
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public final class ReactiveJwtAuthenticationConverter implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {
|
||||||
|
private Converter<Jwt, Flux<GrantedAuthority>> jwtGrantedAuthoritiesConverter
|
||||||
|
= new ReactiveJwtGrantedAuthoritiesConverterAdapter(new JwtGrantedAuthoritiesConverter());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<AbstractAuthenticationToken> convert(Jwt jwt) {
|
||||||
|
return this.jwtGrantedAuthoritiesConverter.convert(jwt)
|
||||||
|
.collectList()
|
||||||
|
.map(authorities -> new JwtAuthenticationToken(jwt, authorities));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link Converter Converter<Jwt, Flux<GrantedAuthority>>} to use.
|
||||||
|
* Defaults to a reactive {@link JwtGrantedAuthoritiesConverter}.
|
||||||
|
*
|
||||||
|
* @param jwtGrantedAuthoritiesConverter The converter
|
||||||
|
* @see JwtGrantedAuthoritiesConverter
|
||||||
|
*/
|
||||||
|
public void setJwtGrantedAuthoritiesConverter(Converter<Jwt, Flux<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {
|
||||||
|
Assert.notNull(jwtGrantedAuthoritiesConverter, "jwtGrantedAuthoritiesConverter cannot be null");
|
||||||
|
this.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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
|
||||||
|
*
|
||||||
|
* http://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 reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts a {@link Converter Converter<Jwt, Collection<GrantedAuthority>>} to a
|
||||||
|
* {@link Converter Converter<Jwt, Flux<GrantedAuthority>>}.
|
||||||
|
* <p>
|
||||||
|
* Make sure the {@link Converter Converter<Jwt, Collection<GrantedAuthority>>}
|
||||||
|
* being adapted is non-blocking.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author Eric Deandrea
|
||||||
|
* @since 5.2
|
||||||
|
* @see JwtGrantedAuthoritiesConverter
|
||||||
|
*/
|
||||||
|
public final class ReactiveJwtGrantedAuthoritiesConverterAdapter implements Converter<Jwt, Flux<GrantedAuthority>> {
|
||||||
|
private final Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter;
|
||||||
|
|
||||||
|
public ReactiveJwtGrantedAuthoritiesConverterAdapter(Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter) {
|
||||||
|
Assert.notNull(grantedAuthoritiesConverter, "grantedAuthoritiesConverter cannot be null");
|
||||||
|
this.grantedAuthoritiesConverter = grantedAuthoritiesConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<GrantedAuthority> convert(Jwt jwt) {
|
||||||
|
return Flux.fromIterable(this.grantedAuthoritiesConverter.convert(jwt));
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package org.springframework.security.oauth2.server.resource.authentication;
|
package org.springframework.security.oauth2.server.resource.authentication;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -23,17 +26,15 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.assertj.core.util.Maps;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
import org.springframework.security.core.GrantedAuthority;
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||||
import org.springframework.security.oauth2.jwt.Jwt;
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link JwtAuthenticationConverter}
|
* Tests for {@link JwtAuthenticationConverter}
|
||||||
*
|
*
|
||||||
@ -43,7 +44,7 @@ public class JwtAuthenticationConverterTests {
|
|||||||
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
|
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
public void convertWhenDefaultGrantedAuthoritiesConverterSet() {
|
||||||
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
||||||
|
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
||||||
@ -55,68 +56,26 @@ public class JwtAuthenticationConverterTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {
|
public void whenSettingNullGrantedAuthoritiesConverter() {
|
||||||
Jwt jwt = this.jwt(Collections.singletonMap("scope", ""));
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(null))
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
.withMessage("jwtGrantedAuthoritiesConverter cannot be null");
|
||||||
|
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
|
||||||
|
|
||||||
assertThat(authorities).containsExactly();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
public void convertWithOverriddenGrantedAuthoritiesConverter() {
|
||||||
Jwt jwt = this.jwt(Collections.singletonMap("scp", Arrays.asList("message:read", "message:write")));
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
||||||
|
|
||||||
|
Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter =
|
||||||
|
token -> Arrays.asList(new SimpleGrantedAuthority("blah"));
|
||||||
|
|
||||||
|
this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
|
||||||
|
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
||||||
|
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
|
|
||||||
assertThat(authorities).containsExactly(
|
assertThat(authorities).containsExactly(
|
||||||
new SimpleGrantedAuthority("SCOPE_message:read"),
|
new SimpleGrantedAuthority("blah"));
|
||||||
new SimpleGrantedAuthority("SCOPE_message:write"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {
|
|
||||||
Jwt jwt = this.jwt(Maps.newHashMap("scp", Arrays.asList()));
|
|
||||||
|
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
|
||||||
|
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
|
||||||
|
|
||||||
assertThat(authorities).containsExactly();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void convertWhenTokenHasBothScopeAndScpThenScopeAttributeIsTranslatedToAuthorities() {
|
|
||||||
Map<String, Object> claims = new HashMap<>();
|
|
||||||
claims.put("scp", Arrays.asList("message:read", "message:write"));
|
|
||||||
claims.put("scope", "missive:read missive:write");
|
|
||||||
Jwt jwt = this.jwt(claims);
|
|
||||||
|
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
|
||||||
|
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
|
||||||
|
|
||||||
assertThat(authorities).containsExactly(
|
|
||||||
new SimpleGrantedAuthority("SCOPE_missive:read"),
|
|
||||||
new SimpleGrantedAuthority("SCOPE_missive:write"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {
|
|
||||||
Map<String, Object> claims = new HashMap<>();
|
|
||||||
claims.put("scp", Arrays.asList("message:read", "message:write"));
|
|
||||||
claims.put("scope", "");
|
|
||||||
Jwt jwt = this.jwt(claims);
|
|
||||||
|
|
||||||
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt);
|
|
||||||
|
|
||||||
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
|
||||||
|
|
||||||
assertThat(authorities).containsExactly();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Jwt jwt(Map<String, Object> claims) {
|
private Jwt jwt(Map<String, Object> claims) {
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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
|
||||||
|
*
|
||||||
|
* http://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 static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.assertj.core.util.Maps;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link JwtGrantedAuthoritiesConverter}
|
||||||
|
*
|
||||||
|
* @author Eric Deandrea
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public class JwtGrantedAuthoritiesConverterTests {
|
||||||
|
private JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenTokenHasScopeAttributeThenTranslatedToAuthorities() {
|
||||||
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly(
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:write"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() {
|
||||||
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", ""));
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() {
|
||||||
|
Jwt jwt = this.jwt(Collections.singletonMap("scp", Arrays.asList("message:read", "message:write")));
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly(
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:write"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() {
|
||||||
|
Jwt jwt = this.jwt(Maps.newHashMap("scp", Arrays.asList()));
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenTokenHasBothScopeAndScpThenScopeAttributeIsTranslatedToAuthorities() {
|
||||||
|
Map<String, Object> claims = new HashMap<>();
|
||||||
|
claims.put("scp", Arrays.asList("message:read", "message:write"));
|
||||||
|
claims.put("scope", "missive:read missive:write");
|
||||||
|
Jwt jwt = this.jwt(claims);
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly(
|
||||||
|
new SimpleGrantedAuthority("SCOPE_missive:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_missive:write"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenTokenHasEmptyScopeAndNonEmptyScpThenScopeAttributeIsTranslatedToNoAuthorities() {
|
||||||
|
Map<String, Object> claims = new HashMap<>();
|
||||||
|
claims.put("scp", Arrays.asList("message:read", "message:write"));
|
||||||
|
claims.put("scope", "");
|
||||||
|
Jwt jwt = this.jwt(claims);
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt);
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Jwt jwt(Map<String, Object> claims) {
|
||||||
|
Map<String, Object> headers = new HashMap<>();
|
||||||
|
headers.put("alg", JwsAlgorithms.RS256);
|
||||||
|
|
||||||
|
return new Jwt("token", Instant.now(), Instant.now().plusSeconds(3600), headers, claims);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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
|
||||||
|
*
|
||||||
|
* http://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 static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ReactiveJwtAuthenticationConverter}
|
||||||
|
*
|
||||||
|
* @author Eric Deandrea
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public class ReactiveJwtAuthenticationConverterTests {
|
||||||
|
ReactiveJwtAuthenticationConverter jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverter();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWhenDefaultGrantedAuthoritiesConverterSet() {
|
||||||
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
||||||
|
|
||||||
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
||||||
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly(
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:read"),
|
||||||
|
new SimpleGrantedAuthority("SCOPE_message:write"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenSettingNullGrantedAuthoritiesConverter() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(null))
|
||||||
|
.withMessage("jwtGrantedAuthoritiesConverter cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertWithOverriddenGrantedAuthoritiesConverter() {
|
||||||
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
||||||
|
|
||||||
|
Converter<Jwt, Flux<GrantedAuthority>> grantedAuthoritiesConverter =
|
||||||
|
token -> Flux.just(new SimpleGrantedAuthority("blah"));
|
||||||
|
|
||||||
|
this.jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
|
||||||
|
|
||||||
|
AbstractAuthenticationToken authentication = this.jwtAuthenticationConverter.convert(jwt).block();
|
||||||
|
Collection<GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly(
|
||||||
|
new SimpleGrantedAuthority("blah"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Jwt jwt(Map<String, Object> claims) {
|
||||||
|
Map<String, Object> headers = new HashMap<>();
|
||||||
|
headers.put("alg", JwsAlgorithms.RS256);
|
||||||
|
|
||||||
|
return new Jwt("token", Instant.now(), Instant.now().plusSeconds(3600), headers, claims);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2018 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
|
||||||
|
*
|
||||||
|
* http://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 static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ReactiveJwtGrantedAuthoritiesConverterAdapter}
|
||||||
|
*
|
||||||
|
* @author Eric Deandrea
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
public class ReactiveJwtGrantedAuthoritiesConverterAdapterTests {
|
||||||
|
@Test
|
||||||
|
public void convertWithGrantedAuthoritiesConverter() {
|
||||||
|
Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write"));
|
||||||
|
|
||||||
|
Converter<Jwt, Collection<GrantedAuthority>> grantedAuthoritiesConverter =
|
||||||
|
token -> Arrays.asList(new SimpleGrantedAuthority("blah"));
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities =
|
||||||
|
new ReactiveJwtGrantedAuthoritiesConverterAdapter(grantedAuthoritiesConverter)
|
||||||
|
.convert(jwt)
|
||||||
|
.toStream()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
assertThat(authorities).containsExactly(
|
||||||
|
new SimpleGrantedAuthority("blah"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whenConstructingWithInvalidConverter() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> new ReactiveJwtGrantedAuthoritiesConverterAdapter(null))
|
||||||
|
.withMessage("grantedAuthoritiesConverter cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Jwt jwt(Map<String, Object> claims) {
|
||||||
|
Map<String, Object> headers = new HashMap<>();
|
||||||
|
headers.put("alg", JwsAlgorithms.RS256);
|
||||||
|
|
||||||
|
return new Jwt("token", Instant.now(), Instant.now().plusSeconds(3600), headers, claims);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user