From 350e1a8a43788def5c6d672af6b5c554ce6a2952 Mon Sep 17 00:00:00 2001 From: timoschneider Date: Tue, 11 Jun 2024 11:10:11 +0200 Subject: [PATCH] added keycloak --- pom.xml | 19 +++++- .../swajodel/security/JwtConverter.java | 60 +++++++++++++++++++ .../security/JwtConverterProperties.java | 19 ++++++ .../swajodel/security/SecurityConfig.java | 33 ++++++++++ src/main/resources/application.properties | 4 ++ 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/anxietyprime/swajodel/security/JwtConverter.java create mode 100644 src/main/java/de/anxietyprime/swajodel/security/JwtConverterProperties.java create mode 100644 src/main/java/de/anxietyprime/swajodel/security/SecurityConfig.java diff --git a/pom.xml b/pom.xml index 345a0ef..fe15c2b 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,24 @@ postgresql 42.7.3 - + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-security + + + org.projectlombok + lombok + true + + + org.springframework.security + spring-security-test + test + diff --git a/src/main/java/de/anxietyprime/swajodel/security/JwtConverter.java b/src/main/java/de/anxietyprime/swajodel/security/JwtConverter.java new file mode 100644 index 0000000..e59c25c --- /dev/null +++ b/src/main/java/de/anxietyprime/swajodel/security/JwtConverter.java @@ -0,0 +1,60 @@ +package de.anxietyprime.swajodel.security; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtClaimNames; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Component +public class JwtConverter implements Converter { + + private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + + private final JwtConverterProperties properties; + + public JwtConverter(JwtConverterProperties properties) { + this.properties = properties; + } + + @Override + public AbstractAuthenticationToken convert(Jwt jwt) { + Collection authorities = Stream.concat( + jwtGrantedAuthoritiesConverter.convert(jwt).stream(), + extractResourceRoles(jwt).stream()).collect(Collectors.toSet()); + return new JwtAuthenticationToken(jwt, authorities, getPrincipalClaimName(jwt)); + } + + private String getPrincipalClaimName(Jwt jwt) { + String claimName = JwtClaimNames.SUB; + if (properties.getPrincipalAttribute() != null) { + claimName = properties.getPrincipalAttribute(); + } + return jwt.getClaim(claimName); + } + + private Collection extractResourceRoles(Jwt jwt) { + Map resourceAccess = jwt.getClaim("resource_access"); + Map resource; + Collection resourceRoles; + + if (resourceAccess == null + || (resource = (Map) resourceAccess.get(properties.getResourceId())) == null + || (resourceRoles = (Collection) resource.get("roles")) == null) { + return Set.of(); + } + return resourceRoles.stream() + .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/java/de/anxietyprime/swajodel/security/JwtConverterProperties.java b/src/main/java/de/anxietyprime/swajodel/security/JwtConverterProperties.java new file mode 100644 index 0000000..41acec0 --- /dev/null +++ b/src/main/java/de/anxietyprime/swajodel/security/JwtConverterProperties.java @@ -0,0 +1,19 @@ +package de.anxietyprime.swajodel.security; + + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; + +@Data +@Validated +@Configuration +@ConfigurationProperties(prefix = "jwt.auth.converter") + + +public class JwtConverterProperties { + + private String resourceId; + private String principalAttribute; +} diff --git a/src/main/java/de/anxietyprime/swajodel/security/SecurityConfig.java b/src/main/java/de/anxietyprime/swajodel/security/SecurityConfig.java new file mode 100644 index 0000000..780ced2 --- /dev/null +++ b/src/main/java/de/anxietyprime/swajodel/security/SecurityConfig.java @@ -0,0 +1,33 @@ +package de.anxietyprime.swajodel.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; + +@RequiredArgsConstructor +@Configuration + @EnableWebSecurity +public class SecurityConfig { + + public static final String ADMIN = "admin"; + public static final String USER = "user"; + private final JwtConverter jwtConverter; + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests((authz) -> + authz.requestMatchers(HttpMethod.GET, "/messages/").permitAll() + .anyRequest().authenticated()); + + http.sessionManagement(sess -> sess.sessionCreationPolicy( + SessionCreationPolicy.STATELESS)); + http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter))); + + return http.build(); + } + +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f440c33..50fef08 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,5 @@ spring.application.name=SWA-Jodel + +# Security Configuration +spring.security.oauth2.resourceserver.jwt.issuer-uri=https://keycloak.anxietyprime.de/realms/Jodel +spring.security.oauth2.resourceserver.jwt.jwk-set-uri=${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs