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