Click for PDF | springboot | springcloud | programming ideas

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

Reading configuration
  • There is now a service demo-server deployed on the machine with the address and port as127.0.0.1:8081Therefore, the URI of the route ishttp://localhost:8081
  • Route to this service using the gateway service,predicates -Path=/demo-server/**, the gateway service port is 8080, start the gateway service, accesslocalhost:8080/demo-serverThe route assertion routes the request to the Demo-server
  • Interface for directly accessing the Demo-serverlocalhost:8081/api/test, and the access address through the gateway islocalhost:8080/demo-server/api/test,predicatesConfigure to assert requests to this route,filters-StripPrefix=1Represents intercepts the first address after /, so demo-server intercepts

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()Implementing filter services
  • Ordered getOrder()Define the filter execution order

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 the filter 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 various ways. For example, some Web download requests create a new page, and it is troublesome to transfer the token in the request header.)
  3. No token, return 401
  4. If there is a token, check whether redis is valid
  5. If it is invalid, return 401. If it is valid, verify release is completed
  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.

* thanks for reading, hope to help you 🙂 *

Source: juejin. Cn/post / 7004756545741258765


Recommend 3 original Springboot +Vue projects, with complete video explanation and documentation and source code:

Build a complete project from Springboot+ ElasticSearch + Canal

  • Video tutorial: www.bilibili.com/video/BV1Jq…
  • A complete development documents: www.zhuawaba.com/post/124
  • Online demos: www.zhuawaba.com/dailyhub

【VueAdmin】 hand to hand teach you to develop SpringBoot+Jwt+Vue back-end separation management system

  • Full 800 – minute video tutorial: www.bilibili.com/video/BV1af…
  • Complete development document front end: www.zhuawaba.com/post/18
  • Full development documentation backend: www.zhuawaba.com/post/19

【VueBlog】 Based on SpringBoot+Vue development of the front and back end separation blog project complete teaching

  • Full 200 – minute video tutorial: www.bilibili.com/video/BV1PQ…
  • Full development documentation: www.zhuawaba.com/post/17

If you have any questions, please come to my official account [Java Q&A Society] and ask me