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

  1. To understand the GatewayFilter implementation

  2. To understand the GatewayFilterFactory implementation

  3. Nacos registration mechanism

  4. 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
  1. 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
  1. 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

  1. 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.

  1. 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
  1. 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
  1. 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
  1. 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.