最开始学习时,没怎么用,只知道它是网关,当时因为经常使用Nginx做网关,慢慢就淡忘了,最近为了代码整合性,就使用它,非常棒。关于JWT以前也使用,后面调用基本以第三方接口开发的比较多,当时自己的服务,添加自己的规则生成token
gateway_1">整合gateway网关
1、pom
<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、启动类注解
// 没有什么特别的
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
启动程序后,直接访问http://localhost:25200/actuator,寻找gateway,在访问/actuator/gateway,c此时会报错404,请求错误,我们要看路由,加上/routes,查看json,大概就知道怎么访问了,http://localhost:25200/feign-client/sayHi,如果是多个feign-client服务,自动就负载均衡了,是不是比nginx方便多了。
3、配置文件
spring:application:name: gateway-sampleredis:host: localhostport: 6379database: 0# 开始配置cloud:gateway:discovery:locator:enabled: truelower-case-service-id: true # FEIGN-CLIENT可以小写访问routes:- id: ymluri: lb://FEIGN-CLIENTpredicates:- Path=/yml/**filters:- StripPrefix=1# 结束配置# 下面这里是限流配置- name: RequestRateLimiterargs: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: falseendpoints:web:exposure:include: "*"endpoint:health:show-details: alwayseureka:client:serviceUrl:defaultZone: http://localhost:15001/eureka,http://localhost:15002/eureka
这里就可以直接将feign-client替换成yml,http://localhost:25200/yml/sayHi
4、配置文件配置
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 {@Autowiredprivate GatewayFilterIn gatewayFilterIn;@Autowiredprivate AuthFilter authFilter;@Autowiredprivate ErrorFilter errorFilter;@Bean@ConditionalOnMissingBeanpublic HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));}@Bean@Orderpublic 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");// 解析字符串为 LocalDateTimeLocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, formatter);// 转换为 ZonedDateTimereturn ZonedDateTime.of(localDateTime, ZoneId.of("UTC"));}// 限流配置@Beanpublic KeyResolver remoteAddrKeyResolver() {return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());}// 限流规则@Beanpublic RedisRateLimiter redisRateLimiter() {// 这里可以设置 RedisRateLimiter 的参数,如 replenishRate、burstCapacity 和 requestedTokensreturn new RedisRateLimiter(1, 1, 1);}
}
这里其实和配置文件大差不差
JWT使用
1、pom
<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、程序
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等
gatewayJWTtoken_270">gateway添加规则使用JWT验证token
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";@Autowiredprivate AuthService authService;@Overridepublic 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());}@Overridepublic int getOrder() {return 0;}
}
还有很多规则,看官方文档或者看代码源码研究,就摸索出来了,还是很有用的