1
+ package io .javatab .microservices .composite .course .config ;
2
+
3
+ import org .slf4j .Logger ;
4
+ import org .slf4j .LoggerFactory ;
5
+ import org .springframework .context .annotation .Bean ;
6
+ import org .springframework .context .annotation .Configuration ;
7
+ import org .springframework .core .convert .converter .Converter ;
8
+ import org .springframework .security .config .Customizer ;
9
+ import org .springframework .security .config .annotation .web .reactive .EnableWebFluxSecurity ;
10
+ import org .springframework .security .config .web .server .SecurityWebFiltersOrder ;
11
+ import org .springframework .security .config .web .server .ServerHttpSecurity ;
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 .jwt .NimbusReactiveJwtDecoder ;
16
+ import org .springframework .security .oauth2 .jwt .ReactiveJwtDecoder ;
17
+ import org .springframework .security .oauth2 .server .resource .authentication .JwtAuthenticationToken ;
18
+ import org .springframework .security .web .server .SecurityWebFilterChain ;
19
+ import org .springframework .web .server .ServerWebExchange ;
20
+ import reactor .core .publisher .Mono ;
21
+
22
+ import java .util .ArrayList ;
23
+ import java .util .Collection ;
24
+ import java .util .List ;
25
+ import java .util .Map ;
26
+
27
+ @ Configuration
28
+ @ EnableWebFluxSecurity
29
+ public class SecurityConfig {
30
+
31
+ private static final Logger logger = LoggerFactory .getLogger (SecurityConfig .class );
32
+
33
+ @ Bean
34
+ public SecurityWebFilterChain securityFilterChain (ServerHttpSecurity http ) {
35
+ http
36
+ .authorizeExchange (exchanges -> exchanges
37
+ .pathMatchers ("/api/course-aggregate" ).hasRole ("COURSE-AGGREGATE-READ" )
38
+ .anyExchange ().authenticated ()
39
+ )
40
+ .oauth2ResourceServer (oauth2 -> oauth2
41
+ .jwt (jwt -> jwt .jwtAuthenticationConverter (grantedAuthoritiesExtractor ()))
42
+ );
43
+
44
+ // Add filter to log roles
45
+ http .addFilterAt ((exchange , chain ) -> logRoles (exchange ).then (chain .filter (exchange )),
46
+ SecurityWebFiltersOrder .AUTHORIZATION );
47
+
48
+ return http .build ();
49
+ }
50
+
51
+ @ Bean
52
+ public ReactiveJwtDecoder jwtDecoder () {
53
+ return NimbusReactiveJwtDecoder .withJwkSetUri ("http://localhost:8081/realms/course-management-realm/protocol/openid-connect/certs" ).build ();
54
+ }
55
+
56
+ @ Bean
57
+ public Converter <Jwt , Mono <JwtAuthenticationToken >> grantedAuthoritiesExtractor () {
58
+ return new Converter <Jwt , Mono <JwtAuthenticationToken >>() {
59
+ @ Override
60
+ public Mono <JwtAuthenticationToken > convert (Jwt jwt ) {
61
+ Collection <GrantedAuthority > authorities = new ArrayList <>();
62
+
63
+ // Extract realm roles
64
+ Map <String , Object > realmAccess = jwt .getClaim ("realm_access" );
65
+ if (realmAccess != null && realmAccess .containsKey ("roles" )) {
66
+ List <String > roles = (List <String >) realmAccess .get ("roles" );
67
+ authorities .addAll (roles .stream ()
68
+ .map (role -> new SimpleGrantedAuthority ("ROLE_" + role .toUpperCase ()))
69
+ .toList ());
70
+ }
71
+
72
+ // Extract client roles (replace "my-resource-server" with your client ID)
73
+ /*Map<String, Object> resourceAccess = jwt.getClaim("resource_access");
74
+ if (resourceAccess != null) {
75
+ Map<String, Object> clientRoles = (Map<String, Object>) resourceAccess.get("my-resource-server");
76
+ if (clientRoles != null && clientRoles.containsKey("roles")) {
77
+ List<String> roles = (List<String>) clientRoles.get("roles");
78
+ authorities.addAll(roles.stream()
79
+ .map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
80
+ .toList());
81
+ }
82
+ }*/
83
+ Map <String , Object > resourceAccess = jwt .getClaim ("resource_access" );
84
+ if (resourceAccess != null ) {
85
+ resourceAccess .forEach ((resource , access ) -> {
86
+ if (access instanceof Map ) {
87
+ Map <String , Object > clientRoles = (Map <String , Object >) access ;
88
+ if (clientRoles .containsKey ("roles" )) {
89
+ List <String > roles = (List <String >) clientRoles .get ("roles" );
90
+ authorities .addAll (roles .stream ()
91
+ .map (role -> new SimpleGrantedAuthority ("ROLE_" + role .toUpperCase ()))
92
+ .toList ());
93
+ }
94
+ }
95
+ });
96
+ }
97
+
98
+ return Mono .just (new JwtAuthenticationToken (jwt , authorities ));
99
+ }
100
+ };
101
+ }
102
+
103
+ private Mono <Void > logRoles (ServerWebExchange exchange ) {
104
+ return exchange .getPrincipal ()
105
+ .cast (JwtAuthenticationToken .class )
106
+ .doOnNext (jwtAuth -> {
107
+ Collection <? extends GrantedAuthority > authorities = jwtAuth .getAuthorities ();
108
+ logger .info ("Roles in Resource Server: {}" , authorities );
109
+ })
110
+ .then ();
111
+ }
112
+ }
0 commit comments