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-8007cloud: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 {// 第一步:载荷payloadJwtClaims 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并将其处理为jwtClaimsJwtClaims 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")) {// 颁发登录tokenString 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 {@Autowiredprivate 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 】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/834198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单数据结构——栈和队列1(栈超全)(初始化,销毁,出栈入栈销毁实现,例题运用)

知识特点 类似数据表链表&#xff0c;在逻辑上依次存储&#xff0c;但对比顺序表和链表有所限制&#xff0c;不能随便存储 一定要先掌握顺序表的实现&#xff0c;本人博客有顺序表专栏大家可以自行查看&#xff0c;看懂顺序表专栏之后再来了解栈的实现会更容易懂。 如果还没…

使用DBeaver连接postgreSql提示缺少驱动

重新安装电脑之后用dbeaver链接数据库的时候&#xff0c;链接PG库一直提示缺少驱动&#xff0c;当选择下载驱动的时候又非常非常慢经常失败&#xff0c;尝试了一下更改源然后下载库驱动就非常快了&#xff0c;当然也包括dbeaver的自动更新。 方法&#xff1a;点击菜单栏【窗口…

闲来装个虚拟机Ubuntu24.04和硬盘分区及挂载

简述 最近ubuntu出新版本了&#xff0c;ubuntu24.04&#xff0c; 俗称高贵食蚁兽。5年前进行Android或者linux开发基本是在windows下的虚拟机中进行。目前&#xff0c;虽然物质基础提高了&#xff0c;功能有独立进行编译、代码管理的服务器了。可以通过ssh登录&#xff0c;但是…

Seata之AT 模式的使用

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Seata 是一款开源的…

ChIP-seq染色质图谱检测方法的局限性及改善方式

ChIP-seq是最广泛使用的染色质图谱检测方法&#xff0c;但有很大的局限性&#xff0c;具体表现为&#xff1a; 1.高细胞需求量&#xff1b; 2.低吞吐量&#xff1b; 3.技术困难&#xff1b; 4.高成本&#xff0c;深度测序&#xff1b; 5.数据质量差&#xff0c;变量大 CUT&…

使用CNN或resnet,分别在flower5,flower17,flower102数据集上实现花朵识别分类-附源码-免费

前言 使用cnn和resnet实现了对flower5&#xff0c;flower17&#xff0c;flower102数据集上实现花朵识别分类。也就是6份代码&#xff0c;全部在Gitee仓库里&#xff0c;记得点个start支持谢谢。 本文给出flower17在cnn网络实现&#xff0c;flower102在resnet网络实现的代码。…

docker私有仓库的registry

简介 Docker私有仓库的Registry是一个服务&#xff0c;主要用于存储、管理和分发Docker镜像。具体来说&#xff0c;Registry的功能包括&#xff1a; 存储镜像&#xff1a;Registry提供一个集中的地方来存储Docker镜像&#xff0c;包括镜像的层次结构和元数据。 版本控制&…

基于HSI模型的水下图像增强算法,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

【数据结构】-- 链表专题

链表的分类 前面我们实现了单链表&#xff0c;单链表只是链表的一种。可以根据以下几个标准来判断链表的类型&#xff1a; 1.单向或者双向 如图所示&#xff0c;单向链表中一个节点的指针域只储存了下一个节点的指针&#xff0c;能通过前一个节点访问后一个节点&#xff0c;无…

【4089】基于小程序实现的互动打卡系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

怎么写毕业论文的? 推荐4个AI工具

写作这件事一直让我们从小学时期就开始头痛&#xff0c;初高中时期800字的作文让我们焦头烂额&#xff0c;一篇作文里用尽了口水话&#xff0c;拼拼凑凑才勉强完成。 大学时期以为可以轻松顺利毕业&#xff0c;结果毕业前的最后一道坎拦住我们的是毕业论文&#xff0c;这玩意不…

短视频矩阵系统源码/saas--总后台端、商户端、代理端、源头开发

短视频矩阵系统源码/saas--总后台端、商户端、代理端、源头开发 搭建短视频矩阵系统源码的交付步骤可以概括为以下几个关键环节&#xff1a; 1. **系统需求分析**&#xff1a;明确系统需要支持的功能&#xff0c;如短视频的上传、存储、播放、分享、评论、点赞等。 2. **技术选…

Python深度学习基于Tensorflow(5)机器学习基础

文章目录 监督学习线性回归逻辑回归决策树支持向量机朴素贝叶斯 集成学习BaggingBoosting 无监督学习主成分分析KMeans聚类 缺失值和分类数据处理处理缺失数据分类数据转化为OneHot编码 葡萄酒数据集示例 机器学习的流程如下所示&#xff1a; 具体又可以分为以下五个步骤&#…

Python开源工具库使用之运动姿势追踪库mediapipe

文章目录 前言一、姿势估计1.1 姿态关键点1.2 旧版 solution API1.3 新版 solution API1.4 俯卧撑计数 二、手部追踪2.1 手部姿态2.2 API 使用2.3 识别手势含义 参考 前言 Mediapipe 是谷歌出品的一种开源框架&#xff0c;旨在为开发者提供一种简单而强大的工具&#xff0c;用…

[C++核心编程-04]----C++类和对象之封装

目录 引言 正文 01-类和对象简介 02-封装简介 03-封装的意义 04-封装案例之设计学生类 05-封装的权限控制 06-struct和class的区别 07-成员属性设置为私有 08-封装案例1-设计立方体 09-封装案例2-判断点和圆的关系 总结 引言 在C中&#xff0c;…

Failed to build flash-attn:ERROR: Could not build wheels for flash-attn

安装 FlashAttention 的时候遇到报错&#xff1a; Failed to build flash-attn ERROR: Could not build wheels for flash-attn, which is required to install pyproject.toml-based projects可能是安装的版本与环境存在冲突吧&#xff0c;我的环境是&#xff1a; python 3.1…

堆的应用2——TOPK问题

TOPK问题 TOP-K问题&#xff1a;即求数据结合中前K个最大的元素或者最小的元素&#xff0c;一般情况下数据量都比较大。 比如&#xff1a;专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 情况1——数据量小 对于Top-K问题&#xff0c;能想到的最简单直接的方式就…

嵌入式C语言高级教程:实现基于STM32的自适应交通信号控制系统

自适应交通信号控制系统能够基于实时交通流数据调整信号灯的时长&#xff0c;提高路口的通行效率。本教程将指导您如何在STM32微控制器上实现一个基本的自适应交通信号控制系统。 一、开发环境准备 硬件要求 微控制器&#xff1a;STM32F103C8&#xff0c;具备足够的处理能力…

Eclipse下载安装教程(包含JDK安装)【保姆级教学】【2023.10月最新版】

目录 文章最后附下载链接 第一步&#xff1a;下载Eclipse&#xff0c;并安装 第二步&#xff1a;下载JDK&#xff0c;并安装 第三步&#xff1a;Java运行环境配置 安装Eclipse必须同时安装JDK &#xff01;&#xff01;&#xff01; 文章最后附下载链接 第一步&#xf…

[法规规划|数据概念]金融行业数据资产和安全管理系列文件解析(3)

“ 金融行业在自身数据治理和资产化建设方面一直走在前列。” 一直以来&#xff0c;金融行业由于其自身需要&#xff0c;都是国内开展信息化建设最早&#xff0c;信息化程度最高的行业。 在当今数据要素资产化的浪潮下&#xff0c;除了行业自身自身数据治理和资产化建设方面&am…