SpringCloudAlibaba:4.3云原生网关higress的JWT 认证

概述

简介

JWT是一种用于双方之间传递安全信息的简洁的、URL安全的声明规范。

定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息,特别适用于分布式站点的单点登录(SSO)场景

session认证的缺点

1.安全性:CSRF攻击因为基于cookie来进行用户识别, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

2.扩展性:对于分布式应用,需要实现 session 数据共享

3.性能:每一个用户经过后端应用认证之后,后端应用都要在服务端做一次记录,以方便用户下次请求的鉴别, 通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大

JWT的优点

1.无状态

2.适合移动端应用

3.单点登录友好

流程

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage,此后,客户端每次与服务器通信,都要带上这个JWT。 你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

编写JWT的流程

第一步:头部header

JSON对象,描述 JWT 的元数据 { "alg": "HS256", "typ": "JWT" }

alg属性表示签名的算法,默认是 HMAC SHA256(写成 HS256 )

typ属性表示这个令牌(token)的类型(type),统一写为 JWT

第二步:载荷payload

{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }

内容又可以分为3中标准:标准中注册的声明/公共的声明/私有的声明

1.payload-标准中注册的声明 (建议但不强制使用):

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的.

iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 2.payload-公共的声明 : 公共的声明可以添加任何的信息。一般这里我们会存放一下用户的基本信息(非敏感信息)

3.payload-私有的声明 : 私有声明是提供者和消费者所共同定义的声明。需要注意的是,不要存放敏感信息!!!

第三步:签证signature

签证的值是对头部和载荷进行base64UrlEncode后使用指定算法签名生成【私钥签名,到时候用公钥解密】

1.放入头部

2.放入载荷

3.使用私【公】钥签署

4.设置签名算法

如:以默认HS256为例,指定一个密钥(secret),就会按照如下公式生成

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret, )

第四步

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户

示例

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>java_sc_alibaba</artifactId>
        <groupId>jkw.life</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>test-higress-jwt-8007</artifactId>

    <dependencies>
        <!-- nacos-discovery -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- SpringMVC-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- JWT-->
        <dependency>
            <groupId>org.bitbucket.b_c</groupId>
            <artifactId>jose4j</artifactId>
            <version>0.7.0</version>
        </dependency>
    </dependencies>

</project>

application.yml

server:
  port: 8007
spring:
  application:
    name: test-higress-jwt-8007
  cloud:
    nacos:
      discovery:
        # 配置 nacos注册中心地址
        server-addr: 192.168.66.103:8848

启动类

package jkw;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class Main8007 {
    public static void main(String[] args) {
        SpringApplication.run(Main8007.class, args);
        log.info("************** 服务提供者 8001 启动成功 ************");
    }
}

工具类【utils.JWTUtil】

package jkw.utils;

import org.jose4j.json.JsonUtil;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.ErrorCodes;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;

import java.security.PrivateKey;
import java.security.PublicKey;

/**
 * JWT工具类
 */
public class JWTUtil {

    public static void main(String[] args) throws JoseException {
        RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
        //生成公钥
        //{"kty":"RSA","n":"ziX1yaqbQWGGto1B4NxvmYifUTSigM2LEN0KubXoxt7t9Nz9NaqES4Y36e_v_DhT_v0mKC74pReTWcDVSXZE49jFWphBNlcsnWOsjMlntVlZ_rOQyLZEMhcqshQVvBU8UPFoc77UYBddAjnnShdrSsP5e9qMeMAJVsRCEJZ3Y1IkwRmUGThhmqXNGn1UEhtMSrXDewkre7AWNVkixky7SV-0WhdA6QrEPtLfXoXBQseO2QgRAA73Gc7rs1hF89lKphcBx_mtngonAltNtGGuDhXriBCnt_zuUx8Bt7S-XlECxjSFtHbWKsgOuWTXxMIOVMHoerinsDP1AKmIqPo5xw","e":"AQAB"}
        final String publicKey = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
        System.out.println(publicKey);
        //生成私钥
        //{"kty":"RSA","n":"ziX1yaqbQWGGto1B4NxvmYifUTSigM2LEN0KubXoxt7t9Nz9NaqES4Y36e_v_DhT_v0mKC74pReTWcDVSXZE49jFWphBNlcsnWOsjMlntVlZ_rOQyLZEMhcqshQVvBU8UPFoc77UYBddAjnnShdrSsP5e9qMeMAJVsRCEJZ3Y1IkwRmUGThhmqXNGn1UEhtMSrXDewkre7AWNVkixky7SV-0WhdA6QrEPtLfXoXBQseO2QgRAA73Gc7rs1hF89lKphcBx_mtngonAltNtGGuDhXriBCnt_zuUx8Bt7S-XlECxjSFtHbWKsgOuWTXxMIOVMHoerinsDP1AKmIqPo5xw","e":"AQAB","d":"LclImg4GhbL_lLQzGZpcPyGVIRgrr6f3ZztxEmZQ2TrSZzxeEPlagNvCt3bPOpnYLh5Tx0EHgMOHuruVo8dc7a5Lxx9h_IvIIPzuaiahninGT0fatHmnE-kJVpwXZ7rftqqnpG2SBfWqdsAdmtswvV5hnxyfboJYkKjuc3i385r4s1s0pTZp33C6adHWB7B_dyVouQyjKQfUu_hToD32omJcTmJNcsTIKOR2Lztx-2Dzc4V99-3qDVbwXbBTfle_1tIeHYtSOsaBqVWpqdPOXSw5D4QuFjqUIXEVhIpTw5qNIejlAJ6wSHKUpU7DRm7t7Yl3yH5TqGr1WRB5eA6cYQ","p":"91hbnLKTPYeZrIq77YoSO0mzu8Q-rdJOv9Nece_oTL9zxLyV0JdzBWRkCUEqoaWSb6j1oeXC1qliHOYyls3Jf1e7bjFvJVedPGRtmEBWW_-nRFrIIYKswcco5_qRy4fHtOdNqwObyO1-8hLlu46kU5pxujlHKz0DAclLVOG8Wjs","q":"1VySxagoE5SDnhb2PDmNRF1uXrFHo1bg62KqAfCaws-MtNxaC9dtfBAHKH-s2QEfMYpiLv1i0IkronhvUZug0L-DzLemdwCyV98naaBElMzkTsC2hZpkmqR95HTACFSzpC5KRspl1ZvIxq-U-n5BY9asqpkhoyn7dCpkRy9tWeU","dp":"R5SEfqaXQdk6Odq0ZBvvBsVfhFlYokkYjR8IWATLv1owkKDa4lDR8p-I67y2L62Q4UuOOloZtrGyORbNUSMgyv-CuHMJ7U6brFyL8uG7nEgyCfATts7wW-vdBLVY-APFYa8GpRUYQl-ouzmIzmyLVb5-ZxwoYnT3p86vRFNHhP0","dq":"i9qIYoNc8aixtVh7wvI-hQdxJySxPoHeIKyln2vlJbkCFDMz2vs0ytN-va8iz4OKvOBmh0KUGPkw3uhun2GRwgMnE3N17B9Kx4qAvR3OlnLPXEe53E1dkHguBSf6D_vlXMLy8QAOTDw3GPVSg_dqSVUYDSMfB2KnbnezD24pEXk","qi":"kSGhqTuRjKESkVeJdWipF9kFy5-1p6T2Ym-S74PVVjBChuasvkkJtfLppw-yb0fX504TjoLDJIvYNp2fDY3Gv8CVr_W4cQjViEONrbGspzQTZmPAVEH0TiovFZ7z_KNp3P5Pl6JXMhOwxKsEM3hGj8IL6-4Bkh9-wOHG1mOi4sQ"}
        final String privateKey = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
        System.out.println(privateKey);
    }

    //公钥
    private static final String publicKeyString = "{\"kty\":\"RSA\",\"n\":\"ziX1yaqbQWGGto1B4NxvmYifUTSigM2LEN0KubXoxt7t9Nz9NaqES4Y36e_v_DhT_v0mKC74pReTWcDVSXZE49jFWphBNlcsnWOsjMlntVlZ_rOQyLZEMhcqshQVvBU8UPFoc77UYBddAjnnShdrSsP5e9qMeMAJVsRCEJZ3Y1IkwRmUGThhmqXNGn1UEhtMSrXDewkre7AWNVkixky7SV-0WhdA6QrEPtLfXoXBQseO2QgRAA73Gc7rs1hF89lKphcBx_mtngonAltNtGGuDhXriBCnt_zuUx8Bt7S-XlECxjSFtHbWKsgOuWTXxMIOVMHoerinsDP1AKmIqPo5xw\",\"e\":\"AQAB\"}";
    //私钥
    private static final String privateKeyString = "{\"kty\":\"RSA\",\"n\":\"ziX1yaqbQWGGto1B4NxvmYifUTSigM2LEN0KubXoxt7t9Nz9NaqES4Y36e_v_DhT_v0mKC74pReTWcDVSXZE49jFWphBNlcsnWOsjMlntVlZ_rOQyLZEMhcqshQVvBU8UPFoc77UYBddAjnnShdrSsP5e9qMeMAJVsRCEJZ3Y1IkwRmUGThhmqXNGn1UEhtMSrXDewkre7AWNVkixky7SV-0WhdA6QrEPtLfXoXBQseO2QgRAA73Gc7rs1hF89lKphcBx_mtngonAltNtGGuDhXriBCnt_zuUx8Bt7S-XlECxjSFtHbWKsgOuWTXxMIOVMHoerinsDP1AKmIqPo5xw\",\"e\":\"AQAB\",\"d\":\"LclImg4GhbL_lLQzGZpcPyGVIRgrr6f3ZztxEmZQ2TrSZzxeEPlagNvCt3bPOpnYLh5Tx0EHgMOHuruVo8dc7a5Lxx9h_IvIIPzuaiahninGT0fatHmnE-kJVpwXZ7rftqqnpG2SBfWqdsAdmtswvV5hnxyfboJYkKjuc3i385r4s1s0pTZp33C6adHWB7B_dyVouQyjKQfUu_hToD32omJcTmJNcsTIKOR2Lztx-2Dzc4V99-3qDVbwXbBTfle_1tIeHYtSOsaBqVWpqdPOXSw5D4QuFjqUIXEVhIpTw5qNIejlAJ6wSHKUpU7DRm7t7Yl3yH5TqGr1WRB5eA6cYQ\",\"p\":\"91hbnLKTPYeZrIq77YoSO0mzu8Q-rdJOv9Nece_oTL9zxLyV0JdzBWRkCUEqoaWSb6j1oeXC1qliHOYyls3Jf1e7bjFvJVedPGRtmEBWW_-nRFrIIYKswcco5_qRy4fHtOdNqwObyO1-8hLlu46kU5pxujlHKz0DAclLVOG8Wjs\",\"q\":\"1VySxagoE5SDnhb2PDmNRF1uXrFHo1bg62KqAfCaws-MtNxaC9dtfBAHKH-s2QEfMYpiLv1i0IkronhvUZug0L-DzLemdwCyV98naaBElMzkTsC2hZpkmqR95HTACFSzpC5KRspl1ZvIxq-U-n5BY9asqpkhoyn7dCpkRy9tWeU\",\"dp\":\"R5SEfqaXQdk6Odq0ZBvvBsVfhFlYokkYjR8IWATLv1owkKDa4lDR8p-I67y2L62Q4UuOOloZtrGyORbNUSMgyv-CuHMJ7U6brFyL8uG7nEgyCfATts7wW-vdBLVY-APFYa8GpRUYQl-ouzmIzmyLVb5-ZxwoYnT3p86vRFNHhP0\",\"dq\":\"i9qIYoNc8aixtVh7wvI-hQdxJySxPoHeIKyln2vlJbkCFDMz2vs0ytN-va8iz4OKvOBmh0KUGPkw3uhun2GRwgMnE3N17B9Kx4qAvR3OlnLPXEe53E1dkHguBSf6D_vlXMLy8QAOTDw3GPVSg_dqSVUYDSMfB2KnbnezD24pEXk\",\"qi\":\"kSGhqTuRjKESkVeJdWipF9kFy5-1p6T2Ym-S74PVVjBChuasvkkJtfLppw-yb0fX504TjoLDJIvYNp2fDY3Gv8CVr_W4cQjViEONrbGspzQTZmPAVEH0TiovFZ7z_KNp3P5Pl6JXMhOwxKsEM3hGj8IL6-4Bkh9-wOHG1mOi4sQ\"}";

    /**
     * 生成token
     *
     * @param userId   用户id
     * @param username 用户名
     * @return
     */
    public static String sign(Long userId, String username) throws JoseException {

        // 第一步:载荷payload
        JwtClaims claims = new JwtClaims();
        // 注册的声明 1.jwt签发者
        claims.setIssuer("user");
        // 注册的声明 2.jwt所面向的用户
        claims.setSubject("subject");
        // 注册的声明 3.接收jwt的一方
        claims.setAudience("Audience");
        // 注册的声明 4.jwt的过期时间,这个过期时间必须要大于签发时间【从现在开始10分钟】
        claims.setExpirationTimeMinutesInTheFuture(10000);
        // 注册的声明 5.定义在什么时间之前,该jwt都是不可用的【2分钟前】
        claims.setNotBeforeMinutesInThePast(2);
        // 注册的声明 6.jwt的签发时间
        claims.setIssuedAtToNow();
        // 注册的声明 7.jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
        claims.setGeneratedJwtId();
        // 公共的声明:可可以添加任何的信息,一般这里我们会存放一下用户的基本信息
        claims.setClaim("userId", userId);
        claims.setClaim("username", username);


        // 第二步:签证signature:其值是对头部header和载荷payload进行base64UrlEncode后使用指定算法签名生成
        JsonWebSignature jws = new JsonWebSignature();
        // 1.放入头部
        jws.setKeyIdHeaderValue("keyId");
        // 2.放入载荷
        jws.setPayload(claims.toJson());
        // 3.使用私钥签名
        PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyString)).getPrivateKey();
        jws.setKey(privateKey);
        // 4.设置签名算法
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);

        //第三步:算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户
        String jwt = jws.getCompactSerialization();
        return jwt;
    }

    /**
     * 验证jwt
     *
     * @param jwt
     */
    public static void checkJwt(String jwt) throws MalformedClaimException, JoseException {

        //1.引入公钥,使用公钥对私钥的签名解密
        PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(publicKeyString)).getRsaPublicKey();
        //2.使用JwtConsumer解密
        JwtConsumer jwtConsumer = new JwtConsumerBuilder().setRequireExpirationTime()
                .setAllowedClockSkewInSeconds(30) // 允许在验证基于时间的令牌时留有一定的余地,以计算时钟偏差,单位/秒
                .setRequireSubject() // 主题声明
                .setExpectedIssuer("user") // 验证 jwt签发者
                .setExpectedAudience("Audience") // 验证 接收jwt的一方
                .setVerificationKey(publicKey) // 用公钥验证签名 ,验证私钥
                .setJwsAlgorithmConstraints( // 使用生成jwt的签名算法解密
                        new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, // 白名单
                                AlgorithmIdentifiers.RSA_USING_SHA256))
                .build();
        try {
            // 验证JWT并将其处理为jwtClaims
            JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
            //如果JWT失败的处理或验证,将会抛出InvalidJwtException,希望能有一些有意义的解释关于哪里出了问题
            System.out.println("JWT validation succeeded! " + jwtClaims);
        } catch (InvalidJwtException e) {
            System.out.println("Invalid JWT! " + e);
            // 对JWT无效的(某些)特定原因的编程访问也是可能的
            // 在某些情况下,您是否需要不同的错误处理行为。
            // JWT是否已经过期是无效的一个常见原因
            if (e.hasExpired()) {
                System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
            }
            // 或者观众是无效的
            if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) {
                System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
            }
        }
    }

}

状态码枚举类

package jkw.vo;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 状态码枚举类
 */
@Getter
@AllArgsConstructor
public enum CodeEnum {
    SUCCESS(200, "OK"),
    SYSTEM_ERROR(500, "系统异常"),
    ;


    private final Integer code;
    private final String message;
}

返回结果封装类

package jkw.vo;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

/**
 * 返回结果封装类
 */
@Data
@AllArgsConstructor
public class BaseResult<T> implements Serializable {
    private Integer code;//状态码(成功:200,失败:其他)
    private String message;//提示信息
    private T data;//返回数据

    //构建成功结果
    public static <T> BaseResult<T> ok() {
        return new BaseResult(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage(), null);
    }

    //构建带有数据的成功结果
    public static <T> BaseResult<T> ok(T data) {
        return new BaseResult(CodeEnum.SUCCESS.getCode(), CodeEnum.SUCCESS.getMessage(), data);
    }

}

用户服务

package jkw.service;


import com.alibaba.nacos.common.utils.StringUtils;
import jkw.utils.JWTUtil;
import jkw.vo.BaseResult;
import org.jose4j.lang.JoseException;
import org.springframework.stereotype.Service;

/**
 * 用户服务
 */
@Service
public class UserService {


    /**
     * 登录
     *
     * @param username
     * @param password
     * @return
     * @throws JoseException
     */
    public BaseResult login(String username, String password) throws JoseException {
        // 1.用户名或者密码校验
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            return new BaseResult(301, "用户名或者密码为空", null);
        }
        // 2.判断用户名和密码是否正确
        if (username.equals("admin") && password.equals("123456")) {
            // 颁发登录token
            String token = JWTUtil.sign(1001L, "admin");
            return BaseResult.ok(token);
        } else {
            return new BaseResult(301, "用户名或者密码不对", null);
        }
    }


}

用户控制器

package jkw.controller;

import jkw.service.UserService;
import jkw.vo.BaseResult;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.lang.JoseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用户控制器
 */
@RequestMapping("/user")
@RestController
public class UserCon {


    @Autowired
    private UserService userService;


    @PostMapping("/login")
    public BaseResult login(String username, String password) throws MalformedClaimException, JoseException {
        return userService.login(username, password);
    }


}

higress配置

文档

https://higress.io/zh-cn/docs/plugins/jwt-auth/

1.路由配置【8007-higress-jwt 精确匹配 | /user/login test-higress-jwt-8007.DEFAULT-GROUP.public.nacos】

2.插件配置【全局,配置】:策略:JWT Auth

consumers:
- issuer: "user"
  jwks: |
    {
     "keys": [
     {
      "kty":"RSA",
      "n":"ziX1yaqbQWGGto1B4NxvmYifUTSigM2LEN0KubXoxt7t9Nz9NaqES4Y36e_v_DhT_v0mKC74pReTWcDVSXZE49jFWphBNlcsnWOsjMlntVlZ_rOQyLZEMhcqshQVvBU8UPFoc77UYBddAjnnShdrSsP5e9qMeMAJVsRCEJZ3Y1IkwRmUGThhmqXNGn1UEhtMSrXDewkre7AWNVkixky7SV-0WhdA6QrEPtLfXoXBQseO2QgRAA73Gc7rs1hF89lKphcBx_mtngonAltNtGGuDhXriBCnt_zuUx8Bt7S-XlECxjSFtHbWKsgOuWTXxMIOVMHoerinsDP1AKmIqPo5xw",
      "e":"AQAB",
      "kid": "keyId",
      }
     ]
    }
  name: "consumer1"
global_auth: false

3.路由配置【路由级别,使用】:策略:JWT Auth

allow:
- "consumer1"

4.测试步骤:开启服务test-higress8006和test-higress-jwt-8007

首先访问http://www.jkw.com/user/login【admin/123456】获取jwt,

然后在访问配置jwt Auth的http://www.jkw.com/test/index

【请求头中添加Authorization,值为Bearer jwt的值,前面一定要加Bearer 】

相关推荐
it技术分享just_free1 小时前
基于 K8S kubernetes 搭建 安装 EFK日志收集平台
运维·docker·云原生·容器·kubernetes·k8s
loveLifeLoveCoding2 小时前
K8S volumn 挂载文件
云原生·容器·kubernetes
2401_840192274 小时前
在k8s中,客户端访问服务的链路流程,ingress--->service--->deployment--->pod--->container
云原生·容器·kubernetes
_.Switch5 小时前
构建现代应用的Python Serverless架构详解
运维·开发语言·python·云原生·架构·serverless·restful
小诸葛的博客5 小时前
istio中使用serviceentry结合egressgateway实现多版本路由
云原生·istio
福大大架构师每日一题6 小时前
16.2 k8s容器基础资源指标讲解
云原生·容器·kubernetes·prometheus
周湘zx6 小时前
k8s中的微服务
linux·运维·服务器·微服务·云原生·kubernetes
工业甲酰苯胺7 小时前
k8s 中的 Ingress 简介
云原生·容器·kubernetes
周湘zx7 小时前
k8s中的存储
linux·运维·云原生·容器·kubernetes
液态不合群17 小时前
低代码革命:加速云原生时代的端到端产品创新
低代码·云原生