springcloudgateway拦截器例子

本文通过示例介绍了如何在SpringCloudGateway中实现全局Token校验的GlobalFilter以及特定应用的拦截器逻辑。创建了两个微服务one和two,全局拦截器TokenFilter对所有请求生效,而UrlFilter仅对one服务起作用,过滤器的执行顺序由order属性决定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springcloudgateway写2个filter,一个GlobalFilter实现公共token的校验,一个实现具体应用的拦截器逻辑。

package com.example.gate.filter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.synchronoss.cloud.nio.multipart.util.IOUtils;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class TokenFilter implements GlobalFilter, Ordered {

	Logger logger = LoggerFactory.getLogger(TokenFilter.class);

	@Override
	public int getOrder() {
		// TODO Auto-generated method stub
		return 120;
	}
	
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		logger.info("TokenFilter开始............");
		
		getAllParamtersRequest(exchange.getRequest());
		getAllHeadersRequest(exchange.getRequest());
		
		
		//拦截的逻辑。根据具体业务逻辑做拦截。
		String token = exchange.getRequest().getQueryParams().getFirst("token");
		if (token == null || token.isEmpty()) {
			logger.info("token is empty...");
//			exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//			return exchange.getResponse().setComplete();
			
			//设置status和body
	        return Mono.defer(() -> {
	            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置status
	            final ServerHttpResponse response = exchange.getResponse();
	            byte[] bytes = "{\"code\":\"99999\",\"message\":\"非法访问,没有检测到token~~~~~~\"}".getBytes(StandardCharsets.UTF_8);
	            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
	            response.getHeaders().set("aaa", "bbb");//设置header
	            logger.info("TokenFilter拦截非法请求,没有检测到token............");
	            return response.writeWith(Flux.just(buffer));//设置body
	        });
		}
		
		//没有被if条件拦截,就放行
		return chain.filter(exchange);
	}
	
	
	private Map getAllParamtersRequest(ServerHttpRequest request) {
		logger.info("getAllParamtersRequest开始............");
		Map map = new HashMap();
		MultiValueMap<String, String> paramNames = request.getQueryParams();
		Iterator it= paramNames.keySet().iterator();
		while (it.hasNext()) {
			String paramName = (String) it.next();
			
			List<String> paramValues = paramNames.get(paramName);
			if (paramValues.size() >= 1) {
				String paramValue = paramValues.get(0);
				logger.info("request参数:"+paramName+",值:"+paramValue);
				map.put(paramName, paramValue);
			}
		}
		return map;
	}
	
	private Map getAllHeadersRequest(ServerHttpRequest request) {
		logger.info("getAllHeadersRequest开始............");
		Map map = new HashMap();
		HttpHeaders hearders = request.getHeaders();
		Iterator it= hearders.keySet().iterator();
		while (it.hasNext()) {
			String keyName = (String) it.next();
			
			List<String> headValues = hearders.get(keyName);
			if (headValues.size() >= 1) {
				String kvalue = headValues.get(0);
				logger.info("request header的key:"+keyName+",值:"+kvalue);
				map.put(keyName, kvalue);
			}
		}
		return map;
	}
	
//	/**
//	 * 
//	 * @param request
//	 * @return
//	 */
//	private Map getAllParamtersRequest(HttpServletRequest request) {
//		logger.info("getAllParamtersRequest............");
//		Map map = new HashMap();
//		Enumeration paramNames = request.getParameterNames();
//		while (paramNames.hasMoreElements()) {
//			String paramName = (String) paramNames.nextElement();
//			String[] paramValues = request.getParameterValues(paramName);
//			if (paramValues.length >= 1) {
//				String paramValue = paramValues[0];
//				if (paramValue.length() != 0) {
//					logger.info("request参数:"+paramName+",值:"+paramValue);
//					map.put(paramName, paramValue);
//				}
//
//			}
//		}
//		return map;
//	}

}
package com.example.gate.filter;

import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * 
 * Ordered 负责filter的顺序,数字越小越优先,越靠前。
 * 
 * GatewayFilter:
 * 需要通过spring.cloud.routes.filters 配置在具体路由下,
 * 只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。
 * 需要用代码的形式,配置一个RouteLocator,里面写路由的配置信息。
 * 
 * GlobalFilter:
 * 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,
 * 它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
 * 代码配置需要声明一个GlobalFilter对象。
 * 
 * 
 * 对一个应用来说,GatewayFilter和GlobalFilter是等价的,order也会按照顺序进行拦截。所以两个order不要写一样!
 * 
 * @author lsy
 *
 */
public class UrlFilter implements GatewayFilter, Ordered {

	Logger logger = LoggerFactory.getLogger(UrlFilter.class);
	
	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		logger.info("UrlFilter开始............");
		URI uri = exchange.getRequest().getURI();
		logger.info("UrlFilter开始............URI==="+uri.toString());
		
		HttpMethod method = exchange.getRequest().getMethod();
		logger.info("UrlFilter开始............HttpMethod==="+method.toString());
		
		String methodValue= exchange.getRequest().getMethodValue();
		logger.info("UrlFilter开始............getMethodValue==="+methodValue);
		
		
		RequestPath requestPath =exchange.getRequest().getPath();
		logger.info("UrlFilter开始............RequestPath==="+requestPath.toString());
		
		InetSocketAddress inetSocketAddress =exchange.getRequest().getRemoteAddress();
		logger.info("UrlFilter开始............InetSocketAddress==="+inetSocketAddress.toString());
		
		
		//拦截的逻辑。根据具体业务逻辑做拦截。
		String perm = exchange.getRequest().getQueryParams().getFirst("perm");
		if (perm == null || perm.isEmpty()) {
			logger.info("perm is empty...");
			//设置HttpStatus,返回体是空的
//					exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//					return exchange.getResponse().setComplete();
			
			//下面可以设置返回体的具体内容
			//设置status和body
	        return Mono.defer(() -> {
	            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);//设置status
	            final ServerHttpResponse response = exchange.getResponse();
	            byte[] bytes = "{\"code\":\"99000\",\"message\":\"非法访问,没有检测到权限码perm~~~~~~\"}".getBytes(StandardCharsets.UTF_8);
	            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
	            response.getHeaders().set("permValid", "false");//设置header
	            logger.info("UrlFilter拦截非法请求,没有检测到权限码perm............");
	            return response.writeWith(Flux.just(buffer));//设置body
	        });
		}
		
		//没有被if条件拦截,就放行
		return chain.filter(exchange);
	}

}
package com.example.gate.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.gate.filter.TokenFilter;
import com.example.gate.filter.UrlFilter;

@Configuration
public class FilterConfig {

	Logger logger = LoggerFactory.getLogger(FilterConfig.class);
	
	@Bean
	public GlobalFilter tokenFilter() {
	    return new TokenFilter();
	}
	
	
	
	
	@Bean
    public RouteLocator urlFilterRouteLocator(RouteLocatorBuilder builder) {
		logger.info("FilterConfig---urlFilterRouteLocator---");
        return builder.routes()
                .route(r -> r.path("/one/**")
                        .filters(f -> f.stripPrefix (1).filter(new UrlFilter())
                                .addResponseHeader("urlFilterFlag", "pass"))
                        .uri("lb://one")
                        .order(0)
                        .id("one")
                )
                .build();
    }
	
	
	
}
server: 
   port: 8888
   
spring:
  application:
    name: gate
  cloud:
    consul:
      host: 127.0.0.1  #注册中心的ip或host。也是集群地址,配置一个即可。
      port: 8500
      discovery:
        #enabled: true #使用服务发现客户端定义路由信息。即不配置routes节点,采用默认配置;配置了,以配置为准。默认true。
        register: true #是否将服务注册到Consul集群中心
        deregister: true #默认true,服务停止时注销服务,即从服务列表中删除。设置成false的话,???
        #service-name: ${spring.application.name}  #注册在consul上面的名字,在consul的调用中,是通过此名字调用的。默认服务名,不要改
        instance-id: ${spring.application.name}-${spring.cloud.client.ip-address}:${server.port} #只供显示用,在ID列显示
        health-check-interval: 5s  #配置 Consul 健康检查频率,也就是心跳频率。
        health-check-critical-timeout: 1s #健康检查失败多长时间后,取消注册。在node上显示红色叉。
        #health-check-path: /tmp #健康检查路径
        #prefer-ip-address: true #表示注册时使用IP而不是hostname
    gateway:
      discovery:
        locator:
          enabled: true #表明gateway开启服务注册和发现的功能,并且自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
          lower-case-service-id: true # 服务名小写
#      routes:
#      - id: one
#        # lb代表从注册中心获取服务,且已负载均衡方式转发
#        uri: lb://one #目标服务地址
#        predicates: #路由条件
#          - Path=/one/**
#        #过滤规则。 StripPrefix=1标识去掉url的第一个前缀,用后面的内容拼接到uri后面
#        filters:
#          - StripPrefix=1
#      - id: two
#        # lb代表从注册中心获取服务,且已负载均衡方式转发
#        uri: lb://two #目标服务地址
#        predicates: #路由条件
#          - Path=/two/**
#        #过滤规则。 StripPrefix=1标识去掉url的第一个前缀,用后面的内容拼接到uri后面
#        filters:
#          - StripPrefix=1
        

#eureka:
#  instance:
#    prefer-ip-address: true
#    instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
#    lease-expiration-duration-in-seconds: 10
#    lease-renewal-interval-in-seconds: 3
#  client:
#    registry-fetch-interval-seconds: 5
#    serviceUrl:
#      defaultZone: http://localhost:8761/eureka/

#启用监控.开启监控后,可直接通过actuator的rest接口查看服务的详细信息。例如查看网关的路由:http://localhost:8888/actuator/gateway/routes
management:
  endpoints:
    web:
      exposure:
        include: 
        - "*"  # 开放所有端点health,info,metrics,通过actuator/+端点名就可以获取相应的信息。默认打开health和info
  endpoint:
    health:
      show-details: always  #未开启actuator/health时,我们获取到的信息是{"status":"UP"},status的值还有可能是 DOWN。开启后打印详细信息



  

我起2个微服务,一个叫one,一个叫two。都有一个测试的controller:

package com.example.one.controller;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

//@Component
@RestController
//@RequestMapping("/beat")
public class HelloController {
	
	@Value("${spring.cloud.client.ip-address}")
	private String ip;
	
	@Value("${spring.application.name}")
	private String servername;
	
	@Value("${server.port}")
	private String port;
	
//	@Autowired
//	private ComConfig conf;

	@GetMapping(value="/hello")
	public String hello() {
		String[] colorArr=new String[] {"red","blue","green","pink","gray"};
		DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//        LocalDateTime startTime = LocalDateTime.now();
		String message="hello !  I am   <span style=\"color:"+colorArr[0]+"\">["+servername+":"+ip+":"+port+"]</span>"+"..."+LocalDateTime.now().format(df);
		System.out.println(message);
		return message;
	}
}

根据以上代码,我定义了一个全局的过滤器TokenFilter,给one服务添加了UrlFilter(two没有)

经测试,全局拦截器对2个应用都起作用。UrlFilter只对one起作用。

值得注意的是,对于one,他有2个Filter。顺序是按照order来得。对于他来说已经不分GatewayFilter和GlobalFilter了。

 

 

 

 

 

 

 

 

 

### 实现 Spring Cloud Gateway 请求参数和响应参数的全局加解密 为了实现在 Spring Cloud Gateway 中对所有接口的请求参数和响应参数进行加密和解密,可以通过创建 `GlobalFilter` 来处理这些操作。下面展示了具体的实现方法。 #### 创建 AES 加密工具类 首先,需要引入 Hutool 工具库以便于使用其中提供的 AES 加密功能: ```xml <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.10</version> </dependency> ``` 接着编写一个简单的 AES 工具类来进行字符串级别的加密与解密工作: ```java import cn.hutool.crypto.symmetric.AES; public class AesUtil { private static final String KEY = "your-secret-key"; // 密钥长度需满足AES标准 /** * 使用AES算法加密给定的内容. */ public static String encrypt(String content) { try { byte[] keyBytes = KEY.getBytes("UTF-8"); AES aes = new AES(keyBytes); return aes.encryptBase64(content); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } /** * 解密由aes加密过的内容. */ public static String decrypt(String encryptedContent) { try { byte[] keyBytes = KEY.getBytes("UTF-8"); AES aes = new AES(keyBytes); return aes.decryptStr(encryptedContent, "UTF-8"); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } } ``` #### 编写全局过滤器 接下来,在项目里定义一个新的 Java 类作为全局过滤器来执行实际的数据转换任务: ```java import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class EncryptDecryptFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 处理入站请求前先尝试解析并解码body部分 // 这里的逻辑应该根据实际情况调整, // 比如判断是否为POST/PUT等携带Body的方法, // 并且只针对某些路径或服务应用此规则。 // 同样地,在返回response之后也需要对其进行编码处理。 // 下面是一个简化版的例子: // 假设我们已经得到了原始request body string形式的数据叫做originalRequestBodyString String originalRequestBodyString = ""; // 获取原生请求体的方式取决于框架版本等因素 if (!"".equals(originalRequestBodyString)) { // 如果存在非空的请求体,则对其实施解密 String decryptedRequestData = AesUtil.decrypt(originalRequestBodyString); // 更新exchange对象中的请求信息... // 继续传递修改后的exchange实例至下一个filter链节点 return chain.filter(exchange.mutate().request(/*更新过的*/).build()); } else { // 若无任何待解密内容则直接放行 return chain.filter(exchange); } } @Override public int getOrder() { return 0; // 设置优先级数值越低代表越早被执行 } // Response encryption logic should be implemented similarly here or within another dedicated global filter. } ``` 请注意上述代码片段仅为示意用途;真实场景下可能还需要考虑更多细节问题,比如如何安全有效地读取HTTP消息主体、怎样适当地设置响应头指示客户端采用何种方式进行后续交互等等[^2]。 对于响应数据加密而言,可以在同一个全局过滤器内完成,也可以单独再建立另一个专门负责响应阶段工作的过滤器组件。无论哪种方式都遵循类似的模式——截获即将发出的 HTTP 响应流,从中提取出要保护的信息加以变换后再送出去。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值