Filter过滤器工厂
gateway里面的过滤器和 Servlet里面的过滤器,功能差不多,路由过滤器可以用于修改进入Http请求和返回 Http响应
按生命周期分两种
pre在业务逻辑之前 post在业务逻辑之后
GatewayFilter需要配置某个路由,才能过滤。如果需要使用全局路由,需要配置 DefaultFilters。
GlobalFilter全局过滤器,不需要配置路由,系统初始化作用到所有路由上
全局过滤器统计 :请求次数,限流 ,token的校验, ip黑名单拦截,跨域本质(filter) 144开头的电话限制一些 ip的访问
官方文档查看过滤器
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories
自定义网关过滤器(重点)
全局过滤器的优点的初始化时默认挂到所有路由上,我们可以使用它来完成 IP过滤,限流等功能
创建配置类 GlobalFilterConfig
@Component
@Slf4jpublic
class GlobalFilterConfig implements GlobalFilter, Ordered{
@Override //重写filter方法
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("进入了我自己的全局过滤器");
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (token == null) {
log.error("token为空,说明没有认证");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
log.info("验证通过");
return chain.filter(exchange);
}
/*** order越小越先执行**
@return*/
@Overridepublic
int getOrder() {
return 0;
}
}
访问测试
http://localhost/info?token=asdad
IP认证拦截实战
创建 IPGlobalFilter
@Component@Slf4jpublic
class IPCheckFilter implements GlobalFilter, Ordered {
@SneakyThrows
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String ip = exchange.getRequest().getHeaders().getHost().getHostName();
//这里写死了,只做演示
if (ip.equals("localhost")) {
//说明是黑名单里面的
ipServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
Map<String, Object> map = new HashMap<>();
map.put("code", HttpStatus.UNAUTHORIZED);
map.put("msg", "非法访问");
response.getHeaders().add("content-Type","application/json;charset=UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = objectMapper.writeValueAsBytes(map);
DataBuffer buffer = response.bufferFactory().wrap(bytes);return response.writeWith(Mono.just(buffer));
}
return chain.filter(exchange);
}
/***设置此过滤器的执行顺序** @return*/
@Override
public int getOrder() {return 1;}
}
测试访问
token校验实战
登录:
@RestController
@CrossOrigin // 加上这个注解之后 这个controller里面的方法就可以直接被访问了
public class LoginController {
@Autowired
public StringRedisTemplate redisTemplate;
@GetMapping("doLogin")
@CrossOrigin
public String doLogin(String name, String pwd) {
System.out.println(name);
System.out.println(pwd);
// 这里假设去做了登录
User user = new User(1, name, pwd, 18);
// token
String token = UUID.randomUUID().toString();
// 存起来 key是token value是登录后的user对象
redisTemplate.opsForValue().set(token, user.toString(), Duration.ofSeconds(7200)); //过期时间7200秒
return token;
}
}
拦截(过滤器):
/**
* token校验
*/
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {
/**
* 指定好允许放行的路径
*/ //登录的操作 --- 放行,如果是其他的操作(如teacher-service) 就要检查是否登录过(有7200秒过期时间)
public static final List<String> ALLOW_URL = Arrays.asList("/login-service/doLogin", "/myUrl","/doLogin");
//注入redis对象
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 前提是? 和前端约定好 一般放在请求头里面 一般key : Authorization (请求头里的字段) value: bearer token
* 1.拿到请求url
* 2.判断放行
* 3.拿到请求头
* 4.拿到token
* 5.校验
* 6.放行/拦截
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (ALLOW_URL.contains(path)) { //判断是不是 指定允许放行的路径 (白名单,走后门 不需要校验)
return chain.filter(exchange); //放行
}
// 不是允许放行的路径,检查
HttpHeaders headers = request.getHeaders();
List<String> authorization = headers.get("Authorization");//拿到key(登录时存入的UUID)
if (!CollectionUtils.isEmpty(authorization)) {
String token = authorization.get(0);
if (StringUtils.hasText(token)) { //hasText()返回值是 布尔类型的,字符串 不是 null ,并且不为空,
//而且不能是空白字符,只有这三个条件同时满足时才 返回 true ,其他情况均返回 false 。
// 约定好的有前缀的 bearer token
String realToken = token.replaceFirst("bearer ", "");
if (StringUtils.hasText(realToken) && redisTemplate.hasKey(realToken)) {
//(判断redis中是否存在登录时存入的uuid) 有就放行
return chain.filter(exchange);
}
}
}
// 否则拦截
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().set("content-type","application/json;charset=utf-8");
HashMap<String, Object> map = new HashMap<>(4);
// 返回401
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","未授权");
ObjectMapper objectMapper = new ObjectMapper();
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
}
/**
* 这个顺序 最好在0附近 -2 -1 0 1
* @return
*/
@Override
public int getOrder() {
return 1;
}
}