Java——令牌技术

目录

一、何为令牌

JWT令牌

介绍

JWT组成

二、JWT用于验证用户登录

三、JWT令牌生成和校验

简单用法

1.创建生成密钥的方法

2.接着添加过期时间,密钥,BASE64解码密钥的属性以及生成token的方法,合并上面生成密钥的方法,下面是完整代码。

3.当然也可以写解析token的方法进行验证,通过对称密钥KEY进行对token进行验证,得到载荷部分:

进阶用法

1.创建密钥库文件(JKS)

2.读取JKS密钥库并进行签名,生成token

3.验证Token


一、何为令牌

令牌其实就是一个用户身份的标识,其本质上就是一个字符串。

比如我们出行在外,会带着自己的身份证,需要验证身份时,就要身份证了,而身份证不能伪造,可以辨别真假。

服务器具备生成令牌和验证令牌的能力。

JWT令牌

令牌本质就是一个字符串,它的实现方式有很多种,我们采用一个JWT令牌来实现。

介绍

JWT全称:JSON Web Toekn

官网:++https://jwt.io/++

JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),用于客户端和服务器之间传递安全可靠的信息。

其本质是一个token,是一种紧凑的URL安全方法。

JWT组成

JWT由三部分组成,使用(.)分隔,比如:aaaaa.bbbbb.cccc,这三部分为:

  • Header(头部):头部包括令牌类型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)
  • Payload(负载):负载部分是存放有效信息的地方,里面是一些自定义的内容。比如:{"userId":"123","userName":"zhangsan"},也可以存在jwt提供的现场字段,比如exp(过期时间戳)等 。注意:此部分不建议存放敏感信息,因为此部分能够被解码还原原始内容
  • Signature(签名):此部分用于防止jwt内容被篡改,确保安全性。

防止被篡改,而不是防止被解析。

jwt之所以安全,就是因为最后的签名。jwt当中任何一个字符被篡改,整个令牌都会校验失效。这就好比我们的身份证,之所以能标识一个人的身份,是因为不能被篡改,而不是因为内容加密。


二、JWT用于验证用户登录

具体流程如下

++1.用户登录(生成JWT)++

(1)用户提交凭证

* 场景:用户输入用户名和密码,点击登录。

(2)认证服务器验证凭证

验证流程:

* 检查用户名和密码是否匹配数据库中的记录

* 确认用户状态(是否被禁用)

(3)生成JWT

JWT结构:

* Header:声明算法类型(如"alg": "RS256", "typ": "JWT"}).

* Payload:包含用户身份和权限信息({"sub": "user123", "roles": ["user"], "exp": 1720000000})。

* Signature:使用私钥对头部和载荷签名 (如 RSA-SHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), privateKey))。

++2.客户端存储并携带JWT++

客户端把令牌存储起来,可以存储在Cookie中,也可以存储在其他的存储空间。

++3.服务器验证JWT++

(1)解析JWT结构

(2)验证签名

非对称加密流程:从认证服务器获取公钥,使用公钥重新计算签名,比对JWT中的签名是否一致。同时验证令牌是否有效,有效就说明执行了登录操作,否则就是未登录。

总结:

1.用户登录请求,经过负载均衡,把请求转给了第一台服务器,第一台服务器进行账号密码验证,验证成功后,生成一个令牌,并返回给客户端。

2.客户端收到令牌后,把令牌存储起来,可以存储在Cookie中,也可以存储在其他的存储空间。

3.用户登录成功后,携带令牌继续执行查询操作,比如查询博客列表,此时请求转发到了第二台机器 ,第二台机器会先进行权限验证操作。服务器验证令牌是否有效,就说明执行了登录操作,如果令牌是无效的,就说明用户之前未执行登录操作。


三、JWT令牌生成和校验

在使用之前先引入相关的依赖:

放在<dependencies> </dependencies>标签下:

XML 复制代码
       <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is
preferred -->
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>

简单用法

主要思想是利用生成的对称密钥对token进行签名(加密)和验证(解密)。

++下面代码主要利用测试类进行讲解:++

1.创建生成密钥的方法

java 复制代码
    @Test
    public void genKey(){
        //随机生成一个key,HS256算法生成 。H256是一种堆成密钥算法
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        //利用BASE64对密钥进行编码
        String key = Encoders.BASE64.encode(secretKey.getEncoded());
        System.out.println(key);
    }

运行后生成结果如下:

java 复制代码
B2S6Xk0/9aDxArJCaVWEAa2moixi3RVx+O8oraRQ3cQ=

2.接着添加过期时间,密钥,BASE64解码密钥的属性以及生成token的方法,合并上面生成密钥的方法,下面是完整代码。

java 复制代码
@SpringBootTest
public class JWTUtilTest {

    //过期毫秒时长10年
    public static final long Expiration =10 * 365 * 24 * 60 * 60 * 1000L;;

    //密钥,就是利用下面genKey()方法生成的密钥
    private static final String secretSrting = 
"B2S6Xk0/9aDxArJCaVWEAa2moixi3RVx+O8oraRQ3cQ=";            

    //生成安全密钥,BASE64解码
    private static final SecretKey KEY =
            Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretSrting));

    //生成token
    @Test
    public void genToken(){
        //生成载荷部分
        Map<String,Object> claim = new HashMap<>();
        claim.put("id",1);
        claim.put("username","zhangsan");

        //自定义信息
        String jwt = Jwts.builder()
                .setClaims(claim)//自定义内容(负载)
                .setExpiration(new Date(System.currentTimeMillis()+Expiration))//设置过期时间
                .signWith(KEY) //密钥签名
                .compact();
        System.out.println(jwt);
    }


    //生成密钥
    @Test
    public void genKey(){
        //随机生成一个key,HS256算法生成
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String key = Encoders.BASE64.encode(secretKey.getEncoded());
        System.out.println(key);
    }

}

运行genToken()方法,得到token字符串。

java 复制代码
eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImV4cCI6MjA2MTAwMTY0OX0.
7eFIjaWuKEZTFnBlrVgdVtplVowslGnd_VHenj3qQpU

通过得到的token和密钥到官网去验证JSON Web Tokens - jwt.ioJSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).https://jwt.io/结果如下:

3.当然也可以写解析token的方法进行验证,通过++对称密钥KEY进行对token进行验证++,得到载荷部分:

java 复制代码
    //校验token信息
    @Test
    public void parseToken(){
        String token ="eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImV4cCI6MjA2MTAwMTY0OX0.7eFIjaWuKEZTFnBlrVgdVtplVowslGnd_VHenj3qQpU";
        //利用对称密钥再进行验证token
        JwtParser bulid = Jwts.parserBuilder().setSigningKey(KEY).build();
        Claims body = bulid.parseClaimsJws(token).getBody();
        System.out.println(body);
    }

运行结果如下:

进阶用法

处于安全考虑,我们可以通过RSA算法生成非对称密钥,利用私钥签名,利用公钥进行验证,同时通过还需要这对对密钥进行管理,将其管理在密钥库中。

++下面是实现步骤:++

1.创建密钥库文件(JKS)

我们需要把生成这对对 公钥-私钥 ,并将其保存在密钥库中。可以使用keytool命令创建JKS文件(在idea中的终端运行)。

命令如下:

java 复制代码
keytool -genkeypair -alias myalias -keyalg RSA -keysize 2048 -keystore mykeystore.jks -storepass keystorepass -validity 365

命令解析:

复制代码
keytool -genkeypair \
  -alias myalias \          # 密钥条目别名(自定义标识)
  -keyalg RSA \             # 密钥算法(RSA 非对称加密)
  -keysize 2048 \           # 密钥长度(2048 位,安全推荐值)
  -keystore mykeystore.jks \# 密钥库文件名(保存路径)
  -storepass keystorepass \ # 密钥库密码(保护整个文件)
  -validity 365             # 证书有效期(365 天)

此命令用于生成一个java密钥库(JKS文件),并在其中创建一个RSA密钥(非对称)对。

运行后keytool会提示输入相关信息(学习阶段随便填即可):

java 复制代码
您的名字与姓氏是什么?
  [Unknown]:  localhost       # 建议填域名(如服务器域名或本地测试填 localhost)
您的组织单位名称是什么?
  [Unknown]:  MyOrg           # 组织单位(可选)
您的组织名称是什么?
  [Unknown]:  MyCompany       # 组织名称(可选)
您所在的城市或区域名称是什么?
  [Unknown]:  Beijing         # 城市(可选)
您所在的省/市/自治区名称是什么?
  [Unknown]:  Beijing         # 省份(可选)
该单位的双字母国家/地区代码是什么?
  [Unknown]:  CN              # 国家代码(如 CN 表示中国)
CN=localhost, OU=MyOrg, O=MyCompany, L=Beijing, ST=Beijing, C=CN是否正确?
  [否]:  是                  # 确认信息

输入完后,会提示输入口令,++注意这个口令是和后面获取私钥的时候挂钩的++,我们可以直接按回车和密钥库密码一样,同时也可以自定义。

输入完后,项目里面会生成jks文件,我们可以剪贴到resource目录下,后面需要该文件路径。

2.读取JKS密钥库并进行签名,生成token

下面是详细代码:

java 复制代码
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
import java.util.Date;
import java.util.Map;


public class JWTUtils {

//    EYSTORE_PASSWORD(密钥库密码):用于保护整个 JKS 密钥库文件。在加载密钥库时需要使用此密码。


    //配置密钥库的路径、别名。访问密码
    private static final String KETSTORE_PATH = "src/main/resources/mykeystore.jks";
    //密钥库密码
    private static final String KEYSTORE_PASSWORD = "keystorepass";
    //密钥别名
    private static final String KEY_ALIAS = "myalias";
    //访问私钥的密码(刚刚自己输入的口令)
    private static final String KEY_PASSWORD = "123456";

    //从jks文件中加载私钥
    private static Key getPrivateKey() throws Exception  {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        try (FileInputStream keyStoreStream = new FileInputStream(KETSTORE_PATH)){
            keyStore.load(keyStoreStream, KEYSTORE_PASSWORD.toCharArray());
        }
        return  keyStore.getKey(KEY_ALIAS, KEY_PASSWORD.toCharArray());
    }

    //生成TOKEN
    public static String generateToken(Map<String,Object> claim)throws Exception{
        Key privateKey = getPrivateKey();
        return Jwts.builder()
                .setClaims(claim)//自定义内容(负载)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis()+60*60*1000)) //过期时间1小时
                .signWith(privateKey, SignatureAlgorithm.RS256) //使用私钥签名,相当于用私钥加密
                .compact();
    }
}

3.验证Token

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

public class JwtValidator {

    //获取公钥,公钥无需密码去访问直接获取即可
    private static PublicKey getpublicKey()throws  Exception{
        KeyStore keyStore = KeyStore.getInstance("JKS");
        try (FileInputStream keyStoreStream = new FileInputStream("src/main/resources/mykeystore.jks")) {
            keyStore.load(keyStoreStream,"keystorepass".toCharArray());
        }
        return keyStore.getCertificate("myalias").getPublicKey();
    }

    //验证JWT并解析
    public static Claims validateToken(String token)throws Exception{
        PublicKey publicKey = getpublicKey();
        return Jwts.parserBuilder()
                .setSigningKey(publicKey)  //使用公钥验证
                .build()
                .parseClaimsJws(token)//解析jwt
                .getBody();
    }

//运行main方法执行程序
    public static void main(String[] args) throws Exception {
        //写入相关参数
        Map<String,Object> claim = new HashMap<>();
        claim.put("id",1);
        claim.put("username","zhangsan");
        String token = JWTUtils.generateToken(claim);
//        System.out.println(token);
        Claims claims = validateToken(token);
        System.out.println(claims);
    }
}

运行结果:


以上是JWT令牌的讲解,喜欢的支持一下,感谢观看!!!

相关推荐
八股文领域大手子1 小时前
如何给GitHub项目提PR(踩坑记录
大数据·elasticsearch·github
泯泷2 小时前
探索DeepWiki:开发者的代码知识库利器
github·openai·ai编程
福尔摩东3 小时前
从零到精通:2025年最全大模型学习资源
人工智能·github
Gladiator5754 小时前
博客记录-day153-力扣
github
大鹏dapeng4 小时前
使用gonectr操作gone项目,包括:创建项目、安装依赖、生成代码、编译和运行
后端·go·github
梓羽玩Python6 小时前
开源AI代理爆火!Suna:3天内新增5.5K+标星,自然对话驱动的自动化神器!
人工智能·python·github
余渔鱼11238 小时前
ASP.NET CORE部署IIS的三种方式
后端·github·asp.net
清风徐来QCQ8 小时前
git和github的使用指南
git·github
喵叔哟9 小时前
16.【.NET 8 实战--孢子记账--从单体到微服务--转向微服务】--微服务基础工具与技术--Github Action
微服务·github·.net