JWT数字签名与token实现

news2024/11/17 19:47:33

JWT介绍

官方介绍

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

什么时候你应该用JSON Web Token ?

下列场景中使用JSON Web Token是很有用的:

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。
    在这里插入图片描述

博主讲解

JWT具体用来干什么的?

JSON Web Token (JWT) 就是用来保证数据不可篡改,鉴定消息无篡改;绝不是保证消息保密性的东西;这一点要特别清楚,首先清楚JWT具体用来干什么的,从而能够理解其设计原理;经过jwt加密的消息是公开可见的,是不可篡改的,因此jwt的消息体中payload的数据不要放敏感数据,如果需要放敏感数据,请加密。例如payload中可以存放用户名,密码的话需要加密后在存放;

明白了上述用途,再去了解原理才会融会贯通,下面讲解一下非对称加密;

非对称加密

非对称加密(如RSA)是通过公钥和私钥进行加密,解密的过程;私钥只能被一方所拥有,一般是服务器;公钥可以公开,任何人可以获取公钥;通过公钥加密的消息只能通过私钥解密,通过私钥加密的消息也只能通过公钥解密;但是这个过程不是线性的,使用公钥加密和私钥加密的密文是不一样的;

  • 公钥加密,私钥解密

    这种情况用于消息的加密,保证消息的安全性,即保证消息的不可见性,保密性,敏感性;一般是客户端向服务器发送消息时为了避免消息被泄露,使用服务器公开的公钥进行消息加密形成密文发给服务器,只有服务器使用私钥才能解密密文获得消息。

  • 私钥加密,公钥解密

    使用私钥加密,可以作为数字签名防止伪造,但是无法保证数据的不可见性;因为私钥加密的密文可以被其他用户使用公钥解密,其他用户也就可以获取消息,如果要避免敏感消息被盗取,需要将敏感消息加密后放在jwt的payload中。

小常识

遗憾的是,公钥算法速度非常慢,大约比对称算法慢 1000 倍。 使用它们来加密大量数据是不切实际的。 实际上,公钥算法用于加密 会话密钥。 对称算法 用于加密/解密大多数数据。由于对消息进行签名实际上会加密消息,因此使用公钥签名算法对大型消息进行签名是不切实际的。

JWT的数据结构

JWT其实就是一个很长的字符串,字符之间通过"."分隔符分为三个子串,各字串之间没有换行符。每一个子串表示了一个功能块,总共有三个部分:JWT头(header)有效载荷(payload)签名(signature),如下图所示:

在这里插入图片描述

JWT头

JWT头是一个描述JWT元数据的JSON对象,通常如下所示:

{"alg": "HS256","typ": "JWT"}

alg:表示签名使用的算法,默认为HMAC SHA256(写为HS256)

typ:表示令牌的类型,JWT令牌统一写为JWT

最后,使用Base64 URL算法将上述JSON对象转换为字符串,注意Base64 URL只是编码算法,不是加密算法,可以被解码得到数据。

有效载荷

有效载荷,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。

有效载荷部分规定有如下七个默认字段供选择:

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,还可以自定义私有字段。

userid:100001
username:nick
email:123@qq.com

最后,同样使用Base64 URL算法将有效载荷部分JSON对象转换为字符串,注意Base64 URL只是编码算法,不是加密算法,可以被解码得到数据。

签名

签名实际上是一个使用私钥加密的过程,是对上面两部分数据通过指定的算法生成哈希,以确保数据不会被篡改。

首先需要指定一个密码(secret,可以服务器随机设置,相当于种子),该密码仅仅保存在服务器中,并且不能向用户公开。然后使用JWT头中指定的签名算法(默认情况下为HMAC SHA256),根据以下公式生成签名哈希:为了得到签名部分,你必须有编码过的header、编码过的payload、一个密码,签名算法是header中指定的那个,然对它们签名即可。

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

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串 ,每个部分用"."分隔,就构成整个JWT对象。

xxxxx.yyyyy.zzzzz,xxxxx是header经过Base64 URL编码后的字符串,yyyyy是payload经过Base64 URL编码后的字符串,zzzzz是对xxxxx.yyyyy经过私钥加密后得到的字符串;

JWT签名算法

JWT签名算法中,一般有两个选择:HS256和RS256。

HS256 (带有 SHA-256 的 HMAC )是一种对称加密算法, 双方之间仅共享一个密钥,没有公钥私钥之分。由于使用相同的密钥生成签名和验证签名, 因此必须注意确保密钥不被泄密。

RS256 (采用SHA-256 的 RSA 签名) 是一种非对称加密算法, 它使用公共/私钥对: JWT的提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。

token认证

在讲token认证之前,我们先回顾一下以前熟悉的seesion认证,Cookie-session 认证机制是通过浏览器带上来Cookie对象来与服务器端的session对象匹配来实现状态管理。第一次请求认证在服务端创建一个Session对象,同时在用户的浏览器端创建了一个Cookie对象;当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效。session 认证的缺点其实很明显,由于 session 是保存在服务器里,所以如果分布式部署应用的话,会出现session不能共享的问题,很难扩展。最大的问题是session将用户信息存储在了服务器上,这样会导致服务器存储压力。而token相反,把认证签名存放在客户端,客户端访问只需要带上token令牌签名即可访问自己的账户;

JSON Web Tokens是如何工作的

在认证的时候,当用户用他们的凭证成功登录以后,一个JSON Web Token将会被返回。此后,token就是用户凭证了,你必须非常小心以防止出现安全问题。一般而言,你保存令牌的时候不应该超过你所需要它的时间。HTTP协议是无状态的,也就是说,如果我们已经认证了一个用户,那么他下一次请求的时候,服务器不知道我是谁,我们必须再次认证。传统的做法是将已经认证过的用户信息存储在服务器上,比如Session。用户下次请求的时候带着Session ID,然后服务器以此检查用户是否认证过。Sessions : 每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。 JWT与Session的差异 相同点是,它们都是存储用户信息;然而,Session是在服务器端的,而JWT是在客户端的。

  • Session方式存储用户信息的最大问题在于要占用大量服务器内存,增加服务器的开销。

  • 而JWT方式将用户状态分散到了客户端中,可以明显减轻服务端的内存压力。

  • Session的状态是存储在服务器端,客户端只有session id;而Token的状态是存储在客户端。

基于Token的身份认证是如何工作的 基于Token的身份认证是无状态的,服务器或者Session中不会存储任何用户信息。没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。

虽然这一实现可能会有所不同,但其主要流程如下:

  1. 用户携带用户名和密码请求访问 ,第一次访问必须使用账号密码;

  2. 服务器校验用户凭据,验证账号密码;

  3. 应用提供一个token给客户端,通过后生成jwt数据xxxxx.yyyyy.zzzzz作为token;

  4. 客户端存储token,并且在随后的每一次请求中都带着它,token用于验证用户身份;

  5. 服务器校验token并返回数据,如何验证呢?只需要使用私钥对xxxxx.yyyyy进行加密,然后得到的结果与zzzzz对比即可,通过后对yyyyy进行base64解码获得用户id,即证明客户端是上一次访问的用户,且数据未被篡改;如果被篡改了,那么xxxxx.yyyyy加密不能得到zzzzz。

    每一次请求都需要token,Token应该放在请求header中 ,我们还需要将服务器设置为接受来自所有域的请求。

其他

用Token的好处 - 无状态和可扩展性:Tokens存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。 - 安全:Token不是Cookie。(The token, not a cookie.)每次请求的时候Token都会被发送。而且,由于没有Cookie被发送,还有助于防止CSRF攻击。即使在你的实现中将token存储到客户端的Cookie中,这个Cookie也只是一种存储机制,而非身份认证机制。没有基于会话的信息可以操作,因为我们没有会话!

还有一点,token在一段时间以后会过期,这个时候用户需要重新登录。这有助于我们保持安全。还有一个概念叫token撤销,它允许我们根据相同的授权许可使特定的token甚至一组token无效。JWT与OAuth的区别 -OAuth2是一种授权框架 ,JWT是一种认证协议 -无论使用哪种方式切记用HTTPS来保证数据的安全性 -OAuth2用在使用第三方账号登录的情况(比如使用weibo, qq, github登录某个app),而JWT是用在前后端分离, 需要简单的对后台API进行保护时使用。

实战jwt

jjwt是一个提供JWT创建和验证的Java库。永远免费和开源(Apache License,版本2.0),JJWT很容易使用和理解。

导入依赖

    <dependencies>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.0</version>
        </dependency>

实战案例

package com.example.test;

import cn.hutool.core.io.FileUtil;
import io.jsonwebtoken.*;
import io.jsonwebtoken.SignatureException;
import org.junit.Test;

import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;

public class JwtTest {

    /**
     * 对称加密算法
     */

    @Test
    public void test_HS256(){
        String key = "8888"; //秘钥要大于3个字符,至少四个字符
        // 封装jwt的head部信息
        HashMap<String, Object> header = new HashMap<>();
        // SignatureAlgorithm.HS256.getValue() 使用HS256非对称加密算法
        header.put("arg",SignatureAlgorithm.HS256.getValue()); //指定加密算法,不使用加密算法的话设置value为none即可
        header.put("typ","JWT"); //指定令牌类型

        // 封装jwt的body部分
        HashMap<String, Object> body = new HashMap<>();
        body.put("account","15637283927");
        body.put("phone","123456");
        body.put("role","user");

        // 生成JWT令牌
        String token = Jwts.builder()
                .setHeader(header)
                .setClaims(body)
                .setId("00001") //设置这个jwt的唯一标识,不写也可以
                .signWith(SignatureAlgorithm.HS256,key) //使用公钥加密
                .compact();
        // 打印生成的jwt令牌token
        System.out.println(token);
        String[] split = token.split("\\.");
        System.out.println("header:"+split[0]);
        System.out.println("body:"+split[1]);

        // 解析jwt令牌,使用服务器的私钥解密
        Jwt res = null;
        try {
            res = Jwts
                    .parser()
                    .setSigningKey(key)
                    .parse(token);

            Header header_parser = res.getHeader();
            Object body_parser = res.getBody();
            System.out.println(header_parser);
            System.out.println(body_parser);

        } catch (SignatureException e) {
            System.out.println("秘钥错误!");
        }
    }
    /**
     * 生成公钥和私钥
     * @param seedPassword 提供种子密码
     * @return
     * @throws Exception
     */
    //生成自己的 秘钥/公钥 对
    public byte[][] GeneratPubPriKey(String seedPassword) throws Exception{
        // 使用RSA算法,获得RSA算法实例
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(seedPassword.getBytes());
        // 使用种子密码初始化
        keyPairGenerator.initialize(1024, secureRandom);
        // 获得密码对,编码为byte类型
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        //存入本地文件,可以使用最后面提供的代码读取秘钥文件,这里作为例子直接返回
//        FileUtil.writeBytes(publicKeyBytes, "d:\\pub.key");
//        FileUtil.writeBytes(privateKeyBytes, "d:\\pri.key");
        byte[][] key = new byte[][]{publicKeyBytes,privateKeyBytes};
        return key;
    }

    /**
     * 非对称加密算法:实现的是私钥加密,公钥解密
     * 一般用于身份识别,电子签名,确认加密的数据属于公钥下发方
     * 能用你的公钥解密,那么数据一定是你的(一定使用你的私钥加密的)
     * 用于识别服务器身份
     */
    @Test
    public void test_RS256() throws Exception {
        // 封装jwt的head部信息
        HashMap<String, Object> header = new HashMap<>();
        // SignatureAlgorithm.HS256.getValue() 使用HS256非对称加密算法
        header.put("arg",SignatureAlgorithm.RS256.getValue()); //指定加密算法,不使用加密算法的话设置value为none即可
        header.put("typ","JWT"); //指定令牌类型

        // 封装jwt的body部分
        HashMap<String, Object> body = new HashMap<>();
        body.put("account","mimiNick");
        body.put("phone","123456");
        body.put("role","user");

        // 获取公钥和私钥
        byte[][] keys = GeneratPubPriKey("8888888");
        // 获取私钥对象
        byte[] priKeyByte = keys[1];
        PKCS8EncodedKeySpec spec2 = new PKCS8EncodedKeySpec(priKeyByte);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(spec2);

        // 生成JWT令牌
        String token = Jwts.builder()
                .setHeader(header)
                .setClaims(body)
                .setId("00001") //设置这个jwt的唯一标识,不写也可以
                .signWith(SignatureAlgorithm.RS256,privateKey) //使用私钥加密
                .compact();

        // 打印生成的jwt令牌token
        System.out.println(token);
        String[] split = token.split("\\.");
        System.out.println("header:"+split[0]);
        System.out.println("body:"+split[1]);

        //获取公钥
        byte[] publicByte = keys[0];
        X509EncodedKeySpec spec = new X509EncodedKeySpec(publicByte);
        PublicKey publicKey = kf.generatePublic(spec);

        // 解析jwt令牌,使用服务器的私钥解密
        Jwt res = null;
        try {
            res = Jwts
                    .parser()
                    .setSigningKey(publicKey) //使用公钥解密
                    .parse(token);

            Header header_parser = res.getHeader();
            Object body_parser = res.getBody();
            System.out.println(header_parser);
            System.out.println(body_parser);

        } catch (SignatureException e) {
            System.out.println("秘钥错误!");
        }
    }



    @Test
    public void test_RS256_2() throws Exception {
        // 封装jwt的head部信息
        HashMap<String, Object> header = new HashMap<>();
        // SignatureAlgorithm.HS256.getValue() 使用HS256非对称加密算法
        header.put("arg",SignatureAlgorithm.RS256.getValue()); //指定加密算法,不使用加密算法的话设置value为none即可
        header.put("typ","JWT"); //指定令牌类型

        // 封装jwt的body部分
        HashMap<String, Object> body = new HashMap<>();
        body.put("account","mimiNick");
        body.put("phone","123456");
        body.put("role","user");

        // 获取公钥和私钥
        byte[][] keys = GeneratPubPriKey("8888888");
        // 获取私钥对象
        byte[] priKeyByte = keys[1];
        PKCS8EncodedKeySpec spec2 = new PKCS8EncodedKeySpec(priKeyByte);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = kf.generatePrivate(spec2);

        //获取公钥
        byte[] publicByte = keys[0];
        X509EncodedKeySpec spec = new X509EncodedKeySpec(publicByte);
        PublicKey publicKey = kf.generatePublic(spec);


        // 生成JWT令牌
        String token = Jwts.builder()
                .setHeader(header)
                .setClaims(body)
                .setId("00001") //设置这个jwt的唯一标识,不写也可以
                .signWith(SignatureAlgorithm.RS256,privateKey) //使用私钥加密
                .compact();

        // 打印生成的jwt令牌token
        System.out.println(token);
        String[] split = token.split("\\.");
        System.out.println("header:"+split[0]);
        System.out.println("body:"+split[1]);


        // 解析jwt令牌,使用服务器的私钥解密
        Jwt res = null;
        try {
            res = Jwts
                    .parser()
                    .setSigningKey(publicKey) //使用公钥解密
                    .parse(token);

            Header header_parser = res.getHeader();
            Object body_parser = res.getBody();
            System.out.println(header_parser);
            System.out.println(body_parser);

        } catch (SignatureException e) {
            System.out.println("秘钥错误!");
        }
    }
}

获取磁盘上的秘钥文件代码

    //获取私钥
    public PrivateKey getPriKey() throws Exception{
        InputStream resourceAsStream = 
            this.getClass().getClassLoader().getResourceAsStream("pri.key");
        DataInputStream dis = new DataInputStream(resourceAsStream);
        byte[] keyBytes = new byte[resourceAsStream.available()];
        dis.readFully(keyBytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePrivate(spec);
    }

    //获取公钥
    public PublicKey getPubKey() throws Exception{
        InputStream resourceAsStream = 
            this.getClass().getClassLoader().getResourceAsStream("pub.key");
        DataInputStream dis = new DataInputStream(resourceAsStream);
        byte[] keyBytes = new byte[resourceAsStream.available()];
        dis.readFully(keyBytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        return kf.generatePublic(spec);
    }

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

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

相关文章

python爬虫—selenium获取csdn质量分并用echarts可视化分析

文章目录 ⭐前言⭐selenium&#x1f496; 获取所有的文章url&#x1f496; 根据url查询分数&#x1f496; inscode结合echarts展示结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享关于python自动化获取个人博客质量分并可视化。 该系列文章&#xff1a; python爬…

MATLAB App Designer基础教程 Matlab GUI入门(一)

MATLAB GUI入门 第一天 学习传送门&#xff1a; 【MATLAB App Designer基础教程Matlab GUI界面设计&#xff08;全集更新完毕-用户界面设计appdesigner&#xff08;中文&#xff09;Matlab Gui教程】 https://www.bilibili.com/video/BV16f4y147x9/?p2&share_sourcecopy_…

音视频解码流程解析

文章目录 1.音视频解码过程2.FFmpeg解音视频流程3.关键函数说明 1.音视频解码过程 2.FFmpeg解音视频流程 3.关键函数说明 avcodec_find_decoder:根据指定AVCodecID查找注册的解码器av_parser_init:初始化AVCodecParserContextavcodec_alloc_context3:创建AVCodecContext上下文…

同行共建,引领潮流,我来说说亚马逊云科技的中国新举措

亚马逊云科技全面阐述生成式AI新机遇&#xff0c;宣布多项举措助力企业加速创新、可持续发展。 【全球云观察 &#xff5c; 热点关注】当前&#xff0c;全球公有云整体规模已经发展到足够大了&#xff0c;来自专业分析机构的数据统计显示&#xff0c;包括了laaS、PaaS、SaaS在内…

gitlab集成kubernetes

gitlab集成kubernetes 1. 管理员通过服务模板添加Kubernetes集群&#xff0c;是生效所有项目 2. 群组中也可以添加Kubernetes集群&#xff0c;优先于管理员添加的全局集群 3. 链接现有集群 4. 配置集群信息 http://178.119.31.180/help/user/project/clusters/add_existing_cl…

uniapp中环状进度条

调用插件&#xff1a; <circle-progress-bar :pro"84/100" :border_back_color"#297DFE" :border_color"#FB8F23">{{84}}%</circle-progress-bar>添加插件引用&#xff1a; <script>import CircleProgressBar from ../../com…

QT桌面挂件动画

目录 参考功能实现05DesktopPattern.promain.cppdesktoppattern.hdesktoppattern.cppwallpaper.hwallpaper.cpp 效果模糊知识点 参考 图片资源 功能 桌面挂件动画置顶切换挂件动画图片选择更换桌面壁纸显示时改变桌面壁纸&#xff0c;隐藏/退出时还原桌面壁纸系统托盘菜单&a…

R语言复现一篇6分的孟德尔随机化文章

上一期我们对孟德尔随机化做了一个简单的介绍&#xff0c;今天我们来复现一篇6分左右的使用了孟德尔随机化方法的文章&#xff0c;文章的题目是&#xff1a;Mendelian randomization analysis does not reveal a causal influence of mental diseases on osteoporosis&#xff…

Angular 与 PDF之五 实现方式的选择与扩展

在纯web的前提下&#xff08;不考虑移动端native&#xff09;&#xff0c;PDF的功能基本包括&#xff1a; 客户端PDF&#xff1a;最简单的场景&#xff0c;实现方式也很多&#xff0c;基本不需要有什么顾虑的地方&#xff0c;简单的实现可以参考系列第一篇文章。客户端PDF预览&…

【NM 2019】综述:基于机器学习引导的定向进化蛋白质工程

Machine-learning-guided directed evolution for protein engineering | Nature Methods Machine-learning-guided directed evolution for protein engineering 机器学习引导的定向进化蛋白质工程 图1 | 带和不带机器学习的定向进化。 a&#xff09;定向进化利用迭代循环的…

MySQL数据库---笔记5

MySQL数据库---笔记5 一、锁1.1、介绍1.2、全局锁1.2.1、全局锁介绍1.2.2、一致性数据备份 1.3、表级锁1.3.1、表锁1.3.2、元数据锁&#xff08;meta data lock , MDL&#xff09;1.3.3、意向锁 1.4、行级锁1.4.1、介绍1.4.2、行锁1.4.3、间隙锁/临建锁 二、InnoDB引擎2.1、逻辑…

vue和node使用websocket实现数据推送,实时聊天

需求&#xff1a;node做后端根据websocket&#xff0c;连接数据库&#xff0c;数据库的字段改变后&#xff0c;前端不用刷新页面也能更新到数据&#xff0c;前端也可以发送消息给后端&#xff0c;后端接受后把前端消息做处理再推送给前端展示 1.初始化node&#xff0c;生成pac…

STM32杂记之单片机复位状态

参考源码 概况 复位后&#xff0c;器件从内部高速振荡器 &#xff08;HSI 8MHz&#xff09; 运行&#xff0c;FLASH 0 等待状态&#xff0c;FLASH预取缓冲区使能&#xff0c;除内部 SRAM、FLASH和 JTAG 外&#xff0c;所有外设均关闭。高速 &#xff08;AHB&#xff09; 和低…

LLaMA模型微调版本:斯坦福 Alpaca 详解

项目代码&#xff1a;https://github.com/tatsu-lab/stanford_alpaca 博客介绍&#xff1a;https://crfm.stanford.edu/2023/03/13/alpaca.html Alpaca 总览 Alpaca 是 LLaMA-7B 的微调版本&#xff0c;使用Self-instruct[2]方式借用text-davinct-003构建了52K的数据&#x…

三相一次重合闸程序逻辑原理(二)

在手动合闸至故障线路或手动分闸及保护或自动装置要求不允许重合闸&#xff08;如母线、变压器保护及低频减载动作&#xff09;等情况下&#xff0c;闭锁重合闸的输入开关量触点接通&#xff0c;H4输出“1”&#xff0c;非门Z4输出“0”&#xff0c;计数器清零&#xff08;CD0&…

健身戴哪种耳机好、适合健身运动的耳机推荐

随着越来越多的人加入运动健身的行列&#xff0c;市场上涌现出越来越多适用于跑步的运动耳机。对于喜欢运动的朋友们来说&#xff0c;一副优秀的运动耳机成为了必不可少的装备。当进行力量训练时&#xff0c;佩戴耳机可以帮助提升训练的专注度&#xff1b;而在进行有氧运动时&a…

部署 kubeadm 1.20

目录 一、环境准备二、所有节点安装docker三、所有节点安装kubeadm&#xff0c;kubelet和kubectl四、部署K8S集群 硬件准备 master&#xff08;2C/4G&#xff0c;cpu核心数要求大于2&#xff09; 192.168.154.10 docker、kubeadm、kubelet、kubectl、flannel node01&#xff08…

oracle dblink mysql查询text无法显示问题

帮客户做了一个oracle到mysql的dblink之后&#xff0c;客户反馈发现有的表查询字段不全&#xff0c;通过select * 查询&#xff0c;mysql中有个字段INTERVENTION字段没有显示&#xff0c;首先想到的就是可能不支持查询&#xff0c;检查这个字段类型为text&#xff0c;猜测可能是…

LeetCode刷题 | 139. 单词拆分

139. 单词拆分 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 示例 1&#xff1a; 输入: s "leetcode"…

直播预约|湘江公益直播大讲堂:以低代码助力中小企业数字化转型

在当今数字时代&#xff0c;中小企业面临着前所未有的机遇和挑战。在激烈的商业竞争环境中&#xff0c;如何快速、高效地实现数字化转型并提升企业的竞争力成为中小企业亟需解决的关键问题。 低代码平台的兴起&#xff0c;为中小企业的数字化转型带来了全新的解决方案。 6月29日…