Microservice-gateway authentication

I. Introduction

The gateway is the middle layer between the client and the server, and all external requests will go through the gateway first. That is to say, more business logic is considered in the implementation of the API, while security, performance, and monitoring can be done by the gateway, which not only improves business flexibility but also does not lack security.

RBAC is based on role access control, which is currently the most widely used permission model. I believe that everyone has a better understanding of this permission model. This model has three users, roles, and permissions. In the traditional permission model, users are directly associated with roles, decoupling users and permissions, and making the permission system have a clearer division of responsibilities and higher flexibility.

Two, gateway authentication

insert image description here

1. Dependency configuration

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--json-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${fastjson.version}</version>
</dependency>

2. Write code

@Order(1)
@Component
public class AuthorizationFilter implements GlobalFilter {
    
    

    @Autowired
    private AuthConfig myConfig;

    @Autowired
    private AuthService authService;
    @Autowired
    private RedisUtil redisUtil;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    

        ServerHttpResponse response = exchange.getResponse();
        JSONObject message = new JSONObject();
        ServerHttpRequest request = exchange.getRequest();
        //1、获取请求路径
        String path = request.getPath().toString();

        //处理白名单内不需要进行拦截的地址
        if(!Objects.isNull(myConfig.getWhiteList())){
    
    
            for (String whitePath : myConfig.getWhiteList()) {
    
    
                if(path.contains(whitePath)){
    
    
                    return chain.filter(exchange);
                }
            }
        }

        //获取对应的token
        String token = request.getHeaders().getFirst("Authorization");
        //判断传递了token才可以继续解析
        if(!StringUtils.isEmpty(token)){
    
    
            try {
    
    
                //先去本地redis根据token获取value
                String userPhoneRedis = redisUtil.getTokenKey(token);
                //如果没有查到,或者过期时间小于10分钟,则去权限获取信息,权限会进行token续约
                if (Objects.isNull(userPhoneRedis) || redisUtil.getExpire(userPhoneRedis) < 600) {
    
    
                    String userId=authService.checkToken(token);
                    //如果校验token成功
                    if(null!=userId){
    
    
                        //缓存到redis中,一段时间内都不用校验token了
                        //token为key,用户id为value,存储7200秒(2小时)
                        redisUtil.set(token,userId,7200);
                        //放行继续向下调用
                        return chain.filter(exchange);
                    }
                }else{
    
    
                    return chain.filter(exchange);//放行继续向下调用
                }
            }catch (Exception e){
    
    
                //拦截请求,返回状态码401
                message.put("status", -1);
                message.put("data", "鉴权失败");
                byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
                DataBuffer buffer = response.bufferFactory().wrap(bits);
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                //指定编码,否则在浏览器中会中文乱码
                response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
                return response.writeWith(Mono.just(buffer));
            }
        }
        message.put("status", -1);
        message.put("data", "鉴权失败");
        byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }
}

3. Detailed explanation of GlobalFilter

3.1 Introduction to GlobalFilter

GlobalFilter is a component used to filter requests in Spring Cloud Gateway. It can add all GlobalFilter and specific GatewayFilter to the filter chain when the request matches the route, and then execute according to the org.springframework.core.Ordered interface order.

GlobalFilter has the same signature as GatewayFilter, so it can perform some operations before and after processing the request, such as checking request headers, modifying request parameters, verifying permissions, etc. When implementing the GlobalFilter interface, you need to implement the filter method, which accepts a WebExchange as a parameter.

In Spring Cloud Gateway, there are some default GlobalFilters, such as ForwardRoutingFilter and RouteToRequestUrlFilter, which are used to forward the request to the local interface of the current gateway instance and route the request to the specified URL respectively.

When a request matches a route, the filter web handler adds all instances of GlobalFilter and all route-specific instances of GatewayFilter to the filter chain. This combined filter chain is ordered by the org.springframework.core.Ordered interface, which you can set by implementing the getOrder() method. The smaller the value, the earlier the execution. Since Spring Cloud Gateway distinguishes between "pre" and "post" phases of filter logic execution, the filter with the highest priority is the first in the "pre" phase and the last in the "post" phase.

3.2, GlobalFilter custom execution order

When there are multiple GlobalFilters in the gateway module, you can specify the execution order of each GlobalFilter by implementing the Ordered interface or using the @Order annotation. The smaller the order value, the higher the priority and the higher the execution order.

If the order values ​​of multiple GlobalFilters are the same, they will be executed in the default order, which is: defaultFilter > routing filter > GlobalFilter.
insert image description here
insert image description here
For example, in my gateway module, there are two custom GlobalFilters, one for cross-domain processing and one for user authentication. We need to specify their execution order. The priority should be authentication. If you No permission to request services, so there is no need to consider whether the current request is cross-domain. The above picture shows the custom execution order of GlobalFilter realized by @Order;

3.2.1. Implement the Order interface to implement a custom execution order

import org.springframework.cloud.gateway.filter.GlobalFilter;  
import org.springframework.cloud.gateway.filter.GatewayFilterChain;  
import org.springframework.core.Ordered;  
import org.springframework.stereotype.Component;  
import org.springframework.web.server.ServerWebExchange;  
import reactor.core.publisher.Mono;  
  
@Component  
public class CustomGlobalFilter implements GlobalFilter, Ordered {
    
      
  
    @Override  
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
      
        // 在请求处理之前执行一些操作  
        // ...  
  
        // 继续执行下一个过滤器或路由处理程序  
        return chain.filter(exchange);  
    }  
  
    @Override  
    public int getOrder() {
    
      
        // 返回该过滤器的优先级值,值越小优先级越高  
        return 1;  
    }  
}

In this example, we create a class called CustomGlobalFilter that implements the GlobalFilter and Ordered interfaces. In the filter() method, we can perform some operations that need to be performed before the request is processed. The getOrder() method returns the priority value of the filter, here we return 1, which means that the filter will be executed first.

Guess you like

Origin blog.csdn.net/wmj20001225/article/details/132635222