I’ve got a very simple Spring Boot application with resources at /repositories and /persons.
Here is my build.gradle file.
plugins {
id 'org.springframework.boot' version '2.4.0'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
// use java 11 until keycloak is fixed
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencyManagement {
imports {
mavenBom "org.keycloak.bom:keycloak-adapter-bom:12.0.1"
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.keycloak:keycloak-spring-boot-starter'
implementation 'org.flywaydb:flyway-core'
runtime 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
...
...
Here is my SecurityConfig.java file.
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
var keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/persons*")
.hasRole("user")
.anyRequest()
.permitAll();
}
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
Here is my application.yaml file.
spring:
datasource:
url: jdbc:postgresql://localhost:5432/postgres
username: john
password: john
keycloak:
auth-server-url: http://localhost:8081/auth/
realm: myrealm
resource: myclient
credentials:
secret: 45d43bd6-5ab9-476c-83c8-67bd203a78ee
It’s all on my local machine and Keycloak and Postgres are started via docker compose.
version: "3.1"
volumes:
postgres_data:
driver: local
services:
db:
image: "postgres:13.1"
ports:
- "5432:5432"
environment:
POSTGRES_DB: postgres
POSTGRES_USER: john
POSTGRES_PASSWORD: john
postgres:
image: "postgres:13.1"
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
keycloak:
image: quay.io/keycloak/keycloak:12.0.1
environment:
DB_VENDOR: POSTGRES
DB_ADDR: postgres
DB_DATABASE: keycloak
DB_USER: keycloak
DB_SCHEMA: public
DB_PASSWORD: password
KEYCLOAK_USER: admin
KEYCLOAK_PASSWORD: Pa55w0rd
ports:
- 8081:8080
depends_on:
- postgres
I’ve got the users zemirco and one in Keycloak and they both have the user role. It all works very nicely and the route /persons is protected whereas /repositories is open for all.
Here is my problem! I’d like to secure individual resources, e.g. /person/1. I cannot make it work π I’ve been trying for days now but no luck. Here are my keycloak settings.
Here is my policy.
Here is the resource.
Here is the permission.
It all works during the evaluating at Keycloak. However it does not work directly at my Spring Boot application. I can still access /person/1 as zemirco although user one should only have access.
I’m a bit lost at the moment. Do you have any ideas?
Thank you very much!
Edit Jan 10 2021
Thank you all for your answers but I still think it’s doable without a lot of extra effort on my side (the application side). I’m especially looking at policy enforcers https://www.keycloak.org/docs/latest/authorization_services/#_enforcer_overview.
A PEP is responsible for enforcing access decisions from the Keycloak server where these decisions are taken by evaluating the policies associated with a protected resource. It acts as a filter or interceptor in your application in order to check whether or not a particular request to a protected resource can be fulfilled based on the permissions granted by these decisions.
This sound exactly like what I’m trying to do. Unfortunately I cannot make it work. I just think my configuration is false or maybe incomplete. I will add a bounty to the question.
Advertisement
Answer
It finally works π I’m happy!
Here is what I did.
First of all I used the keycloak.json file from my Keycloak client installation. At the beginning I tried to put all configuration into my application.yaml. The keycloak.json file must be located at src/main/webapp/WEB-INF/keycloak.json. When you’re using the JSON file instead of the YAML file you can remove the following from your SecurityConfig.
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
It’s actually quite nice. You can simply copy&paste the content from Keycloak into the file and you can be sure that everything is right. Another problem I saw is that the config in keycloak.json is called policy-enforcer compared to policy-enforcer-config in application.yaml. That’s quite annoying because I didn’t realize it at the beginning. Here is config.
{
"realm": "myrealm",
"auth-server-url": "http://localhost:8081/auth/",
"ssl-required": "external",
"resource": "myclient",
"verify-token-audience": true,
"credentials": {
"secret": "45d43bd6-5ab9-476c-83c8-67bd203a78ee"
},
"confidential-port": 0,
"policy-enforcer": {}
}
The "policy-enforcer": {} is the most important part. Here we enable the default settings and simply say that Keycloak should authorize all requests to our resources.
Then everything seemed to work. I was able to gain access to a protected resource only with the right user. However after I logged out and tried to access the resource again I was still able to do it. Here I learned that my ant matchers were wrong. This is the right configuration.
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/persons/**") // changed from .antMatchers("/persons*")
.hasRole("user")
.anyRequest()
.permitAll()
.and()
// this is just to have a convenient GET /logout method. DO NOT USE IN PRODUCTION
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
Now everything works and my resources/policies/permissions from Keycloak are automatically applied in my Spring Boot application.


