欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring Boot 3 整合 Spring Cloud Gateway實踐過程

 更新時間:2025年02月26日 16:29:16   作者:別惹CC  
本文介紹了如何使用SpringCloudAlibaba2023.0.0.0版本構(gòu)建一個微服務(wù)網(wǎng)關(guān),包括統(tǒng)一路由、限流防刷和登錄鑒權(quán)等功能,并通過一個項目實例進行詳細說明,感興趣的朋友一起看看吧

引子

當(dāng)前微服務(wù)架構(gòu)已成為中大型系統(tǒng)的標(biāo)配,但在享受拆分帶來的敏捷性時,流量治理與安全管控的復(fù)雜度也呈指數(shù)級上升。因此,我們需要構(gòu)建微服務(wù)網(wǎng)關(guān)來為系統(tǒng)“保駕護航”。本文將會通過一個項目(核心模塊包含 鑒權(quán)服務(wù)、文件服務(wù)、主服務(wù) 共 3 個微服務(wù)),采用 Spring Cloud Alibaba 2023.0.0.0 版本技術(shù)棧(核心組件:Nacos 2.5.0 注冊中心與配置中心),分享如何構(gòu)建一個微服務(wù)網(wǎng)關(guān)。

為什么需要微服務(wù)網(wǎng)關(guān)

我們當(dāng)前模擬的這個項目中包含了三個業(yè)務(wù)服務(wù),如果部署到線上的話,每個服務(wù)都有自己的ip(或域名)以及端口號。因此,我們的業(yè)務(wù)入口是分散的且暴露在外的,我們無法統(tǒng)一攔截異常流量以及限制接口訪問等。但有了微服務(wù)網(wǎng)關(guān),我們就可以將所有的請求都先集中在網(wǎng)關(guān)這里(有點類似于一個房子的大門口),由網(wǎng)關(guān)對所有請求進行統(tǒng)一的管理。

實踐

在知曉了網(wǎng)關(guān)的作用后,我們將實踐如何在一個現(xiàn)成的微服務(wù)項目中整合gateway網(wǎng)關(guān)以及做功能開發(fā)。當(dāng)然,在這之前,我們需要先完成整合。首先,我們需要建一個網(wǎng)關(guān)模塊,如下:

完成模塊的創(chuàng)建后,導(dǎo)入gateway相關(guān)的依賴,如下:

    <dependencies>
        <dependency>
            <groupId>com.pitayafruits</groupId>
            <artifactId>wechat-pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>

說明一下:這里引入的pojo包含了項目中常用的方法、工具類等;web則是因為網(wǎng)關(guān)本身也是一個可以訪問的服務(wù),所以需要引入;gateway則是這里需要使用的網(wǎng)關(guān)的依賴。然后來對它進行基礎(chǔ)的配置,如下:

server:
  port: 1000
  tomcat:
    uri-encoding: UTF-8
    max-swallow-size: -1 # 不限制請求體大小
spring:
  application:
    name: gateway
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: naocs
# 日志級別
logging:
  level:
    root: info

我們使用了nacos來管理服務(wù),網(wǎng)關(guān)自然也是一個服務(wù),因此也需要把它注冊到nacos。

1.統(tǒng)一路由

引入網(wǎng)關(guān)的首要作用是統(tǒng)一訪問的入口,所有的服務(wù)訪問都要先經(jīng)過網(wǎng)關(guān)。因此,第一個要實現(xiàn)的功能就是統(tǒng)一路由。而它的實現(xiàn)也是非常簡單,只需要在配置文件中做下簡單配置即可:

spring:
    gateway:
      discovery:
        locator:
          enabled: true # 開啟從注冊中心動態(tài)創(chuàng)建路由的功能,利用微服務(wù)名進行路由
      routes:           # 路由配置信息(數(shù)組/list)
        - id: authRoute # 每項路由規(guī)則都有一個唯一的id編號,可以自定義
          uri: lb://auth-service # lb=負載均衡,會動態(tài)尋址
          predicates:
            - Path=/a/**
        - id: fileRoute
          uri: lb://file-service
          predicates:
            - Path=/f/**
        - id: mainRoute
          uri: lb://main-service
          predicates:
            - Path=/m/**
      globalcors: # 允許跨域的相關(guān)配置
        cors-configurations:
          '[/**]':
            allowedOriginPatterns: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
            allowCredentials: true

這里對routes下的相關(guān)配置說明下:id是給每個服務(wù)的路由一個唯一編號,保證唯一即可,通常我們采用的寫法是服務(wù)名+route;uri則是服務(wù)名稱,如果寫成ip或者域名,那么地址發(fā)生變化,我們還需要重新修改配置,但是服務(wù)名稱是可以固定不變的;接下來是predicates,它可以配置多個值,我們一個服務(wù)里會有多個controller,把每個controller的路由配置在這里即可,/**表示指定的controller下的所有方法。

另外,如果負載均衡這個寫法無法被識別,說明你當(dāng)前使用的spring-cloud版本中默認并不包含相關(guān)依賴,我們需要手動引入它。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

完成上述配置后,我們此時其他服務(wù)的API將無法直接訪問,而統(tǒng)一通過網(wǎng)關(guān)來訪問。例如原本main-service中的127.0.0.1:88/m/hello 變成了 127.0.0.1:1000/m/hello

2.限流防刷

提到網(wǎng)關(guān),一個繞不開的話題就是限流。如果有人惡意刷我們的接口,我們就需要對某些IP進行訪問限制,比如在XX秒內(nèi)訪問同一接口超過XX次,就需要限制訪問。它的實現(xiàn)非常簡單,聲明一個處理類繼承g(shù)ateway的相關(guān)過濾接口即可。代碼如下:

@Component
public class IPLimitFilter implements GlobalFilter {
    private static final Integer continueCounts = 3;
    private static final Integer timeInterval = 20;
    private static final Integer limitTimes = 30;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        return doLimit(exchange, chain);
    }
    /**
     * 限制ip請求次數(shù)的判斷
     *
     * @param exchange 請求交換器
     * @param chain     過濾器鏈
     * @return 返回值
     */
    public Mono<Void> doLimit(ServerWebExchange exchange,
                              GatewayFilterChain chain) {
        // 獲取ip
        ServerHttpRequest request = exchange.getRequest();
        String ip = IPUtil.getIP(request);
        // 正常ip定義
        final String ipRedisKey = "gateway-ip" + ip;
        // 被攔截的黑名單,如果在redis中存在,那么就不允許訪問
        final String ipRedisLimitKey = "gateway-ip:limit" + ip;
        // 判斷當(dāng)前ip的剩余時間,如果大于0,則表示還處于黑名單
        long limitLeftTimes = redis.ttl(ipRedisLimitKey);
        if ( limitLeftTimes > 0 ) {
            return renderErrorMsg(exchange, ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
        }
        // 在redis中更新次數(shù)
        long requestCounts = redis.increment(ipRedisKey, 1);
        // 如果第一次訪問,就需要設(shè)置間隔時間
        if (requestCounts == 1) {
            redis.expire(ipRedisKey, timeInterval);
        }
        // 如果還能獲得正常請求次數(shù),說明用戶的正常請求落在正常時間內(nèi),超過則限制
        if (requestCounts > continueCounts) {
            redis.set(ipRedisLimitKey, ipRedisLimitKey, limitTimes);
            return renderErrorMsg(exchange, ResponseStatusEnum.SYSTEM_ERROR_BLACK_IP);
        }
        // 放行請求
        return chain.filter(exchange);
    }
    //過濾器的順序,數(shù)字越小優(yōu)先級越大.
    @Override
    public int getOrder() {
        return 1;
    }

我們需要借助redis來實現(xiàn)根據(jù)時間對指定ip的控制,這里的邏輯是:如果某個ip在30秒訪問超過三次,就限制訪問,如果限制了,則20秒后再恢復(fù)。

3.登錄鑒權(quán)

關(guān)于登錄鑒權(quán),我們目前通常會采用無狀態(tài)的做法:即用戶登錄后,后端返回token給前端,前端后續(xù)所有的請求都在headers中攜帶token,后端服務(wù)不存儲token,只對前端發(fā)來的token進行校驗和解析。而網(wǎng)關(guān)作為所有服務(wù)的入口,自然而然地也就可以承擔(dān)起這個職責(zé)了。

import com.google.gson.Gson;
import com.pitayafruits.base.BaseInfoProperties;
import com.pitayafruits.grace.result.GraceJSONResult;
import com.pitayafruits.grace.result.ResponseStatusEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
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.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Component
@Slf4j
@RefreshScope
public class SecurityFilterToken extends BaseInfoProperties implements GlobalFilter, Ordered {
    @Resource
    private ExcludeUrlProperties excludeUrlProperties;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 獲取用戶請求路徑
        String url = exchange.getRequest().getURI().getPath();
        // 獲取所有需要排除校驗的url
        List<String> excludeList = excludeUrlProperties.getUrls();
        // 校驗并排除url
        if (excludeList != null && !excludeList.isEmpty()) {
            for (String excludeUrl : excludeList) {
                if (antPathMatcher.matchStart(excludeUrl, url)) {
                    return chain.filter(exchange);
                }
            }
        }
        // 從header中獲得用戶id和token
        String userId = exchange.getRequest().getHeaders().getFirst(HEADER_USER_ID);
        String userToken = exchange.getRequest().getHeaders().getFirst(HEADER_USER_TOKEN);
        // 校驗header中的token
        if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)) {
            String redisToken = redis.get(REDIS_USER_TOKEN + ":" + userId);
            if (redisToken.equals(userToken)) {
                return chain.filter(exchange);
            }
        }
        // 默認不放行
        return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN);
    }
    //過濾器的順序,數(shù)字越小優(yōu)先級越大.
    @Override
    public int getOrder() {
        return 0;
    }
    /**
     * 異常信息包裝
     *
     * @param exchange   交換器
     * @param statusEnum 狀態(tài)枚舉
     * @return 返回值
     */
    public Mono<Void> renderErrorMsg(ServerWebExchange exchange,
                                     ResponseStatusEnum statusEnum) {
        //1.獲得response
        ServerHttpResponse response = exchange.getResponse();
        //2.構(gòu)建jsonResult
        GraceJSONResult jsonResult = GraceJSONResult.exception(statusEnum);
        //3.設(shè)置header類型
        if (!response.getHeaders().containsKey("Content-Type")) {
            response.getHeaders().add("Content-Type",
                    MimeTypeUtils.APPLICATION_JSON_VALUE);
        }
        //4.設(shè)置狀態(tài)碼
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        //5.轉(zhuǎn)換json并向response寫數(shù)據(jù)
        String resultJson = new Gson().toJson(jsonResult);
        DataBuffer buffer = response.bufferFactory().wrap(resultJson.getBytes(StandardCharsets.UTF_8));
        //6.返回
        return response.writeWith(Mono.just(buffer));
    }
}

在我這個示例中,我做的校驗邏輯很簡單:只是用戶登錄的時候會在redis里存放生成的token,然后其他接口訪問的時候比對下傳來的tokenredis里存放的token是否一致。這里需要關(guān)注下過濾器的順序,目前的案例中我們已經(jīng)編寫了兩個過濾器-限流防刷和登錄鑒權(quán)。所以可以把登錄鑒權(quán)過濾器的執(zhí)行順序改為0,限流防抖改為1。

另外,我們需要對部分接口放行不攔截,比如登錄接口。而我這里的做法則是將放行接口寫在配置文件里,并聲明配置類進行讀取。

exclude.urls[0] = /passport/getSMSCode
exclude.urls[1] = /passport/regist
exclude.urls[2] = /passport/login
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@Data
@PropertySource("classpath:excludeUrlPath.properties")
@ConfigurationProperties(prefix = "exclude")
public class ExcludeUrlProperties {
    private List<String> urls;
}

特別說明下:這里制定好過濾器的執(zhí)行順序后,內(nèi)部的驗證邏輯根據(jù)自己實際情況填寫,我這里沒用鑒權(quán)框架只是方便講解,要用也很簡單,引入之后把相關(guān)的鑒權(quán)邏輯寫進對應(yīng)的過濾器就行。

小結(jié)

在本文中,我們完成了Spring Cloud Gateway微服務(wù)網(wǎng)關(guān)的整合,并完成了三個最基礎(chǔ)常見的實踐場景。如果你的項目有更多的業(yè)務(wù)需求,只需要加相應(yīng)的過濾器并制定好過濾器的執(zhí)行順序即可,希望對大家有所幫助!

到此這篇關(guān)于Spring Boot 3 整合 Spring Cloud Gateway實踐過程的文章就介紹到這了,更多相關(guān)Spring Boot 整合 Spring Cloud Gateway 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中分組統(tǒng)計的三種實現(xiàn)方式

    java中分組統(tǒng)計的三種實現(xiàn)方式

    這篇文章主要介紹了java中分組統(tǒng)計的三種實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 解決Unable to start embedded container SpringBoot啟動報錯問題

    解決Unable to start embedded container&nbs

    這篇文章主要介紹了解決Unable to start embedded container SpringBoot啟動報錯問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • springboot讀取文件,打成jar包后訪問不到的解決

    springboot讀取文件,打成jar包后訪問不到的解決

    這篇文章主要介紹了springboot讀取文件,打成jar包后訪問不到的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • SpringBoot 微信退款功能的示例代碼

    SpringBoot 微信退款功能的示例代碼

    這篇文章主要介紹了SpringBoot 微信退款功能的實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • WebUploader實現(xiàn)圖片上傳功能

    WebUploader實現(xiàn)圖片上傳功能

    這篇文章主要為大家詳細介紹了WebUploader實現(xiàn)圖片上傳功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-03-03
  • 如何在Redis中實現(xiàn)分頁排序查詢過程解析

    如何在Redis中實現(xiàn)分頁排序查詢過程解析

    這篇文章主要介紹了如何在Redis中實現(xiàn)分頁排序查詢過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • Java SpringBoot自動裝配原理詳解及源碼注釋

    Java SpringBoot自動裝配原理詳解及源碼注釋

    SpringBoot的自動裝配是拆箱即用的基礎(chǔ),也是微服務(wù)化的前提。其實它并不那么神秘,我在這之前已經(jīng)寫過最基本的實現(xiàn)了,大家可以參考這篇文章,來看看它是怎么樣實現(xiàn)的,我們透過源代碼來把握自動裝配的來龍去脈
    2021-10-10
  • Springmvc請求參數(shù)類型轉(zhuǎn)換器及原生api代碼實例

    Springmvc請求參數(shù)類型轉(zhuǎn)換器及原生api代碼實例

    這篇文章主要介紹了Springmvc請求參數(shù)類型轉(zhuǎn)換器及原生api代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • JCrontab簡單入門實例詳解

    JCrontab簡單入門實例詳解

    這篇文章主要為大家詳細介紹了JCrontab簡單入門實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • java利用pdfbox+poi往pdf插入數(shù)據(jù)

    java利用pdfbox+poi往pdf插入數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于java利用pdfbox+poi如何往pdf插入數(shù)據(jù)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2022-02-02

最新評論