I am trying to modify code from a Keycloak and Spring Boot tutorial that I found online in various places (the code looks to have been duplicated). In any event, although Keycloak secures the proper URLs and Spring Security facilitates, when attempting to retrieve the Principal, it comes back as null. Here is my application.properties file:
keycloak.auth-server-url=http://localhost:9090/auth
keycloak.realm=<some realm>
keycloak.resource=<some resource>
keycloak.ssl-required=external
keycloak.public-client=true
keycloak.principal-attribute=preferred_username
keycloak.use-resource-role-mappings=true
server.port=8100
Here is my SecurityConfig.java file (pristine from the tutorials):
package com.bme.keycloakdemo.configuration;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
// Submits the KeycloakAuthenticationProvider to the AuthenticationManager
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
// Specifies the session authentication strategy
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/demo*")
.hasRole("user")
.anyRequest()
.permitAll();
}
}
My web controller follows:
package com.bme.keycloakdemo.controllers;
import java.security.Principal;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
public class DemoController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping(value = "/first", produces = {MediaType.APPLICATION_JSON_VALUE})
public String first(Principal principal) {
logger.info("WE GOT HERE");
String retval = "WE GOT HERE! ";
if (principal != null) {
retval += principal.toString();
}
else {
retval += "PRINCIPAL IS NULL";
}
return retval;
}
@GetMapping(value = "/second", produces = {MediaType.APPLICATION_JSON_VALUE})
public String second(Principal principal) {
logger.info("WE GOT HERE");
String retval = "WE GOT HERE! " + principal.getName();
return retval;
}
@GetMapping(value = "/third", produces = {MediaType.APPLICATION_JSON_VALUE})
public String third(HttpServletRequest request) {
logger.info("WE GOT HERE");
Principal principal = request.getUserPrincipal();
String retval = "WE GOT HERE! ";
if (principal != null) {
retval += principal.toString();
}
else {
retval += "PRINCIPAL IS NULL";
}
return retval;
}
}
Finally, here is my Maven configuration in case it makes a difference:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.batman-evolution</groupId>
<artifactId>keycloakjournal</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>keycloakjournal</name>
<description>Journal Secured with Keycloak</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.16.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<keycloak.version>3.4.0.Final</keycloak.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.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>
So to recap the question, I am looking to get the Principal from Keycloak. I am currently using Spring Security to return the Principal but really do not care one way or the other so long as the Principal is usable. Thanks for your help!
The Keycloak Spring Boot adapter capitalizes on Spring Boot's auto-configuration, so all we need to do is add the Keycloak Spring Boot starter to our project. The following embedded containers are supported now and don't require any extra dependencies if using Spring Boot Keycloak Starter: Tomcat. Undertow.
This worked for me:
@GetMapping("/details")
public MyUserDetails getUserDetails(Principal principal) {
KeycloakAuthenticationToken kp = (KeycloakAuthenticationToken) principal;
SimpleKeycloakAccount simpleKeycloakAccount = (SimpleKeycloakAccount) kp.getDetails();
AccesToken token = simpleKeycloakAccount.getKeycloakSecurityContext().getToken();
return new MyUserDetails(token.getGivenName(), token.getFamilyName(), token.getEmail());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With