在gateway启动时会加载一些路由断言工厂(判断一句话是否正确 一个boolean表达式)
具体可以在官网查看添加链接描述
注:不能作用在动态路由上
gateway 里面的过滤器和 Servlet 里面的过滤器,功能差不多,路由过滤器可以用于修改进入 Http 请求和返回 Http 响应
继承GlobalFilter可以重写他的filter方法,从exchange可以得到request、response,从而获得想要的数据,而Ordered是将过滤器排序的类,重写getOrder() 方法,返回参数越小,过滤器越先执行
/**
* 定义了一个过滤器
*/
@Component
public class MyGlobalFilter implements GlobalFilter , Ordered {
/**
* 这个就是过滤的方法
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//针对请求的过滤 拿到请求 header
//ServerHttpRequest webflux里面 响应式里面的
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
System.out.println(path);
String methodName = request.getMethod().name();
System.out.println(methodName);
String hostName = request.getRemoteAddress().getHostName();
System.out.println(hostName);
//响应相关的数据
ServerHttpResponse response = exchange.getResponse();
//放行,到下一个过滤器
return chain.filter(exchange);
}
/**
* 指定顺序的方法
* 越小越先执行
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
测试:
若不放行,代码:
//响应相关的数据
ServerHttpResponse response = exchange.getResponse();
//一般通过json传递数据
//{"code":"200","msh","ok"}
//设置编码 响应头里设置
response.getHeaders().set("content-type","application/json;charset=utf-8");
//组装业务返回值
HashMap<String, Object> map = new HashMap<>(4);
map.put("code", HttpStatus.UNAUTHORIZED.value());
map.put("msg","未授权");
ObjectMapper objectMapper = new ObjectMapper();
//把map转换成一个字节
byte[] bytes = new byte[0];
try {
bytes = objectMapper.writeValueAsBytes(map);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//通过buffer工厂将字节数组包装成一个数据包
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Mono.just(wrap));
测试:
校验ip一般有较大的并发量,一般不用mysql,这里采取将黑名单写入内存中
/**
* ip黑名单校验
*/
public class IPFilter implements GlobalFilter, Ordered {
public static final List<String> BLACK_LIST = Arrays.asList("192.1.1.1","193.1.1.1");
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String ip = request.getHeaders().getHost().getHostString();
if(! BLACK_LIST.contains(ip)){
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);
map.put("code", 488);
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));
}
@Override
public int getOrder() {
return -1;
}
}
测试:
token校验的本质就是让服务知晓访问的客户端
接着之前创建的login-service写代码
添加lombok依赖
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
创建entity
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class User {
private Integer id;
private String name;
private String pwd;
private Integer age;
}
引入redis依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-redisartifactId>
<version>2.3.9.RELEASEversion>
dependency>
接下来写controller,假设已经从数据库中查出数据
@RestController
public class LoginController {
@Autowired
public StringRedisTemplate redisTemplate;
@GetMapping("doLogin")
public String doLogin(String name,String pwd){
System.out.println(name);
System.out.println(pwd);
//假设这里做了登录
User user = new User(1,name,pwd,20);
//token
String token = UUID.randomUUID().toString();
//存起来
redisTemplate.opsForValue().set(token,user.toString(), Duration.ofSeconds(7200));
return token;
}
}
启动服务,接着在gateway-server中引入redis依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.datagroupId>
<artifactId>spring-data-redisartifactId>
<version>2.3.9.RELEASEversion>
dependency>
写一个过滤器,为TokenCheckFilter
/**
* token校验
*/
@Component
public class TokenCheckFilter implements GlobalFilter, Ordered {
public static final List<String> ALLOW_URL = Arrays.asList("/02-login-service/doLogin","/myUrl","/doLogin");
@Autowired
public StringRedisTemplate redisTemplate;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/**
* 前提是和前端约定好放在哪,一般放在请求头里面(key Authorization,value bearer token)
* 1.拿到请求url
* 2.判断放行
* 3.拿到请求头
* 4.拿到token
* 5.校验
* 6.放行/拦截
*/
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");
if(!CollectionUtils.isEmpty(authorization)){
String token = authorization.get(0);
if (StringUtils.hasText(token)){
//约定好有前缀的bearer token
String realtoken = token.replace("Bearer ", "");
if(StringUtils.hasText(realtoken)&&redisTemplate.hasKey(realtoken)){
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));
}
@Override
public int getOrder() {
return 0;
}
}
开启网关启动类
使用Postman进行测试