微服务的时代背景下,多个服务之间协同合作已经成为常态,一个高可用的项目应该将细粒度化业务,多节点部署。网关转发即多个服务的门户,暴露一个接口对外,安全性高,只需要在网关层做校验,其余的服务依赖网关转发,代码不冗余,可读性强。
pom依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId> spring-cloud-starter-netflix-eureka-client </artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.2</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.60</version> </dependency>
注:pom文件中不要引入web依赖,网关和网页严格区分开
yml文件:
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启注册中心路由功能
routes:
- id: payment_routh
uri: lb://cloud-payment-service
predicates: # 断言 先判断是否满足这个接口的规则 满足返回true继续访问 不满足 直接返回false
- Path=/**
- id: payment_routh2
uri: lb://cloud-order-service
predicates:
- Path=/**
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://127.0.0.1:7001/eureka
这里的注册中心 没有用nacos/consul 用的是自带的eureka,写了几个demo 都注册到注册中心中,方便网关转发。
主启动类:
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class, args);
}
}
其实就已经可以简单转发了。为了接口的安全性 我加上了token进行校验
TokenUtil工具类 用于生成和校验token
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TokenUtil {
//设置过期时间
private static final long EXPIRE_DATE=60*1000*60*12;
//token秘钥
private static final String TOKEN_SECRET = "ZCfasfhuaUUHufguGuwu2020BQWE";
public static String token (String username){
String token = "";
try {
//过期时间
Date date = new Date(System.currentTimeMillis()+EXPIRE_DATE);
//秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头部信息
Map<String,Object> header = new HashMap<>();
header.put("typ","JWT");
header.put("alg","HS256");
//携带username,生成签名
token = JWT.create()
.withHeader(header)
.withClaim("username",username)
.withExpiresAt(date)
.sign(algorithm);
}catch (Exception e){
e.printStackTrace();
return null;
}
return token;
}
/**
* @desc 验证token,通过返回true
* @params [token]需要校验的串
**/
public static boolean verify(String token){
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
}catch (Exception e){
e.printStackTrace();
return false;
}
}
}
访问所有接口前,增加校验
TokenHandle类
mport com.alibaba.fastjson.JSON;
import com.springcloud.entities.CommonResult;
import org.apache.commons.lang.StringUtils;
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.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
public class TokenHandle implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String path = request.getURI().getPath();
//如果是登陆接口 就直接放过校验
if(path.indexOf("/login")>=0){
return chain.filter(exchange);
}
String token = request.getHeaders().getFirst("token");
if(StringUtils.isEmpty(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return getMono(response,"token 缺失");
}
boolean verify = TokenUtil.verify(token);
if(!verify){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return getMono(response,"token 失效");
}
return chain.filter(exchange);
}
public Mono<Void> getMono(ServerHttpResponse response,String msg){
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
CommonResult<Object> objectCommonResult = new CommonResult<>();
objectCommonResult.setCode(-1);
objectCommonResult.setMessage(msg);
DataBuffer wrap = response.bufferFactory().wrap(JSON.toJSONString(objectCommonResult).getBytes());
return response.writeWith(Flux.just(wrap));
}
@Override
public int getOrder() {
return -100;
}
}
这样一个完整的网关就可以运行了,只要在登陆接口 生成token传给前端 让前端进行保存,后端不做维护,只校验规则即可。