preface

This article records how I use Gateway to set up Gateway service and achieve dynamic routing, to help you learn how to quickly set up a Gateway service, understand the routing configuration, authentication process and business processing, interested must see the end, very suitable for students who have not touched Gateway service as a beginner’s course.

Set up service

The framework

  • SpringBoot 2.1
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> < version > 2.1.0. RELEASE < / version > < / parent >Copy the code
  • Spring-cloud-gateway-core
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-gateway-core</artifactId>
</dependency>
Copy the code
  • common-lang3
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
</dependency>
Copy the code

The routing configuration

The gateway serves as the unified entrance for requests, and the route is equivalent to the entrance of each service system. Based on the routing rules, the request can be matched to the entrance of the corresponding micro-service, and the request can be matched to the corresponding service system

server:
  port: 8080

spring:
  cloud:
    gateway:
      enabled: true
      routes:
      - id: demo-server
        uri: http://localhost:8081
        predicates:
        - Path=/demo-server/**
        filters:
          - StripPrefix= 1
Copy the code

routes

Configuration items describe
id The unique route ID is the service name
uri Access address of the routing service
predicates Routing assertion
filters Filtering rules

Reading configuration

  • There is a service demo-server deployed on the local computer with the address and port of 127.0.0.1:8081. Therefore, the route configuration URI is http://localhost:8081
  • Route to this service using the gateway service, predicates -Path=/demo-server/**, gateway service port 8080, start the gateway service, access localhost:8080/ Demo-server, route assertion will route the request to the Demo-server
  • Direct access to the demo interface – server localhost: 8081 / API/test, through the access gateway address is localhost: 8080 / demo – server/API/test, predicates configuration will request assertions to walks by, Filters-stripprefix =1 intercepts the first address after /, so demo-server intercepts it

Gateway can be used to configure routes through the configuration file, which is very convenient. As long as we fully understand the meaning and rules of configuration items, it is ok. However, if these configurations are to be modified, the service needs to be restarted. Restarting the gateway service will make the whole system unavailable, which is unacceptable

Dynamic routing

To realize dynamic routing using NACOS and gateway-Server, we need to first deploy a NACOS service, which can be deployed using Docker or downloaded source code to start locally. For specific operations, please refer to the official documents

Nacos configuration

GroupId: use the gateway service name

dataId: routes

Configuration format: JSON

- server [{" id ":" XXX ", "order" : 1, # priority "predicates" : [{# routing assertion "args" : {" pattern ":"/XXX - server / * * "}, "name" : "Path"}], "filters":[{# filters" args": {"parts": 0 #k8s: http://xxx-server/xxx-server}, "name": [{# filters" args": {"parts": 0 #k8s: http://xxx-server/xxx-server}, "name": The beginning of the "StripPrefix" # interception index}], "uri" : "http://localhost:8080/xxx-server" target address}] #Copy the code

The configuration items in JSON format correspond to those in YAML. Therefore, you need to know how to write the configuration in JSON

Compare the JSON configuration to the YAML configuration

{
    "id":"demo-server",
    "predicates":[
        {
            "args":{
                "pattern":"/demo-server/**"
            },
            "name":"Path"
        }
    ],
    "filters":[
        {
            "args":{
                "parts":1
            },
            "name":"StripPrefix"
        }
    ],
    "uri":"http://localhost:8081"
}
Copy the code
spring:
  cloud:
    gateway:
      enabled: true
      routes:
      - id: demo-server
        uri: http://localhost:8081
        predicates:
        - Path=/demo-server/**
        filters:
          - StripPrefix= 1
Copy the code

Code implementation

The core of Nacos to realize dynamic routing is to use Nacos configuration monitoring, and then execute gateway related API to create routes after configuration changes

@Component public class NacosDynamicRouteService implements ApplicationEventPublisherAware { private static final Logger  LOGGER = LoggerFactory.getLogger(NacosDynamicRouteService.class); @Autowired private RouteDefinitionWriter routeDefinitionWriter; private ApplicationEventPublisher applicationEventPublisher; /** Routing id */ private static List<String> routeIds = Lists. NewArrayList (); /** * listen to the nacOS routing configuration, @param configInfo / @nacosConfigListener (dataId = "routes", groupId = "gateway-server") public void routeConfigListener(String configInfo) { clearRoute(); try { List<RouteDefinition> gatewayRouteDefinitions = JSON.parseArray(configInfo, RouteDefinition.class); for (RouteDefinition routeDefinition : gatewayRouteDefinitions) { addRoute(routeDefinition); } publish(); LOGGER.info("Dynamic Routing Publish Success"); } catch (Exception e) { LOGGER.error(e.getMessage(), e); Private void clearRoute() {for (String id: routeIds) { routeDefinitionWriter.delete(Mono.just(id)).subscribe(); } routeIds.clear(); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } @param definition */ private void addRoute(RouteDefinition definition) {try { routeDefinitionWriter.save(Mono.just(definition)).subscribe(); routeIds.add(definition.getId()); } catch (Exception e) { LOGGER.error(e.getMessage(), e); }} / distribution routing, make effective routing * * * * / private void publish () {this. ApplicationEventPublisher. PublishEvent (new RefreshRoutesEvent(this.routeDefinitionWriter)); }}Copy the code

The filter

Gateway provides two interfaces, GlobalFilter and Ordered, to define filters. We only need to implement these two interfaces to customize filters

  • GlobalFilter filter() implements the filter service
  • Ordered getOrder() defines the order in which filters are executed

Generally, the filtering of a gateway service mainly includes authentication (login or not, blacklist or not, login exempt interface…). Traffic limiting (IP traffic limiting, etc.) function, today we briefly introduce the process of authentication filter implementation

Authentication filter

To implement authentication filters, we first need to understand the login and authentication process, as shown in the following figure

As can be seen from the figure, the core of authentication and filtering is to verify whether token is valid. Therefore, the gateway service needs to be in the same REDIS library as the business system, and the Redis dependency and configuration should be added to the gateway first

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
Copy the code
spring:
  redis:
    host: redis-server
    port: 6379
    password:
    database: 0
Copy the code

Code implementation

  • 1. Define an AuthFilter
  • 2. Obtain the request object obtains the token from the request header or parameter or cookie. (It is more friendly for the client to transfer the token in a variety of ways.
  • 3. If there is no token, return 401
  • 4. If there is a token, check whether redis is valid
  • 5. If it is invalid, return to 401. If it is valid, complete verification release
  • 6. Reset the token expiration time and add internal request headers to facilitate service system permission processing
@Component public class AuthFilter implements GlobalFilter, Ordered { @Autowired private RedisTemplate<String, String> redisTemplate; private static final String TOKEN_HEADER_KEY = "auth_token"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. ServerHttpRequest Request = exchange.getrequest (); String token = getToken(request); // 2. ServerHttpResponse response = exchange.getResponse(); If (stringutils.isblank (token)) {// if (stringutils.isblank (token)) {// if the token is null return 401 Response.setStatusCode (httpstatus.unauthorized); return response.setComplete(); } // 4. Check whether the token is valid String userId = getUserIdByToken(token); If (stringutils.isblank (userId)) {// if (stringutils.isblank (userId)) {// if (stringutils.isblank (userId)) {return 401 Response.setStatusCode (httpstatus.unauthorized); return response.setComplete(); } // The token is valid, the subsequent business processing // from write request header, Convenient business system from the request header to obtain a user id to access relevant processing ServerHttpRequest. Builder Builder = exchange. GetRequest () mutate (); request = builder.header("user_id", userId).build(); // Extend the cache expiration time - Token cache users will reset the expiration time if they keep operating. // In this way, service operations and experience will not be affected by sudden expiration. ResetTokenExpirationTime (Token, userId) Expires only when the interval between user operations is greater than the expiration time of the cache. Return chain.filter(exchange); } @override public int getOrder() {return 0; } /** * get user ID from redis * The redis key is auth_token: the token value is user ID * * @param token * @return */ private String getUserIdByToken(String token) {String redisKey = String.join(":", "auth_token", token); return redisTemplate.opsForValue().get(redisKey); } /** * resetTokenExpirationTime ** @param token * @param userId */ private void resetTokenExpirationTime(String token, String userId) { String redisKey = String.join(":", "auth_token", token); redisTemplate.opsForValue().set(redisKey, userId, 2, TimeUnit.HOURS); } private static String getToken(ServerHttpRequest Request) {HttpHeaders ** @param Request * @return */ private static String getToken(ServerHttpRequest Request) headers = request.getHeaders(); String token = headers. GetFirst (TOKEN_HEADER_KEY); If (stringutils.isblank (token)) {// Obtain token from url if the request header has no token. Token = request.getQueryParams().getfirst (TOKEN_HEADER_KEY); } if (stringutils.isblank (token)) {// Get HttpCookie from cookies cookie = request.getCookies().getFirst(TOKEN_HEADER_KEY); if (cookie ! = null) { token = cookie.getValue(); } } return token; }}Copy the code

conclusion

Gateway can realize the routing function through configuration items, integrate Nacos and configure listener can realize dynamic routing, realize GlobalFilter, Ordered two interfaces can quickly realize a filter, the article also describes in detail the request authentication process after login, if there is any unclear place can see the comment section.

By: Hot butterbeer Link: juejin.cn/post/700475… Source: Rare earth mining copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.