Skip to content

Commit cc26ab3

Browse files
Konstantin PankratovKonstantin Pankratov
authored andcommitted
Implement Oauth2 resource server. Add Keycloak to docker compose
1 parent c7c7035 commit cc26ab3

File tree

7 files changed

+152
-11
lines changed

7 files changed

+152
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ target/
55
start_vpn.sh
66
server/generated-sources/
77
data/
8+
.env

docker-compose.yml

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
services:
2-
backend:
3-
build:
4-
context: .
5-
dockerfile: Dockerfile
6-
image: panderu/study-buddies-backend:latest
7-
container_name: backend
8-
ports:
9-
- "8080:8080"
2+
keycloak_web:
3+
image: keycloak/keycloak:latest
4+
container_name: kc-web
5+
environment:
6+
KC_DB: postgres
7+
KC_DB_URL: jdbc:postgresql://keycloakdb:5432/keycloak
8+
KC_DB_USERNAME: ${KC_DB_USERNAME}
9+
KC_DB_PASSWORD: ${KC_DB_PASSWORD}
10+
11+
KEYCLOAK_ADMIN: ${KC_DB_USERNAME}
12+
KEYCLOAK_ADMIN_PASSWORD: ${KC_DB_PASSWORD}
13+
14+
15+
KC_HOSTNAME: localhost
16+
KC_HOSTNAME_PORT: 8080
17+
KC_HOSTNAME_STRICT: 'false'
18+
KC_HOSTNAME_STRICT_HTTPS: 'false'
19+
KC_LOG_LEVEL: debug
20+
21+
KC_METRICS_ENABLED: 'true'
22+
KC_HEALTH_ENABLED: 'true'
23+
KC_PROXY: edge
24+
KC_PROXY_HEADERS: forwarded
25+
command: start-dev
26+
depends_on:
27+
- keycloakdb
28+
ports:
29+
- '7070:8080'
30+
######################################################
31+
keycloakdb:
32+
image: postgres:15
33+
volumes:
34+
- postgres_data:/var/lib/postgresql/data
35+
environment:
36+
POSTGRES_DB: keycloak
37+
POSTGRES_USER: ${KC_DB_USERNAME}
38+
POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
39+
######################################################
40+
volumes:
41+
postgres_data:

server/application.properties

Whitespace-only changes.

server/pom.xml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,46 @@
9999
<artifactId>bcpkix-jdk18on</artifactId>
100100
<version>1.78.1</version>
101101
</dependency>
102+
103+
<dependency>
104+
<groupId>org.keycloak</groupId>
105+
<artifactId>keycloak-spring-boot-starter</artifactId>
106+
<version>25.0.3</version>
107+
</dependency>
108+
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
109+
<dependency>
110+
<groupId>org.springframework.security</groupId>
111+
<artifactId>spring-security-core</artifactId>
112+
<version>6.4.2</version>
113+
</dependency>
114+
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
115+
<dependency>
116+
<groupId>org.springframework.security</groupId>
117+
<artifactId>spring-security-web</artifactId>
118+
<version>6.4.2</version>
119+
</dependency>
120+
121+
<dependency>
122+
<groupId>org.springframework.boot</groupId>
123+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
124+
</dependency>
125+
126+
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
127+
<dependency>
128+
<groupId>org.springframework.security</groupId>
129+
<artifactId>spring-security-config</artifactId>
130+
<version>6.4.2</version>
131+
</dependency>
132+
<dependency>
133+
<groupId>org.springframework.boot</groupId>
134+
<artifactId>spring-boot-starter-oauth2-client</artifactId>
135+
</dependency>
136+
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-oauth2-authorization-server -->
137+
<dependency>
138+
<groupId>org.springframework.security</groupId>
139+
<artifactId>spring-security-oauth2-authorization-server</artifactId>
140+
</dependency>
141+
102142
</dependencies>
103143

104144
<!-- Build section: Contains build-specific information such as plugins -->
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.studybuddies.server.configuration;
2+
3+
import java.util.Collection;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.stream.Collectors;
8+
import java.util.stream.Stream;
9+
import org.springframework.core.convert.converter.Converter;
10+
import org.springframework.lang.NonNull;
11+
import org.springframework.security.authentication.AbstractAuthenticationToken;
12+
import org.springframework.security.core.GrantedAuthority;
13+
import org.springframework.security.core.authority.SimpleGrantedAuthority;
14+
import org.springframework.security.oauth2.jwt.Jwt;
15+
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
16+
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
17+
18+
public class KeycloakJwtAuthenticationConverter implements
19+
Converter<Jwt, AbstractAuthenticationToken> {
20+
21+
22+
@Override
23+
public AbstractAuthenticationToken convert(@NonNull Jwt source) {
24+
return new JwtAuthenticationToken(
25+
source,
26+
Stream.concat(
27+
new JwtGrantedAuthoritiesConverter().convert(source).stream(),
28+
extractResourceRoles(source).stream()
29+
).collect(Collectors.toSet())
30+
);
31+
}
32+
33+
private Collection<? extends GrantedAuthority> extractResourceRoles(Jwt jwt) {
34+
var resourceAccess = new HashMap<>(jwt.getClaim("resource_access"));
35+
var eternal = (Map<String, List<String>>) resourceAccess.get("account");
36+
var roles = eternal.get("roles");
37+
return roles.stream()
38+
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.replace("-", "_"))).collect(
39+
Collectors.toSet());
40+
}
41+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.studybuddies.server.configuration;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7+
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
8+
import org.springframework.security.web.SecurityFilterChain;
9+
10+
@Configuration
11+
@EnableWebSecurity
12+
public class SecurityConfig {
13+
14+
@Bean
15+
protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
16+
http
17+
.csrf(AbstractHttpConfigurer::disable) // Disable csrf because of use of JWT Tokens
18+
.authorizeHttpRequests(req -> req.anyRequest().authenticated()); // Permit only authenticated requests
19+
http.oauth2ResourceServer(auth -> auth.jwt(token -> token.jwtAuthenticationConverter(new KeycloakJwtAuthenticationConverter())));
20+
21+
return http.build();
22+
}
23+
}

server/src/main/resources/application.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ spring:
55
name: server
66
datasource:
77
url: jdbc:h2:file:./data/demo
8-
username: sa
8+
username: sa #TODO: Extract this into a dev config application-dev.yml
99
password: password
1010
driverClassName: org.h2.Driver
1111
jpa:
@@ -15,6 +15,10 @@ spring:
1515
ddl-auto: update
1616
h2:
1717
console.enabled: true
18-
18+
security:
19+
oauth2:
20+
resourceserver:
21+
jwt:
22+
issuer-uri: "http://localhost:7070/realms/study-buddies"
1923
springdoc:
20-
swagger-ui.path: /api-docs
24+
swagger-ui.path: /api-docs

0 commit comments

Comments
 (0)