Now JwtGrantedAuthoritiesConverter only support one claim (scp/scope -> SCOPE_), now we have request to support multiple claims. For example:
scp -> SCOPE_
roles -> ROLE_
So I suggest to implement JwtGrantedAuthoritiesConverter like this:
public class JwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
private final Log logger = LogFactory.getLog(getClass());
private final Map<String, String> claimToAuthorityPrefixMap;
public JwtGrantedAuthoritiesConverter(Map<String, String> claimToAuthorityPrefixMap) {
this.claimToAuthorityPrefixMap = Collections.unmodifiableMap(claimToAuthorityPrefixMap);
}
@Override
public Collection<GrantedAuthority> convert(Jwt jwt) {
return getAuthorityStrings(jwt, this.claimToAuthorityPrefixMap);
}
private Collection<GrantedAuthority> getAuthorityStrings(Jwt jwt, Map<String, String> claimToAuthorityPrefixMap) {
Collection<String> authorityStrings = new HashSet<>();
for (String claim : claimToAuthorityPrefixMap.keySet()) {
String authorityPrefix = claimToAuthorityPrefixMap.get(claim);
getClaimValues(jwt, claim).stream()
.map(claimValue -> authorityPrefix + claimValue)
.forEach(authorityStrings::add);
}
return authorityStrings.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
}
private Collection<String> getClaimValues(Jwt jwt, String claimName) {
if (claimName == null) {
this.logger.trace("Returning no authorities since could not find any claims that might contain scopes");
return Collections.emptyList();
}
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Looking for scopes in claim %s", claimName));
}
Object claimValue = jwt.getClaim(claimName);
if (claimValue instanceof String) {
if (StringUtils.hasText((String) claimValue)) {
return Arrays.asList(((String) claimValue).split(" "));
}
return Collections.emptyList();
}
if (claimValue instanceof Collection) {
return castToCollection(claimValue);
}
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
private Collection<String> castToCollection(Object object) {
return (Collection<String>) object;
}
}
And use it like this:
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(jwtAuthenticationConverter())
.and()
.and();
// @formatter:on
}
private JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
converter.setJwtGrantedAuthoritiesConverter(new JwtGrantedAuthoritiesConverter(claimToAuthorityPrefixMap()));
return converter;
}
private Map<String, String> claimToAuthorityPrefixMap() {
Map<String, String> claimToAuthorityPrefixMap = new HashMap<>();
claimToAuthorityPrefixMap.put("scope", "SCOPE_");
claimToAuthorityPrefixMap.put("scp", "SCOPE_");
claimToAuthorityPrefixMap.put("roles", "ROLE_");
return claimToAuthorityPrefixMap;
}
}
Comment From: jzheaux
This appears to duplicate #8844. Let's continue the conversation over there.
For a quick reference to your specific use case, though, you can do this using DelegatingJwtGrantedAuthoritiesConverter:
JwtGrantedAuthoritiesConverter authoritiesConverter() {
JwtGrantedAuthoritiesConverter scp = new JwtGrantedAuthoritiesConverter();
JwtGrantedAuthoritiesConverter roles = new JwtGrantedAuthoritiesConverter();
roles.setAuthorityPrefix("ROLE_");
roles.setAuthorityClaimName("roles");
return new DelegatingJwtGrantedAuthoritiesConverter(scp, roles);
}
Comment From: chenrujun
@jzheaux Thank you for your response. I have another question: How about creating an constructor, so we can write code like this:
JwtGrantedAuthoritiesConverter authoritiesConverter() {
JwtGrantedAuthoritiesConverter scp = new JwtGrantedAuthoritiesConverter();
JwtGrantedAuthoritiesConverter roles = new JwtGrantedAuthoritiesConverter("roles", "ROLE_");
return new DelegatingJwtGrantedAuthoritiesConverter(scp, roles);
}
Comment From: jzheaux
@chenrujun, thanks for the suggestion. Please read up on #8844 if you haven't already as there is a similar suggestion there. We can continue trading ideas over there if you like.