I have developed a Spring Boot Webservice and use Keycloak for Access Management. The website stores some userdata in a database. I try to connect these data with the user logged in.
At the moment I store the username with the data. But I like to store the user id instead the username. How can I do that?
I try to get SecurityContext by this:
@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakSecurityContext getKeycloakSecurityContext() {
return ((KeycloakPrincipal<KeycloakSecurityContext>) getRequest().getUserPrincipal()).getKeycloakSecurityContext();
}
But I get an error:
There was an unexpected error (type=Internal Server Error, status=500).
Error creating bean with name 'scopedTarget.getKeycloakSecurityContext'
defined in com.SiteApplication: Bean instantiation via factory method
failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.keycloak.KeycloakSecurityContext]: Factory method
'getKeycloakSecurityContext' threw exception; nested exception is
java.lang.ClassCastException:
org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
cannot be cast to org.keycloak.KeycloakPrincipal
Is this the right way? What is missing?
Thank you!
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.
Keycloak is an open-source Identity and Access Management solution administered by RedHat and developed in Java by JBoss. In this tutorial, we'll learn how to set up a Keycloak server embedded in a Spring Boot application. This makes it easy to start up a pre-configured Keycloak server.
Navigate to the Postman Authorization tab of your request. From the Type dropdown menu, select OAuth 2.0: Click on the Get New Access Token button that will open a dialog box for configuring the identity server (Keycloak in our case).
I found a much simpler solution than the above:
@GetMapping
public ResponseEntity getEndpoint(String someParam, HttpServletRequest request) {
KeycloakAuthenticationToken principal = (KeycloakAuthenticationToken) request.getUserPrincipal();
String userId = principal.getAccount().getKeycloakSecurityContext().getIdToken().getSubject();
//....do something
return new ResponseEntity(HttpStatus.OK);
}
I think the exception you are getting above is because you are trying to cast getRequest().getUserPrincipal()
to KeycloakPrincipal<KeycloakSecurityContext>
while it is of type KeycloakAuthenticationToken
, so ((KeycloakAuthenticationToken) getRequest().getUserPrincipal())
would work.
I've done something similar in our code.
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting a HTTP request");
}
RefreshableKeycloakSecurityContext context = (RefreshableKeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
if (context == null) {
handleNoSecurityContext(request, response, chain);
return;
}
AccessToken accessToken = context.getToken();
Integer userId = Integer.parseInt(accessToken.getOtherClaims().get("user_id").toString());
chain.doFilter(request, response);
}
}
Before you can do this you must add the user_id
to the access tokens being issued by keycloak. You can do this through a mapper as shown in the screenshot below.
Also, don't forgot to add the processing filter from above to your application lifecycle by adding a @Bean
method to your application class.
@Configuration
@EnableAutoConfiguration(exclude = {FallbackWebSecurityAutoConfiguration.class, SpringBootWebSecurityConfiguration.class, DataSourceAutoConfiguration.class})
@ComponentScan
@EnableAsync
public class MyServiceClass {
public static void main(String[] args) {
Properties properties = new Properties();
new SpringApplicationBuilder(MyServiceClass.class)
.properties(properties)
.bannerMode(Banner.Mode.OFF)
.run(args);
}
@Bean
public AuthenticationTokenProcessingFilter authFilter() {
return new AuthenticationTokenProcessingFilter();
}
}
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