最开始学习时,没怎么用,只知道它是网关,当时因为经常使用Nginx做网关,慢慢就淡忘了,最近为了代码整合性,就使用它,非常棒。关于JWT以前也使用,后面调用基本以第三方接口开发的比较多,当时自己的服务,添加自己的规则生成token
整合gateway网关
1、pom
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>com.xl</groupId>
<artifactId>auth-service-api</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
其中auth-service-api和redis后面会用到,当前用不到
2、启动类注解
java
// 没有什么特别的
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
3、配置文件
yaml
spring:
application:
name: gateway-sample
redis:
host: localhost
port: 6379
database: 0
# 开始配置
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true # FEIGN-CLIENT可以小写访问
routes:
- id: yml
uri: lb://FEIGN-CLIENT
predicates:
- Path=/yml/**
filters:
- StripPrefix=1
# 结束配置
# 下面这里是限流配置
- name: RequestRateLimiter
args:
key-resolver: '#{@remoteAddrKeyResolver}'
redis-rate-limiter.replenishRate: 1 # 每秒填充令牌
redis-rate-limiter.burstCapacity: 1 # 总令牌,举个例子:假设现在令牌是满的,也就是总令牌20个,每秒填充为10,总共20,当前请求数为9,第一秒时,处理了9个,还剩11个,此时每秒会填充10个,此时有21个,但由于限制总令牌为20个,此时就是20个,第二秒时同理,如果每秒30个,第一秒会限流10个,报429,第二秒会限流20个,报429。
server:
port: 25200
management:
cloudfoundry:
enabled: false
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
eureka:
client:
serviceUrl:
defaultZone: http://localhost:15001/eureka,http://localhost:15002/eureka
这里就可以直接将feign-client替换成yml,http://localhost:25200/yml/sayHi
4、配置文件配置
java
package com.xl.gateway;
import com.xl.gateway.resop.ErrorFilter;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
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 org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageConverter;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.stream.Collectors;
@Configuration
public class GatewayConfiguration {
@Autowired
private GatewayFilterIn gatewayFilterIn;
@Autowired
private AuthFilter authFilter;
@Autowired
private ErrorFilter errorFilter;
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
}
@Bean
@Order
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> {
return r.path("/java/**")
.and().method(HttpMethod.GET)
.and().header("name")
.filters(f -> {
return f.stripPrefix(1)
.addResponseHeader("der", "wer")
.filter(errorFilter)
.filter(authFilter)
// 限流规则
.requestRateLimiter(config -> config
.setKeyResolver(this.remoteAddrKeyResolver())
.setRateLimiter(this.redisRateLimiter())
)
;
})
.uri("lb://FEIGN-CLIENT");
})
.route(r -> {
return r.path("/seckill/**")
.and().between(time("2025-01-15 17:30:00"), time("2025-01-15 17:35:00"))
.filters(f -> {
return f.stripPrefix(1);
})
.uri("lb://FEIGN-CLIENT");
})
.build();
}
// 时间处理
public static ZonedDateTime time(String dateTimeStr) {
// 定义日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 解析字符串为 LocalDateTime
LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, formatter);
// 转换为 ZonedDateTime
return ZonedDateTime.of(localDateTime, ZoneId.of("UTC"));
}
// 限流配置
@Bean
public KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getHostName());
}
// 限流规则
@Bean
public RedisRateLimiter redisRateLimiter() {
// 这里可以设置 RedisRateLimiter 的参数,如 replenishRate、burstCapacity 和 requestedTokens
return new RedisRateLimiter(1, 1, 1);
}
}
这里其实和配置文件大差不差
JWT使用
1、pom
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.1</version>
</dependency>
2、程序
java
package com.xl.auth;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Date;
@Slf4j
@Service
public class JwtService {
private static final String KEY = "xl";
private static final String ISSUE = "ligang";
private static final Long EXPIRE_TIME = 60000L;
private static final String USER_NAME = "username";
public String token(Account account) {
Algorithm algorithm = Algorithm.HMAC256(KEY);
Date date = new Date();
String sign = JWT.create()
.withIssuer(ISSUE)
.withIssuedAt(date)
.withExpiresAt(new Date(date.getTime() + EXPIRE_TIME))
.withClaim(USER_NAME, account.getUsername())
.sign(algorithm);
log.info("jwt generated user:{}", account.getUsername());
return sign;
}
public Boolean verify(String token, String username) {
log.info("verifying jwt - username:{}", username);
try {
Algorithm algorithm = Algorithm.HMAC256(KEY);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(ISSUE)
.withClaim(USER_NAME, username)
.build();
verifier.verify(token);
return true;
} catch (Exception e) {
log.error("auth failed", e);
return false;
}
}
}
后面自己对应的接口,比如登录获取token,验证token,续约token等
gateway添加规则使用JWT验证token
java
package com.xl.gateway;
import com.xl.auth.AuthResponse;
import com.xl.auth.AuthService;
import com.xl.auth.ErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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.StopWatch;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Slf4j
@Component("authFilter")
public class AuthFilter implements GatewayFilter, Ordered {
private static final String AUTH = "Aothorzation";
private static final String USERNAME = "user-name";
@Autowired
private AuthService authService;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("Auth start");
ServerHttpRequest request = exchange.getRequest();
HttpHeaders header = request.getHeaders();
String token = header.getFirst(AUTH);
String username = header.getFirst(USERNAME);
ServerHttpResponse response = exchange.getResponse();
if (StringUtils.isBlank(token)) {
log.error("token not found");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
AuthResponse resp = authService.verify(token, username);
if (resp.getCode() != 1L) {
log.error("invalid token");
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
ServerHttpRequest.Builder mutate = request.mutate();
mutate.header("user-name", username);
ServerHttpRequest build = mutate.build();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add("user-name", username);
return chain.filter(
exchange.mutate()
.request(build)
.response(response)
.build());
}
@Override
public int getOrder() {
return 0;
}
}
还有很多规则,看官方文档或者看代码源码研究,就摸索出来了,还是很有用的