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