From b45e57cc40121e32758461473d2526d175175cdf Mon Sep 17 00:00:00 2001 From: Andreas Falk Date: Tue, 13 Aug 2019 22:23:39 +0200 Subject: [PATCH] Add setter for authority prefix in JwtGrantedAuthoritiesConverter Prior to this change mapped authorities are always prefixed with default value 'SCOPE_'. To change this default behaviour the converter had to be replaced completely with a custom one. This commit adds an additional setter to configure a custom authority prefix like e.g. 'ROLE_'. Without specifying a custom prefix the default prefix still remains 'SCOPE_'. This way existing authorization checks using the standard 'ROLE_' prefix can be reused without lots of effort. Fixes gh-7101 --- .../JwtGrantedAuthoritiesConverter.java | 17 +++++- .../JwtGrantedAuthoritiesConverterTests.java | 53 +++++++++++++++---- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverter.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverter.java index a2827dc0c9..341f7fb1b0 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverter.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverter.java @@ -25,6 +25,7 @@ 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.Assert; import org.springframework.util.StringUtils; /** @@ -40,6 +41,8 @@ public final class JwtGrantedAuthoritiesConverter implements Converter WELL_KNOWN_AUTHORITIES_CLAIM_NAMES = Arrays.asList("scope", "scp"); + private String authorityPrefix = DEFAULT_AUTHORITY_PREFIX; + /** * Extract {@link GrantedAuthority}s from the given {@link Jwt}. * @@ -50,11 +53,23 @@ public final class JwtGrantedAuthoritiesConverter implements Converter convert(Jwt jwt) { Collection grantedAuthorities = new ArrayList<>(); for (String authority : getAuthorities(jwt)) { - grantedAuthorities.add(new SimpleGrantedAuthority(DEFAULT_AUTHORITY_PREFIX + authority)); + grantedAuthorities.add(new SimpleGrantedAuthority(this.authorityPrefix + authority)); } return grantedAuthorities; } + /** + * Sets the prefix to use for {@link GrantedAuthority authorities} mapped by this converter. + * Defaults to {@link JwtGrantedAuthoritiesConverter#DEFAULT_AUTHORITY_PREFIX}. + * + * @param authorityPrefix The authority prefix + * @since 5.2 + */ + public void setAuthorityPrefix(String authorityPrefix) { + Assert.hasText(authorityPrefix, "authorityPrefix cannot be empty"); + this.authorityPrefix = authorityPrefix; + } + private String getAuthoritiesClaimName(Jwt jwt) { for (String claimName : WELL_KNOWN_AUTHORITIES_CLAIM_NAMES) { if (jwt.containsClaim(claimName)) { diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverterTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverterTests.java index 385ac27883..cb76a7bbc6 100644 --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverterTests.java +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtGrantedAuthoritiesConverterTests.java @@ -40,46 +40,75 @@ import org.springframework.security.oauth2.jwt.Jwt; * @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 authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); assertThat(authorities).containsExactly( new SimpleGrantedAuthority("SCOPE_message:read"), new SimpleGrantedAuthority("SCOPE_message:write")); } + @Test + public void convertWithCustomAuthorityPrefixWhenTokenHasScopeAttributeThenTranslatedToAuthorities() { + Jwt jwt = this.jwt(Collections.singletonMap("scope", "message:read message:write")); + + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); + + assertThat(authorities).containsExactly( + new SimpleGrantedAuthority("ROLE_message:read"), + new SimpleGrantedAuthority("ROLE_message:write")); + } + @Test public void convertWhenTokenHasEmptyScopeAttributeThenTranslatedToNoAuthorities() { Jwt jwt = this.jwt(Collections.singletonMap("scope", "")); - Collection authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); - assertThat(authorities).containsExactly(); + assertThat(authorities).isEmpty(); } @Test public void convertWhenTokenHasScpAttributeThenTranslatedToAuthorities() { Jwt jwt = this.jwt(Collections.singletonMap("scp", Arrays.asList("message:read", "message:write"))); - Collection authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); assertThat(authorities).containsExactly( new SimpleGrantedAuthority("SCOPE_message:read"), new SimpleGrantedAuthority("SCOPE_message:write")); } + @Test + public void convertWithCustomAuthorityPrefixWhenTokenHasScpAttributeThenTranslatedToAuthorities() { + Jwt jwt = this.jwt(Collections.singletonMap("scp", Arrays.asList("message:read", "message:write"))); + + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_"); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); + + assertThat(authorities).containsExactly( + new SimpleGrantedAuthority("ROLE_message:read"), + new SimpleGrantedAuthority("ROLE_message:write")); + } + @Test public void convertWhenTokenHasEmptyScpAttributeThenTranslatedToNoAuthorities() { - Jwt jwt = this.jwt(Maps.newHashMap("scp", Arrays.asList())); + Jwt jwt = this.jwt(Maps.newHashMap("scp", Collections.emptyList())); - Collection authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); - assertThat(authorities).containsExactly(); + assertThat(authorities).isEmpty(); } @Test @@ -89,7 +118,8 @@ public class JwtGrantedAuthoritiesConverterTests { claims.put("scope", "missive:read missive:write"); Jwt jwt = this.jwt(claims); - Collection authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); assertThat(authorities).containsExactly( new SimpleGrantedAuthority("SCOPE_missive:read"), @@ -103,9 +133,10 @@ public class JwtGrantedAuthoritiesConverterTests { claims.put("scope", ""); Jwt jwt = this.jwt(claims); - Collection authorities = this.jwtGrantedAuthoritiesConverter.convert(jwt); + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + Collection authorities = jwtGrantedAuthoritiesConverter.convert(jwt); - assertThat(authorities).containsExactly(); + assertThat(authorities).isEmpty(); } private Jwt jwt(Map claims) {