Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Use an official OpenJDK runtime as a parent image
FROM debian:latest

USER root

# Set the working directory inside the container
WORKDIR /app

COPY ./ ./

RUN apt-get update && apt-get install -y maven openjdk-21-jdk

RUN mvn clean install
EXPOSE 8080

ENTRYPOINT ["mvn", "spring-boot:run"]
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,28 @@ The project uses the following key dependencies:
- `spring-boot-starter-security`: For securing the application with basic authentication.
- `springdoc-openapi-ui`: For generating OpenAPI documentation and Swagger UI.
- `spring-boot-starter-actuator`: For monitoring and managing the application.


## Security && rbac
Endpoint Access/Security is configured in package com.ase.userservice.security via SecurityConfig.java.

To add a new rule, you need to fill the function parameters for this snippet:
```java
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/demo").hasRole("DEFAULT-ROLES-SAU")
.requestMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter))
);
```

Specifically you need to add the following line for each protected route and role:
```java
.requestMatchers("/<your-route>").hasRole("<your-role>")
```
Glob pattern matching is supported.

If you need more infos regarding secuirty, visit our documentation page: [placeholder](http://example.com)
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,13 @@
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>
15 changes: 15 additions & 0 deletions src/main/java/com/ase/userservice/controllers/DemoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ase.userservice.controllers;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DemoController {

// to manage access, add route rules in security/SecurityConfig.java like in the
// examples
@GetMapping("/demo")
public String demo() {
return "Hello from DemoController!";
}
}
26 changes: 26 additions & 0 deletions src/main/java/com/ase/userservice/entities/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ase.userservice.entities;

import java.util.ArrayList;

public class User {
public int exp;
public int iat;
public int auth_time;
public String jti;
public String iss;
public String aud;
public String sub;
public String typ;
public String azp;
public String sid;
public String at_hash;
public String acr;
public String upn;
public boolean email_verified;
public String name;
public ArrayList<String> groups;
public String preferred_username;
public String given_name;
public String family_name;
public String email;
}
27 changes: 27 additions & 0 deletions src/main/java/com/ase/userservice/security/JwtAuthConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// based on this tutorial: https://www.javacodegeeks.com/2025/07/spring-boot-keycloak-role-based-authorization.html

package com.ase.userservice.security;

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;
import org.springframework.lang.NonNull;
import java.util.Collection;
import java.util.stream.Collectors;

public class JwtAuthConverter implements Converter<Jwt, Collection<GrantedAuthority>> {

@Override
public Collection<GrantedAuthority> convert(@NonNull Jwt jwt) {
var roles = jwt.getClaimAsStringList("groups");

// you can check the roles here if you want to
//for (String role : roles) {
//System.out.println("Role from JWT: " + role);
// }
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
.collect(Collectors.toList());
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/ase/userservice/security/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// based on this tutorial: https://www.javacodegeeks.com/2025/07/spring-boot-keycloak-role-based-authorization.html

package com.ase.userservice.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableMethodSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(new JwtAuthConverter());

// the role always has to be capatalized
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/demo").hasRole("DEFAULT-ROLES-SAU")
.requestMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter)));
return http.build();
}
}
7 changes: 7 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ spring:
driverClassName: org.h2.Driver
username: sa
password: password
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://keycloak.sau-portal.de/realms/sau
jwk-set-uri: https://keycloak.sau-portal.de/realms/sau/protocol/openid-connect/certs
server:
port: 8080
error:
include-message: always
Loading