[BAEL-5259] Spring Security - Map Authorities from JWT (#11990)
* [BAEL-4849] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Article code * [BAEL-4968] Remove extra comments * [BAEL-5259] simple test case * [BAEL-5259] DSL-based rewrite * [BAEL-5259] Code formatting * [BAEL-5259] Test case naming * WIP: Article code * [BAEL-5259] Tests * [BAEL-5259] Tests
This commit is contained in:
parent
2cda833322
commit
81e22da07c
|
@ -24,6 +24,10 @@
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
<artifactId>spring-boot-starter-oauth2-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.context.ApplicationContextInitializer;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.discovery.SpringOidcDiscoveryApplication;
|
||||||
|
import com.baeldung.openid.oidc.utils.YamlLoaderInitializer;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class SpringOidcJwtAuthoritiesApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication application = new SpringApplication(SpringOidcJwtAuthoritiesApplication.class);
|
||||||
|
ApplicationContextInitializer<ConfigurableApplicationContext> yamlInitializer = new YamlLoaderInitializer("jwtauthorities-application.yml");
|
||||||
|
application.addInitializers(yamlInitializer);
|
||||||
|
application.run(args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.domain.Account;
|
||||||
|
|
||||||
|
public class AccountToken extends JwtAuthenticationToken {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private final Account account;
|
||||||
|
|
||||||
|
public AccountToken(Jwt jwt, Collection<? extends GrantedAuthority> authorities, String name, Account account) {
|
||||||
|
super(jwt, authorities, name);
|
||||||
|
this.account = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account getAccount() {
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
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.security.oauth2.jwt.JwtClaimNames;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.domain.Account;
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.service.AccountService;
|
||||||
|
|
||||||
|
public class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
||||||
|
|
||||||
|
private final Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter;
|
||||||
|
private final String principalClaimName;
|
||||||
|
private AccountService accountService;
|
||||||
|
|
||||||
|
public CustomJwtAuthenticationConverter(AccountService accountService, Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter, String principalClaimName) {
|
||||||
|
this.accountService = accountService;
|
||||||
|
this.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;
|
||||||
|
this.principalClaimName = principalClaimName != null ? principalClaimName : JwtClaimNames.SUB;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractAuthenticationToken convert(Jwt source) {
|
||||||
|
|
||||||
|
Collection<GrantedAuthority> authorities = jwtGrantedAuthoritiesConverter.convert(source);
|
||||||
|
String principalClaimValue = source.getClaimAsString(this.principalClaimName);
|
||||||
|
Account acc = accountService.findAccountByPrincipal(principalClaimValue);
|
||||||
|
return new AccountToken(source, authorities, principalClaimValue, acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "baeldung.jwt.mapping")
|
||||||
|
public class JwtMappingProperties {
|
||||||
|
|
||||||
|
private String authoritiesPrefix;
|
||||||
|
private String authoritiesClaimName;
|
||||||
|
private String principalClaimName;
|
||||||
|
private Map<String,String> scopes;
|
||||||
|
|
||||||
|
public String getAuthoritiesClaimName() {
|
||||||
|
return authoritiesClaimName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthoritiesClaimName(String authoritiesClaimName) {
|
||||||
|
this.authoritiesClaimName = authoritiesClaimName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthoritiesPrefix() {
|
||||||
|
return authoritiesPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthoritiesPrefix(String authoritiesPrefix) {
|
||||||
|
this.authoritiesPrefix = authoritiesPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getPrincipalClaimName() {
|
||||||
|
return principalClaimName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrincipalClaimName(String principalClaimName) {
|
||||||
|
this.principalClaimName = principalClaimName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String,String> getScopes() {
|
||||||
|
return scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScopes(Map<String,String> scopes) {
|
||||||
|
this.scopes = scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class MappingJwtGrantedAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> {
|
||||||
|
|
||||||
|
private static final Collection<String> WELL_KNOWN_AUTHORITIES_CLAIM_NAMES = Arrays.asList("scope", "scp");
|
||||||
|
|
||||||
|
private final Map<String,String> scopes;
|
||||||
|
private String authoritiesClaimName = null;
|
||||||
|
private String authorityPrefix = "SCOPE_";
|
||||||
|
|
||||||
|
MappingJwtGrantedAuthoritiesConverter(Map<String,String> scopes) {
|
||||||
|
this.scopes = scopes == null ? Collections.emptyMap(): scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthoritiesClaimName(String authoritiesClaimName) {
|
||||||
|
this.authoritiesClaimName = authoritiesClaimName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthorityPrefix(String authorityPrefix) {
|
||||||
|
this.authorityPrefix = authorityPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<GrantedAuthority> convert(Jwt jwt) {
|
||||||
|
|
||||||
|
Collection<String> tokenScopes = parseScopesClaim(jwt);
|
||||||
|
if ( tokenScopes.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokenScopes.stream()
|
||||||
|
.map(s -> scopes.getOrDefault(s, s))
|
||||||
|
.map(s -> this.authorityPrefix + s)
|
||||||
|
.map(SimpleGrantedAuthority::new)
|
||||||
|
.collect(Collectors.toCollection(HashSet::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Collection<String> parseScopesClaim(Jwt jwt) {
|
||||||
|
|
||||||
|
String scopeClaim;
|
||||||
|
|
||||||
|
if ( this.authoritiesClaimName == null ) {
|
||||||
|
scopeClaim = WELL_KNOWN_AUTHORITIES_CLAIM_NAMES.stream()
|
||||||
|
.filter( claim -> jwt.hasClaim(claim))
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
if ( scopeClaim == null ) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
scopeClaim = this.authoritiesClaimName;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object v = jwt.getClaim(scopeClaim);
|
||||||
|
if ( v == null ) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( v instanceof String) {
|
||||||
|
return Arrays.asList(v.toString().split(" "));
|
||||||
|
}
|
||||||
|
else if ( v instanceof Collection ) {
|
||||||
|
return ((Collection<?>)v).stream()
|
||||||
|
.map( s -> s.toString())
|
||||||
|
.collect(Collectors.toCollection(HashSet::new));
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.service.AccountService;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(JwtMappingProperties.class)
|
||||||
|
@EnableMethodSecurity
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private final JwtMappingProperties mappingProps;
|
||||||
|
private final AccountService accountService;
|
||||||
|
|
||||||
|
public SecurityConfig(JwtMappingProperties mappingProps, AccountService accountService) {
|
||||||
|
this.mappingProps = mappingProps;
|
||||||
|
this.accountService = accountService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public String jwtGrantedAuthoritiesPrefix() {
|
||||||
|
return mappingProps.getAuthoritiesPrefix();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter() {
|
||||||
|
MappingJwtGrantedAuthoritiesConverter converter = new MappingJwtGrantedAuthoritiesConverter(mappingProps.getScopes());
|
||||||
|
|
||||||
|
if (StringUtils.hasText(mappingProps.getAuthoritiesPrefix())) {
|
||||||
|
converter.setAuthorityPrefix(mappingProps.getAuthoritiesPrefix()
|
||||||
|
.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(mappingProps.getAuthoritiesClaimName())) {
|
||||||
|
converter.setAuthoritiesClaimName(mappingProps.getAuthoritiesClaimName());
|
||||||
|
}
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Converter<Jwt,AbstractAuthenticationToken> customJwtAuthenticationConverter(AccountService accountService) {
|
||||||
|
return new CustomJwtAuthenticationConverter(
|
||||||
|
accountService,
|
||||||
|
jwtGrantedAuthoritiesConverter(),
|
||||||
|
mappingProps.getPrincipalClaimName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain customJwtSecurityChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http.oauth2ResourceServer(oauth2 -> {
|
||||||
|
oauth2.jwt()
|
||||||
|
.jwtAuthenticationConverter(customJwtAuthenticationConverter(accountService));
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.domain;
|
||||||
|
|
||||||
|
public class Account {
|
||||||
|
|
||||||
|
private final Long id;
|
||||||
|
private final String branch;
|
||||||
|
private final String accountNumber;
|
||||||
|
|
||||||
|
public Account(Long id, String branch, String accountNumber) {
|
||||||
|
this.id = id;
|
||||||
|
this.branch = branch;
|
||||||
|
this.accountNumber = accountNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBranch() {
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountNumber() {
|
||||||
|
return accountNumber;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.service;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.domain.Account;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class AccountService {
|
||||||
|
|
||||||
|
public Account findAccountByPrincipal(String principal) {
|
||||||
|
// NOTE: real-world code would typically perform some sort of DB lookup
|
||||||
|
return new Account(1l, "0001", "101888444-0");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.web.controllers;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.config.AccountToken;
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.domain.Account;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/user")
|
||||||
|
public class UserRestController {
|
||||||
|
|
||||||
|
@GetMapping("/authorities")
|
||||||
|
@PreAuthorize("hasAuthority(@jwtGrantedAuthoritiesPrefix + 'profile.read')")
|
||||||
|
public Map<String,Object> getPrincipalInfo( JwtAuthenticationToken principal) {
|
||||||
|
|
||||||
|
Collection<String> authorities = principal.getAuthorities()
|
||||||
|
.stream()
|
||||||
|
.map(GrantedAuthority::getAuthority)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
Map<String,Object> info = new HashMap<>();
|
||||||
|
info.put("name", principal.getName());
|
||||||
|
info.put("authorities", authorities);
|
||||||
|
info.put("tokenAttributes", principal.getTokenAttributes());
|
||||||
|
|
||||||
|
if ( principal instanceof AccountToken ) {
|
||||||
|
info.put( "account", ((AccountToken)principal).getAccount());
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/account/{accountNumber}")
|
||||||
|
@PreAuthorize("authentication.account.accountNumber == #accountNumber")
|
||||||
|
public Account getAccountById(@PathVariable("accountNumber") String accountNumber, AccountToken authentication) {
|
||||||
|
return authentication.getAccount();
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,4 +4,3 @@ server:
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
org.springframework.web.client.RestTemplate: DEBUG
|
org.springframework.web.client.RestTemplate: DEBUG
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
spring:
|
||||||
|
security:
|
||||||
|
oauth2:
|
||||||
|
resourceserver:
|
||||||
|
jwt:
|
||||||
|
# issuer-uri: https://sts.windows.net/2e9fde3a-38ec-44f9-8bcd-c184dc1e8033/
|
||||||
|
issuer-uri: http://localhost:8083/auth/realms/baeldung
|
||||||
|
baeldung:
|
||||||
|
jwt:
|
||||||
|
mapping:
|
||||||
|
authorities-prefix: "MY_SCOPE_"
|
||||||
|
principal-claim-name: preferred_username
|
||||||
|
scopes:
|
||||||
|
profile: profile.read
|
||||||
|
"profile_read": profile.read
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
import com.baeldung.openid.oidc.jwtauthorities.service.AccountService;
|
||||||
|
|
||||||
|
class CustomJwtAuthenticationConverterUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGivenCustomJwtAuthenticationConverter_whenConvert_thenReturnAccountToken() {
|
||||||
|
|
||||||
|
AccountService accountService = new AccountService();
|
||||||
|
MappingJwtGrantedAuthoritiesConverter authoritiesConverter = new MappingJwtGrantedAuthoritiesConverter(new HashMap<>());
|
||||||
|
|
||||||
|
CustomJwtAuthenticationConverter converter = new CustomJwtAuthenticationConverter(
|
||||||
|
accountService, authoritiesConverter, null);
|
||||||
|
|
||||||
|
Jwt jwt = Jwt.withTokenValue("NOTUSED")
|
||||||
|
.header("typ", "JWT")
|
||||||
|
.subject("user")
|
||||||
|
.claim("scp", "openid email profile")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Object auth = converter.convert(jwt);
|
||||||
|
assertTrue(auth instanceof AccountToken, "Authentication must be instance of AccountToken");
|
||||||
|
AccountToken token = AccountToken.class.cast(auth);
|
||||||
|
|
||||||
|
assertEquals("user", token.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGivenCustomPrincipalClaimName_whenConvert_thenReturnAccountToken() {
|
||||||
|
|
||||||
|
AccountService accountService = new AccountService();
|
||||||
|
MappingJwtGrantedAuthoritiesConverter authoritiesConverter = new MappingJwtGrantedAuthoritiesConverter(new HashMap<>());
|
||||||
|
|
||||||
|
CustomJwtAuthenticationConverter converter = new CustomJwtAuthenticationConverter(
|
||||||
|
accountService, authoritiesConverter, "preferred_username");
|
||||||
|
|
||||||
|
Jwt jwt = Jwt.withTokenValue("NOTUSED")
|
||||||
|
.header("typ", "JWT")
|
||||||
|
.claim("preferred_username", "user")
|
||||||
|
.claim("scp", "openid email profile")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Object auth = converter.convert(jwt);
|
||||||
|
assertTrue(auth instanceof AccountToken, "Authentication must be instance of AccountToken");
|
||||||
|
AccountToken token = AccountToken.class.cast(auth);
|
||||||
|
|
||||||
|
assertEquals("user", token.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.baeldung.openid.oidc.jwtauthorities.config;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
|
||||||
|
class MappingJwtGrantedAuthoritiesConverterUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGivenConverterWithScopeMap_whenConvert_thenResultHasMappedAuthorities() {
|
||||||
|
Jwt jwt = Jwt.withTokenValue("NOTUSED")
|
||||||
|
.header("typ", "JWT")
|
||||||
|
.subject("user")
|
||||||
|
.claim("scp", "openid email profile")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, String> scopeMap = new HashMap<>();
|
||||||
|
scopeMap.put("profile", "profile.read");
|
||||||
|
MappingJwtGrantedAuthoritiesConverter converter = new MappingJwtGrantedAuthoritiesConverter(scopeMap);
|
||||||
|
Collection<GrantedAuthority> result = converter.convert(jwt);
|
||||||
|
|
||||||
|
assertTrue("Result must contain the authoriry 'SCOPE_profile.read'",
|
||||||
|
result.contains(new SimpleGrantedAuthority("SCOPE_profile.read")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGivenConverterWithCustomScopeClaim_whenConvert_thenResultHasAuthorities() {
|
||||||
|
Jwt jwt = Jwt.withTokenValue("NOTUSED")
|
||||||
|
.header("typ", "JWT")
|
||||||
|
.subject("user")
|
||||||
|
.claim("myscope_claim", "openid email profile")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, String> scopeMap = new HashMap<>();
|
||||||
|
MappingJwtGrantedAuthoritiesConverter converter = new MappingJwtGrantedAuthoritiesConverter(scopeMap);
|
||||||
|
converter.setAuthoritiesClaimName("myscope_claim");
|
||||||
|
Collection<GrantedAuthority> result = converter.convert(jwt);
|
||||||
|
|
||||||
|
assertTrue("Result must contain the authoriry 'SCOPE_profile'",
|
||||||
|
result.contains(new SimpleGrantedAuthority("SCOPE_profile")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGivenTokenWithNonMappedScope_whenConvert_thenResultHasOriginalScope() {
|
||||||
|
Jwt jwt = Jwt.withTokenValue("NOTUSED")
|
||||||
|
.header("typ", "JWT")
|
||||||
|
.subject("user")
|
||||||
|
.claim("scp", "openid email profile custom")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, String> scopeMap = new HashMap<>();
|
||||||
|
scopeMap.put("profile", "profile.read");
|
||||||
|
MappingJwtGrantedAuthoritiesConverter converter = new MappingJwtGrantedAuthoritiesConverter(scopeMap);
|
||||||
|
Collection<GrantedAuthority> result = converter.convert(jwt);
|
||||||
|
|
||||||
|
assertTrue("Result must contain the authority SCOPE_custom",
|
||||||
|
result.contains(new SimpleGrantedAuthority("SCOPE_custom")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGivenConverterWithCustomPrefix_whenConvert_thenAllAuthoritiesMustHaveTheCustomPrefix() {
|
||||||
|
Jwt jwt = Jwt.withTokenValue("NOTUSED")
|
||||||
|
.header("typ", "JWT")
|
||||||
|
.subject("user")
|
||||||
|
.claim("scp", "openid email profile custom")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Map<String, String> scopeMap = new HashMap<>();
|
||||||
|
scopeMap.put("profile", "profile.read");
|
||||||
|
MappingJwtGrantedAuthoritiesConverter converter = new MappingJwtGrantedAuthoritiesConverter(scopeMap);
|
||||||
|
converter.setAuthorityPrefix("MY_SCOPE");
|
||||||
|
Collection<GrantedAuthority> result = converter.convert(jwt);
|
||||||
|
|
||||||
|
long count = result.stream()
|
||||||
|
.map(GrantedAuthority::getAuthority)
|
||||||
|
.filter(s -> !s.startsWith("MY_SCOPE"))
|
||||||
|
.count();
|
||||||
|
|
||||||
|
assertTrue("All authorities names must start with custom prefix", count == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue