This article is participating in the Java Theme Month – Java Debug Notes EventActive link
background
Here’s the thing: I want to make a custom interceptor. Every time I request the SpringCloudGateway service, the Gateway intercepts a specific URL and authenticates the user to see if it has permission to manipulate the URL.
However, the routing rule configured in application. Yml does not take effect. For example, WHEN I want to access the gateway IP address :port/user-server/UserService/login, I want to authenticate (implemented in RequestAuthHeaderGateway), but the request is not blocked.
Note: User-server is the value of spring.application.name= for one of my SpringCloud services
server:
port: 80
spring:
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true The service name is lowercase
enabled: true # indicates that the Gateway has enabled service registration and discovery, and that spring Cloud Gateway automatically creates a router for each service based on service discovery
routes:
- id: user
uri: lb://user-server
predicates:
- Path=/user-server/**
filters:
- StripPrefix=1
- RequestAuthHeader
Copy the code
Problems arising
The IP address of the gateway accessing the SpringCloud component :port/user-server/login is not blocked by the Path of routes in the application-yml file. Guess what.
Welcome to pay attention to the public number, the article faster step
My public account: Tibet thinking
Nuggets of gold: Hide Kelvin
Jane: Hide Kelvin
My Gitee: Underground Collection Kelvin gitee.com/kelvin-cai
The prerequisite knowledge of problem solving
-
To understand the GatewayFilter implementation
-
To understand the GatewayFilterFactory implementation
-
Nacos registration mechanism
-
Learn about SpringCloudGateway enabling discovery of other services in the registry and creating default routing rules for all services.
The rule named/service name /* calls the url of /* under the service
Spring: Cloud: gateway: Discovery: locator: lowerCaseServiceId: true True # indicates that the Gateway enables service registration and discovery, and that the Spring Cloud Gateway automatically creates a router for each service based on service discoveryCopy the code
- Gateway service IP address :port: urIs
spring:
cloud:
gateway:
routes:
- id: user
uri: lb://user-server
predicates:
- Path=/user-server/**
filters:
- StripPrefix=1
- RequestAuthHeader
Copy the code
- Custom interceptor code
This is my custom GatewayFilterFactory code to implement the logic to intercept authentication. The code logic is fine.
public class RequestAuthHeaderGatewayFilterFactory
extends AbstractGatewayFilterFactory<RequestAuthHeaderGatewayFilterFactory.Config> {
public RequestAuthHeaderGatewayFilterFactory(a) {
super(Config.class);
}
public static class Config {
// TODO: relaxed HttpStatus converter
private String status;
public String getStatus(a) {
return status;
}
public void setStatus(String status) {
this.status = status; }}@Override
public GatewayFilter apply(Config config) {
return newRequestAuthHeaderFilter(); }}Copy the code
Troubleshooting phase
- Find where your code is being called
- In the custom RequestAuthHeaderGatewayFilterFactory, to interrupt the apply method.
@Override
public GatewayFilter apply(Config config) {
return new RequestAuthHeaderFilter();
}
Copy the code
- Findings are RouteDefinitionRouteLocator loadGatewayFilters this method call.
@SuppressWarnings("unchecked") List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) { ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size()); for (int i = 0; i < filterDefinitions.size(); i++) { FilterDefinition definition = filterDefinitions.get(i); GatewayFilterFactory factory = this.gatewayFilterFactories .get(definition.getName()); if (factory == null) { throw new IllegalArgumentException( "Unable to find GatewayFilterFactory with name " + definition.getName()); } if (logger.isDebugEnabled()) { logger.debug("RouteDefinition " + id + " applying filter " + definition.getArgs() + " to " + definition.getName()); } // @formatter:off Object configuration = this.configurationService.with(factory) .name(definition.getName()) .properties(definition.getArgs()) .eventFunction((bound, properties) -> new FilterArgsEvent( // TODO: why explicit cast needed or java compile fails RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties)) .bind(); // @formatter:on // some filters require routeId // TODO: is there a better place to apply this? if (configuration instanceof HasRouteId) { HasRouteId hasRouteId = (HasRouteId) configuration; hasRouteId.setRouteId(id); GatewayFilter GatewayFilter = factory. Apply (configuration); if (gatewayFilter instanceof Ordered) { ordered.add(gatewayFilter); } else { ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1)); } } return ordered; }Copy the code
You can see that the Routes interceptor configured in YML is registered here. The interceptor that matches the URL here is each gatewayFilter implementation class under an ID in YML.
- Investigation mechanism for the transferred area
- Interrupt the first line of the loadGatewayFilters method. Found that this method is executed periodically.
loadGatewayFilters(String id,List<FilterDefinition> filterDefinitions)
Copy the code
- But what does he do regularly?
I see the method’s parameter ID, which is the name of all the services in nacOS. Look at the second parameter above, filterDefinitions is the gateWayfilter to be processed.
- Look again at the filterDefinitions parameter.
[FilterDefinition{name='RewritePath', args={regexp=/user-server/(?<remaining>.*), replacement=/${remaining}}}]
Copy the code
- suspicious
We found that he executed RewritePathGatewayFilter, intercepting path is /user-server. The RewritePathGatewayFilter is used to rewrite urls.
Yml is not configured with RewritePath, so it will not rewrite the URL.
So who does this rewrite URL? (point)
See his rules for rewriting regular expressions to replace /user-server/** contents with /**.
Such as:
/user-server/**
Copy the code
Paths, replace them all with
/ * *Copy the code
- Found that regular
Just another mechanism of SpringCloudGateway, which generates a virtual domain name for the servicename of all nacos services as a load-balancing name that can be invoked to obtain one of the IP addresses of the service. This is also a name that restTemplate/ Feign needs to fill in when invoking the remote service.
However, when this mechanism is performed by the Gateway service, it proves that the SpringCloudGateway mechanism performs the above scheduled tasks as a mechanism for the Gateway service to invoke other microservices.
It turns on gateway’s ability to locate other services because of the following code.
Spring: Cloud: gateway: Discovery: locator: lowerCaseServiceId: true True # indicates that the Gateway enables service registration and discovery, and that the Spring Cloud Gateway automatically creates a router for each service based on service discoveryCopy the code
- Find the cause of the conflict
There is a conflict between the Path and the url of the service name in all routes I configured in application.yml.
The path specified in the routes of application.yml is /user-server/**, which is exactly the same as enabled in the above code to enable the gateway service discovery function. As a result, when RewritePathGatewayFilter is performed to rewrite the URL function, The match is /user-server/**, resulting in two functional conflicts
The solution
Method 1
Configure routes in application.yml. Do not match urls with service names. This way, can open spring. Cloud. Gateway. A locator. Enable = true functions. So you don’t have a conflict over the path.
Way 2
The locator function of gateway is disabled in application.yml. Gateway is the unified external network service. This should not be provided because, once enabled, every service of NACOS can be accessed, posing a risk to the Internet.
In addition, according to/service name /**, such a form of access cannot be blocked by the routes configuration of the interception rules, this article describes the cause of the problem.
It is recommended to use only the rule configured in routes, although in this case, the Path filled in the Path can be intercepted whether the service name is included or not. However, I still recommend that Path not include the service name, so that people outside the network can know the service name information. As a way to reduce information leakage.