XDM can pay attention to this issue when it is free. I hope to help you and protect your own rights and interests.
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 ": "/ iot - base - data - server / * *"}, "name" : "Path"}], "filters" : [{# filtering rules "args" : {" parts ": }, "name": "StripPrefix" # truncated 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.