发布时间:2023-12-28 13:00
路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器的作用域是特定的路由。SpringCloud Gateway包括许多内置的GatewayFilter工厂。目前官网提供33种路由过滤器工厂,前面示例中filters里的StripPrefix就是其中一种:
在库存微服务控制器中增加打印
@RequestMapping("/deduct")
public String storage(HttpServletRequest request){
log.info("请求头X-Request-red:{}",request.getHeader("X-Request-red"));
return "扣减库存";
}
Nacos中网关的配置增加AddRequestHeader=X-Request-red, blue
server:
port: 4090
spring:
cloud:
gateway:
routes:
- id: storage_route
uri: lb://ecom-storage-service
predicates:
- Path=/storage-service/**
- Quantity=100,200
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-red, blue
访问http://localhost:4090/storage-service/deduct?quantity=100 ,从库存微服务的日志可以看到网关的过滤器已经添加请求头信息
其他很多种过滤器实现各位有时间可以一一尝试
建立一个授权的自定义工厂过滤器工厂,从redis读取授权信息验证,先创建RedisConfig配置类
package cn.itxs.ecom.gateway.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@Slf4j
public class RedisConfig{
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance , ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
//template.setValueSerializer(jackson2JsonRedisSerializer);
template.setValueSerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
//template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(stringRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
redis信息放在Nacos配置commons-dev.yaml里
创建过滤器工厂AuthorizeGatewayFilterFactory.java继承自AbstractGatewayFilterFactory
package cn.itxs.ecom.gateway.factory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.RedisTemplate;
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.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
@Component
@Slf4j
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {
private static final String AUTHORIZE_TOKEN = "token";
private static final String AUTHORIZE_UID = "uid";
@Autowired
private RedisTemplate redisTemplate;
public AuthorizeGatewayFilterFactory() {
super(Config.class);
log.info("Loaded GatewayFilterFactory [Authorize]");
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("enabled");
}
@Override
public GatewayFilter apply(AuthorizeGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
if (!config.isEnabled()) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String token = headers.getFirst(AUTHORIZE_TOKEN);
String uid = headers.getFirst(AUTHORIZE_UID);
if (token == null) {
token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
}
if (uid == null) {
uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
}
ServerHttpResponse response = exchange.getResponse();
if (!StringUtils.hasText(token) || !StringUtils.hasText(uid)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
String authToken = (String) redisTemplate.opsForValue().get(uid);
if (authToken == null || !authToken.equals(token)) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
};
}
public static class Config {
// 控制是否开启认证
private boolean enabled;
public Config() {}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
如果授权不通过返回HttpStatus.UNAUTHORIZED也即是 http 401
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7fDzQbqT-1657432430026)(http://www.itxiaoshen.com:3001/assets/1657431654370DW5dZAyW.png)]
网关Nacos配置文件增加Authorize=true
server:
port: 4090
spring:
cloud:
gateway:
routes:
- id: storage_route
uri: lb://ecom-storage-service
predicates:
- Path=/storage-service/**
- Quantity=100,200
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-red, blue
- Authorize=true
由于目前redis里面没有uid和token的信息,访问http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc 返回401状态
我们往redis写入uid为1001的数据
再次访问http://localhost:4090/storage-service/deduct?quantity=100&uid=1001&token=abc ,则可以正常获取结果
spring.cloud.gateway.default-filters
实现所配置的过滤器全局生效,但这种方法在实际中比较少用。