【idea版】springcloud微服务(6)之整合gateway网关使用

您所在的位置:网站首页 网关设置什么意思 【idea版】springcloud微服务(6)之整合gateway网关使用

【idea版】springcloud微服务(6)之整合gateway网关使用

2024-07-12 10:10| 来源: 网络整理| 查看: 265

文章目录 前言【idea版】springcloud微服务(6)之整合gateway网关使用1.什么是网关2.API 网关的职能3.Gateway是什么4.环境要求5.创建项目6.引包1)父pom.xml依赖2)pom.xml的引入核心包spring-cloud-starter-gateway 7.新建配置文件8.创建入口GatewayApplication.java9.启动服务访问 10.全局拦截GlobalFilter

前言

【idea版】springcloud微服务系列搭建教程,包括整合nacos、mybatisplus、redis、gateway、ribbion、Rocketmq等

【idea版】springcloud微服务(6)之整合gateway网关使用 1.什么是网关

简单来说就是服务调用的入口,在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计、开发、测试、部署和管理。这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,这就要求必须使用一种语言和平 台无关的服务协议作为各个单元间的通讯方式。

2.API 网关的职能

请求接入,作为所有API接口服务请求的接入点

业务聚合,作为所有后端业务服务的集合点

中介策略,实现安全校验,过滤,路由,控流等

3.Gateway是什么

Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

4.环境要求

如果要实现服务之间的负载均衡的调用,需要一个注册中心,我这个使用是nacos

5.创建项目

根据教程:【idea版】springcloud微服务(1)之多模块版本新建一个springcloud项目 创建一个子模块:springcloud-gateway-example 注:我这个使用会使用到之前的创建的子模块,不明白的可以看看我之前的文章

6.引包 1)父pom.xml依赖 2.4.3 2020.0.2 2.2.1.RELEASE 11 11 11 org.springframework.boot spring-boot-dependencies ${spring-boot.version} pom import org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba.version} pom import 2)pom.xml的引入核心包spring-cloud-starter-gateway springcloud-example org.example 1.0-SNAPSHOT 4.0.0 springcloud-gateway-example org.springframework.cloud spring-cloud-starter-gateway org.projectlombok lombok com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-loadbalancer org.springframework.boot spring-boot-maven-plugin 2.3.5.RELEASE repackage maven-compiler-plugin 11 11 7.新建配置文件

在resources里新建文件application.yml

server: port: 9005 # 设置应用名称,便于在注册中心中查看 spring: application: name: gateway-server # 设置注册中心 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: routes: - id: example-server uri: https://example.org/ predicates: - Path=/test - id: ribbonA-server uri: http://localhost:9003/ predicates: - Path=/a/** filters: - StripPrefix=1 - id: ribbonB-server uri: lb://ribbonB-server predicates: - Path=/b/** filters: - StripPrefix=1

规则就是在predicates断言,如果判断断言为true,就会路由并打多出来的path拼接到uri的后面转发 主要配置说明

spring.cloud: gateway: routes: - id: example-server uri: https://example.org/ predicates: - Path=/test - id: ribbonA-server uri: http://localhost:9003/ predicates: - Path=/a/** filters: - StripPrefix=1 - id: ribbonB-server uri: lb://ribbonB-server predicates: - Path=/b/** filters: - StripPrefix=1 id:就是唯一值,可以随便写,确保唯一就行uri:就是你的请求需要转发后的地址predicates:断言,就是满足条件,满足就转发到urifilters:拦截,StripPrefix是截取,当http://127.0.0.1:9004/b/b,就会转发到服务ribbonB-server,因为有截取,实际转发的url是http://ribbonB-server/b

注:lb://ribbonB-server 在注册中心才能使用,lb是LoadBalanced的缩写,负载均衡的意思,后面是服务名ribbonB-server

8.创建入口GatewayApplication.java @SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } } 9.启动服务 访问

浏览器直接输入:http://127.0.0.1:9006/test 就会转发到:https://example.org/test

浏览器直接输入:http://127.0.0.1:9006/a/a 就会转发到: http://127.0.0.1:9003/a

10.全局拦截GlobalFilter

在GatewayApplication.java加入GlobalFilter

@SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } @Bean public GlobalFilter customFilter() { return new CustomGlobalFilter(); } }

拦截类CustomGlobalFilter.java,实现了会话拦截

import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import io.jsonwebtoken.Claims; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; 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.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.stream.Collectors; /** * 自定义全局拦截器 * * @author libingwei */ @Log4j2 public class CustomGlobalFilter implements GlobalFilter, Ordered { @Autowired private UrlWhiteListConfig urlWhiteListConfig; @Autowired private RedisUtil redisUtil; private static final String START_TIME = "startTime"; /** * 过滤器业务逻辑 * * @param exchange * @param chain * @return */ @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put(START_TIME, System.currentTimeMillis()); ServerHttpRequest request = exchange.getRequest(); //获取请求uri String uriPath = request.getURI().getPath(); log.info("请求,Method:{}, host:{}, path:{}, query: {}", request.getMethod().name(), request.getURI().getHost(), uriPath, request.getQueryParams() ); uriPath = uriPath.substring(uriPath.indexOf("/", 1)); //如果在白名单的URL,请求则放行 if (urlWhiteListConfig.getPathList().contains(uriPath)) { return chainFilter(exchange, chain); } //请求头中获取令牌 String token = request.getHeaders().getFirst("token"); //判断请求头中是否有令牌 if (StringUtils.isEmpty(token)) { return responseWrite(exchange, "没有权限,请登录"); } //如果请求头中有令牌则解析令牌 try { Claims claims = JwtUtil.parseJWT(token); LoginInfoEntity loginInfo = JSON.parseObject(claims.getSubject(), LoginInfoEntity.class); if (redisUtil.get(loginInfo.getName()) == null) { return responseWrite(exchange, "鉴权失效,请重新登录"); } String tokenStr = (String) redisUtil.get(loginInfo.getName()); if (!token.equals(tokenStr)) { return responseWrite(exchange, "鉴权失效,请重新登录"); } //刷新缓存时间 // redisUtil.expire(loginInfo.getName(), 30 * 60); } catch (Exception e) { log.error("jwt解析异常:", e); return responseWrite(exchange, "没有权限,请登录"); } //放行 return chainFilter(exchange, chain); } public Mono chainFilter(ServerWebExchange exchange, GatewayFilterChain chain) { return chain.filter(exchange).then(Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(START_TIME); if (startTime != null) { Long executeTime = System.currentTimeMillis() - startTime; log.info(exchange.getRequest().getURI().getRawPath() + " : " + executeTime + "ms"); } })); } /** * 错误返回信息 * * @param exchange * @param message * @return */ public Mono responseWrite(ServerWebExchange exchange, String message) { Long startTime = exchange.getAttribute(START_TIME); log.info("url:{}, message:{}, {}ms", exchange.getRequest().getURI().getPath(), message, (System.currentTimeMillis() - startTime)); ServerHttpResponse response = exchange.getResponse(); JSONObject jsonObject = new JSONObject(); jsonObject.put("code", "-1"); jsonObject.put("message", message); byte[] bits = jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8); DataBuffer buffer = response.bufferFactory().wrap(bits); response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8"); return response.writeWith(Mono.just(buffer)); } /** * 过滤器执行顺序,数值越小,优先级越高 * * @return */ @Override public int getOrder() { return 0; } }

工具类: UrlWhiteListConfig.java

@Data @Component @ConfigurationProperties(prefix = "urlwhit.filter") public class UrlWhiteListConfig { private List pathList; }

RedisUtil.java

RedisUtil.java 地址

JwtUtil.java

import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; import java.util.Date; /** * @author libingwei * @title: JwtUtil JWT工具类 */ public class JwtUtil { /** * 设置有效期为 60 * 60 秒 */ private static final Long JWT_TTL = 3600000L; /** * 设置秘钥明文 */ private static final String JWT_KEY = "jjwt-joyoh-token"; /** * 创建toke * * @param id * @param subject * @return */ public static String createJWT(String id, String subject) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // if (ttlMillis == null) { // ttlMillis = JwtUtil.JWT_TTL; // } // long expMillis = nowMillis + ttlMillis; // Date expDate = new Date(expMillis); SecretKey secretKey = generalKey(); JwtBuilder builder = Jwts.builder() //唯一的ID .setId(id) // 主题 可以是JSON数据 .setSubject(subject) // 签发者 .setIssuer("admin") // 签发时间 .setIssuedAt(now) //使用HS256对称加密算法签名, 第二个参数为秘钥 .signWith(signatureAlgorithm, secretKey); // 设置过期时间 // .setExpiration(expDate); return builder.compact(); } /** * 解析JWT * * @param compactJwt * @return * @throws Exception */ public static Claims parseJWT(String compactJwt) throws Exception { SecretKey secretKey = generalKey(); return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(compactJwt).getBody(); } /** * 生成加密后的秘钥 secretKey * * @return */ public static SecretKey generalKey() { byte[] encodedKey = Base64.getEncoder().encode(JwtUtil.JWT_KEY.getBytes()); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } // public static void main(String[] args) throws Exception { // Claims claims = JwtUtil.parseJWT("eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxOWI3NjU3OS1hYjYwLTRkNzYtYTE1ZS1lZWM2NmI4MzViMzEiLCJzdWIiOiJ7XCJlbWFpbEFkZHJlc3NcIjpcIlwiLFwibmFtZVwiOlwidGVzdFwifSIsImlzcyI6ImFkbWluIiwiaWF0IjoxNjA2MjkxNjgxfQ.XwEnKVnN-bJDQQ6hGniPfZD-W8LlpgnUl5WOGuSLba8"); // System.out.println(claims.getSubject()); // } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3