From 5ca98df7caebf56990e26aa83a1a2b626ba16660 Mon Sep 17 00:00:00 2001 From: Kamal Wickramanayake Date: Mon, 6 Jul 2026 06:32:08 +0530 Subject: Microservices: Documentation improved --- microservices/02-api-gateway/README | 16 ++++++++++++++++ .../deb/package/etc/ceit-fs-api-gateway/application.yaml | 5 +++++ .../example/apigateway/config/RoleHeaderRelayFilter.java | 13 ++++++------- .../02-api-gateway/src/main/resources/application.yaml | 6 ++++++ 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 microservices/02-api-gateway/README (limited to 'microservices/02-api-gateway') diff --git a/microservices/02-api-gateway/README b/microservices/02-api-gateway/README new file mode 100644 index 0000000..310aeb0 --- /dev/null +++ b/microservices/02-api-gateway/README @@ -0,0 +1,16 @@ +This directory contains an API gateway implemented using Spring Cloud API Gateway. + +Look at the application.yaml as to how the API gateway forwards requests to backend services. + +Additional notes: + After a user successfully authenticates to the external OAuth2 authorization server and is redirected back to this API Gatway, the authorization server may not always return user role details. Some authorization servers may return such details but some others may not based on their configurations. Assuming the roles are defined within the specific application (specific backend services used and the React frontend) instead by the authorization server, the API gateway tries to find the user roles by accessing an internal REST service. This is coded in the SecurityConfig.java and uses the property app.userRolesUrl defined in application.yaml file. + + The JavaScript frontend application can call the /api/security/me endpoint of this API Gateway for it to find the roles of authenticated users (so that the link display visibility and route availability can be controlled within the JavaScript frontend application based on user roles.) Exposing this endpoint has been coded in the UserRestController.java file. + + During user authention this API gateway loads the role data (from the earlier said backend endpoint). After that, this API gateway adds the user roles to HTTP requests that are forworded to the backend services. This is coded in the RoleHeaderRelayFilter.java file. + + If a backend service needs to determine the roles of a user on behalf of whome a request is being served for, the backend service may use the HTTP headers sent by this API gateway. Keep in mind that this header information may not be trused by a backend service if the backend service is not deployed in an access restricted network. Otherwise, an attacker may send requests to the backend service with forged HTTP headers to force the backend service to assume different user roles. + + This API gateway has been configured to forward the OAuth2 access token of a user to the backend service. This is accomplished by the TokenRelay filter added to the routes defined in application.yaml. A backend service configured as an OAuth2 resource server will use this token to detect the user on behalf of whome the API gatway forwarded a request. The backend service can trust the username determined from the access token even if the backend service is not deployed in an access restricted network (as opposed to trusting the details in HTTP headers). + + Look at the README file of the sample resource server for further details about how the backend service determins the user and user roles. \ No newline at end of file diff --git a/microservices/02-api-gateway/linux/deb/package/etc/ceit-fs-api-gateway/application.yaml b/microservices/02-api-gateway/linux/deb/package/etc/ceit-fs-api-gateway/application.yaml index d037d5b..d5197fd 100644 --- a/microservices/02-api-gateway/linux/deb/package/etc/ceit-fs-api-gateway/application.yaml +++ b/microservices/02-api-gateway/linux/deb/package/etc/ceit-fs-api-gateway/application.yaml @@ -49,6 +49,7 @@ spring: - PrefixPath=/myservices - TokenRelay # Add oauth (or jwt) token to request header before forwarding + # Register this API gateway as an OAuth2 client to the Authorization server. security: oauth2: client: @@ -66,6 +67,10 @@ spring: scope: - openid - profile + +# Extra OAuth2 client configuration. Doing this configuration here is optional and can instead be +# done without the com.c4-soft.springaddons dependency. In that case, this configuration can be +# done in a custom security configuration class. This is just convenient compared to that option. com: c4-soft: springaddons: diff --git a/microservices/02-api-gateway/src/main/java/com/example/apigateway/config/RoleHeaderRelayFilter.java b/microservices/02-api-gateway/src/main/java/com/example/apigateway/config/RoleHeaderRelayFilter.java index e3bb241..0bdef6b 100644 --- a/microservices/02-api-gateway/src/main/java/com/example/apigateway/config/RoleHeaderRelayFilter.java +++ b/microservices/02-api-gateway/src/main/java/com/example/apigateway/config/RoleHeaderRelayFilter.java @@ -4,8 +4,6 @@ import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.oauth2.core.user.OAuth2User; @@ -33,11 +31,11 @@ public class RoleHeaderRelayFilter implements GlobalFilter, Ordered { .flatMap(authentication -> { if (authentication != null && authentication.getPrincipal() instanceof OAuth2User) { OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); - + // Extract username (adjust the attribute key based on your IdP provider) String username = oAuth2User.getAttribute("preferred_username"); if (username == null) { - username = oAuth2User.getName(); + username = oAuth2User.getName(); } // Extract roles/authorities @@ -48,13 +46,14 @@ public class RoleHeaderRelayFilter implements GlobalFilter, Ordered { // Mutate the request with new headers ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() .header("X-User-Roles", roles) - .header("X-User-Name", username) // Changed 'U-User-Name' to standard 'X-' format for consistency, or use your exact key + .header("X-User-Name", username) // Changed 'U-User-Name' to standard 'X-' format for + // consistency, or use your exact key .build(); // Proceed with the mutated exchange return chain.filter(exchange.mutate().request(mutatedRequest).build()); } - + // If not authenticated via OAuth2User, just pass through return chain.filter(exchange); }) @@ -64,6 +63,6 @@ public class RoleHeaderRelayFilter implements GlobalFilter, Ordered { @Override public int getOrder() { // Run after the Security Web Filter loads the context, but before routing - return Ordered.LOWEST_PRECEDENCE - 1; + return Ordered.LOWEST_PRECEDENCE - 1; } } diff --git a/microservices/02-api-gateway/src/main/resources/application.yaml b/microservices/02-api-gateway/src/main/resources/application.yaml index f51dbfe..4e1f9e7 100644 --- a/microservices/02-api-gateway/src/main/resources/application.yaml +++ b/microservices/02-api-gateway/src/main/resources/application.yaml @@ -2,6 +2,8 @@ server: port: 8050 address: 127.0.0.1 +# From where this API gatway can load roles of authenticated users? +# Look at the README file for details. app: userRolesUrl: "http://localhost:8052/myservices/security/open/users/{username}/roles" @@ -49,6 +51,7 @@ spring: - PrefixPath=/myservices - TokenRelay # Add oauth (or jwt) token to request header before forwarding + # Register this API gateway as an OAuth2 client with the OAuth2 authorization server security: oauth2: client: @@ -66,6 +69,9 @@ spring: scope: - openid - profile + +# Instead of coding a SecurityConfig class with OAuth2 configurations, com.c4-soft.springaddons dependency +# allows configuring some OAuth2 behaviour within this application.yaml file. This is just convenient. com: c4-soft: springaddons: -- cgit v1.2.3