Reputation: 10024
I've Spring Security Oauth2 app, connected to OIDC server keycloak, I'm able to connect and get access token and perform authentication, however unable to perform Authorization.
The introspect of token responds with Authorities in json response as shown below.
{
"jti": "f21b1ecd-05b7-435b-a571-1b8554ae3666",
"exp": 1583995545,
"nbf": 0,
"iat": 1583994645,
"iss": "http://192.168.56.101:8080/auth/realms/master",
"sub": "e7462035-316e-4970-afde-e44ffd9f169e",
"typ": "Bearer",
"azp": "app1_client",
"auth_time": 1583994645,
"session_state": "7a36dc7f-dd5d-42cb-8684-398825fcacde",
"name": "Administrator 1",
"given_name": "Administrator",
"family_name": "1",
"preferred_username": "admin1",
"email_verified": false,
"acr": "1",
"resource_access": {
"app1_client": {
"roles": [
"APP1_ADMIN"
]
}
},
"scope": "email app1 profile",
"authorities": [
"ROLE_APP1_ADMIN"
],
"client_id": "app1_client",
"username": "admin1",
"active": true
}
However when I print Authorities
in log I'm unable to get the Authorities ROLE_APP1_ADMIN
instead in prints below log.
K-[ROLE_USER, SCOPE_address, SCOPE_app1, SCOPE_email, SCOPE_microprofile-jwt, SCOPE_offline_access, SCOPE_openid, SCOPE_phone, SCOPE_profile]
Below is HelloRest.java
@RestController
@Slf4j
@RequestMapping("/api")
public class HelloRest {
//@PreAuthorize("hasRole('APP1_ADMIN')")
@GetMapping("/admin")
public String admin(OAuth2AuthenticationToken e1) {
log.info("K-{}", e1.getAuthorities());
log.info("K-{}", e1.getAuthorizedClientRegistrationId());
log.info("K-{}", e1.getDetails());
log.info("K-{}", e1.getPrincipal().getAttributes());
log.info("K-{}", e1.getPrincipal().getAuthorities());
log.info("K-{}", e1.getName());
return "Hello from Admin of APP1";
}
@PreAuthorize("hasRole('APP1_USER')")
@GetMapping("/user")
public String user() {
return "Hello from User of APP1";
}
}
application.yml
server:
port: 8082
spring:
security:
oauth2:
# resourceserver:
# jwt:
# issuer-uri: http://192.168.56.101:8080/auth/realms/master
client:
provider:
keycloak:
issuer-uri: http://192.168.56.101:8080/auth/realms/master
registration:
keycloak:
client-id: app1_client
client-secret: <secret>
provider: keycloak
And finally pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.github.kprasad99</groupId>
<artifactId>app1-backend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>app1-backend-1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
How to apply Authorization using spring security with keycloak?
Upvotes: 2
Views: 2756
Reputation: 10024
From source code it looks like, we need to write custom mapper, spring security by default adds scope as role and default Role ROLE_USER
. Added below custom mapper.
@Component
@Slf4j
public class KGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper {
@Override
public Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(mappedAuthorities::add);
authorities.forEach(authority -> {
if (OidcUserAuthority.class.isInstance(authority)) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
OidcUserInfo userInfo = oidcUserAuthority.getUserInfo();
Optional.ofNullable(userInfo.getClaimAsStringList("authorities")).orElse(Collections.emptyList())
.stream().map(SimpleGrantedAuthority::new).forEach(mappedAuthorities::add);
// Map the claims found in idToken and/or userInfo
// to one or more GrantedAuthority's and add it to mappedAuthorities
} else if (OAuth2UserAuthority.class.isInstance(authority)) {
OAuth2UserAuthority oauth2UserAuthority = (OAuth2UserAuthority) authority;
Map<String, Object> userAttributes = oauth2UserAuthority.getAttributes();
log.info("{}", userAttributes);
// Map the attributes found in userAttributes
// to one or more GrantedAuthority's and add it to mappedAuthorities
// Not sure when this is being used
}
});
return mappedAuthorities;
}
}
Any configuration or in-built mappers available, please post, I will mark that as answer.
Upvotes: 0