04 SS之JWT和base64

news2025/1/24 7:13:56

1.1 jwt和token

1.1.1 token介绍

令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。普通的令牌可能以各种形式出现,如访问令牌、身份令牌、刷新令牌等。

简单理解 : 每个用户生成的唯一字符串标识 , 可以进行用户识别和校验

优势: token验证标识无法直接识别用户的信息,盗取token后也无法`登录`程序! 相对安全!

1.1.2 jwt介绍

  • Token是一项规范和标准(接口)
  • JWT(JSON Web Token)是具体可以生成,校验,解析等动作Token的技术(实现类)
  • JWT只通过算法实现对Token合法性的验证,不依赖数据库存储系统,因此可以做到跨服务器验证,只要密钥和算法相同,不同服务器程序生成的Token可以互相验证。
  • JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。
  • JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数
  • JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑(JWT的登出问题)。

思考 : 正常情况下 修改了密码后就会跳转到登录页面 :修改成功后清空浏览器保存的token了
后端怎么玩? 因为服务端不保留token 我用之前的token 还是可以继续访问的

JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

为了减少盗用,JWT 不应该使用 HTTP 80 协议明码传输,要使用 HTTPS 443 协议传输。

1.1.3 JWT解决服务器集群/跨域认证问题

互联网服务离不开用户认证。一般流程是下面这样。

  1. 用户向服务器发送用户名和密码。
  2. 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
  3. 服务器向用户返回一个 jsession_id,写入用户的 Cookie。
  4. 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
  5. 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

这种模式的问题在于,扩展性(scaling)不好。
单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,要求每台服务器都能够读取 session。

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。

另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。 服务器不存数据,客户端存,服务器解析就行了

1.1.4 jwt工作流程

  • 用户提供其凭据(通常是用户名和密码)进行身份验证。
  • 服务器对这些凭据进行验证,并在验证成功后创建一个JWT。
  • 服务器将JWT发送给客户端,并客户端在后续的请求中将JWT附加在请求头或参数中。
  • 服务器接收到请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作
    - 在这里插入图片描述

1.1.5 jwt数据组成和包含信息

一个JWT由三部分组成,各部分以点分隔:

Header(头部)-----base64Url编码的Json字符串
Payload(载荷)—base64url编码的Json字符串
Signature(签名)—使用指定算法,通过Header和Playload加盐计算的字符串

一个JWT看起来像下面这样:
xxxxx.yyyyy.zzzzz

下面这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

jwt可以携带很多信息 一般情况,需要加入:有效时间,签名秘钥,其他用户标识信息!

有效时间的作用 : 保证token的时效性,过期可以重新登录获取!

签名秘钥的作用 : 防止其他人随意解析和校验token数据!

用户信息的作用 : 系统解析的时候,分辨Token对应的具体用户!

1.1.5.1 Header

此部分有两部分组成:

  • 一部分是token的类型,目前只能是JWT
  • 另一部分是签名算法,比如HMAC 、 SHA256 、 RSA

示例:
{
“alg”:“HS256”,
“typ”:“JWT”
}

base64编码命令:
echo -n ‘{“alg”:“HS256”,“typ”:“JWT”}’ | base64

1.1.5.2 Payload

token的第二部分是payload(有效负载),其中包含claims(声明)。Claims是关于一个实体(通常是用户)和其他数据类型的声明。

1.1.5.3 Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)

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

示例:
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)

1.1.6 jwt使用和测试

1.1.6.1 jjwt包实现JWT
  1. 导入依赖
<!--jjwt只是实现JSON的一个包,还有其他的例如Java-jwt-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
  1. 编写配置application.yaml
#jwt配置
jwt:
  token:
    tokenExpiration: 120 #有效时间,单位分钟
    tokenSignKey: headline123456  #当前程序签名秘钥 自定义
  1. 封装jwt技术工具类
    分析 : 从逻辑上讲,该工具类必须有如下功能
    a. 根据传入的用户id生成token并返回 ,以便用户免密登录
    b. 根据传入的token判断用户id , 以便知道登录的是哪个用户
    c. 判断传入的token是否还在有效期
package com.sunsplanter.utils;

import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Date;

@Data
@Component
//读取配置文件中所有前缀为jwt.token的属性,只要最后后缀一样就自动注入
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {

    private  long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒
    private  String tokenSignKey;  //当前程序签名秘钥

    //生成token字符串
    public  String createToken(Long userId) {
        System.out.println("tokenExpiration = " + tokenExpiration);
        System.out.println("tokenSignKey = " + tokenSignKey);
        String token = Jwts.builder()

                .setSubject("YYGH-USER")
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟
                .claim("userId", userId)
                .signWith(SignatureAlgorithm.HS512, tokenSignKey)
                .compressWith(CompressionCodecs.GZIP)
                .compact();
        return token;
    }

    //从token字符串获取userid
    public  Long getUserId(String token) {
        if(StringUtils.isEmpty(token)) return null;
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        Integer userId = (Integer)claims.get("userId");
        return userId.longValue();
    }

    //判断token是否有效
    public  boolean isExpiration(String token){
        try {
            boolean isExpire = Jwts.parser()
                    .setSigningKey(tokenSignKey)
                    .parseClaimsJws(token)
                    .getBody()
                    .getExpiration().before(new Date());
            //没有过期,有效,返回false
            return isExpire;
        }catch(Exception e) {
            //过期出现异常,返回true
            return true;
        }
    }
}
4. 使用和测试
package com.sunsplanter.test;

import com.sunsplanter.utils.JwtHelper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class SBTest {

    @Autowired
    private JwtHelper jwtHelper;

    @Test
    public void test(){
        //生成 传入用户标识
        String token = jwtHelper.createToken(1L);
        System.out.println("token = " + token);

        //解析用户标识
        int userId = jwtHelper.getUserId(token).intValue();
        System.out.println("userId = " + userId);

        //校验是否到期! false 未到期 true到期
        boolean expiration = jwtHelper.isExpiration(token);
        System.out.println("expiration = " + expiration);
    }

}
1.1.6.2 java-jwt包实现JWT
  1. 导入依赖
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.11.0</version>
</dependency>
  1. 封装jwt技术工具类
package com.sunsplanter.utils;

/**
 * 本类用于生成和解析JWT
 */
@Configuration
public class JWTUtil {

    /**
     * 声明一个秘钥
     */
    private static final String SECRET = "woshiyigemiyao ";


    /**
     * 生成JWT
     *
     * @param userId   用户编号
     * @param username 用户名
     * @param auth     用户权限
     */
    public String createToken(Integer userId, String username, List<String> auth) {

        //得到当前的系统时间
        Date currentDate = new Date();
        //根据当前时间计算出过期时间 定死为5分钟
        Date expTime = new Date(currentDate.getTime() + (1000 * 60 * 5));

        //头数据就是算法alg和类型typ,其中type必须是JWT(用JWT生成token)
        Map<String, Object> header = new HashMap<>();
        header.put("alg", "HS256");
        header.put("typ", "JWT");

        return JWT.create()
                .withHeader(header) //头
                .withClaim("userId", userId) //自定义数据
                .withClaim("username", username) //自定义数据
                .withClaim("auth", auth) //自定义数据
                .withIssuedAt(currentDate) //创建时间
                .withExpiresAt(expTime)//过期时间
                .sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 验证JWT并解析
     *
     * @param token 要验证的jwt的字符串
     */
    public static Boolean verifyToken(String token) {
        try{
            // 使用秘钥创建一个解析对象
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
            //验证JWT
            DecodedJWT decodedJWT = jwtVerifier.verify(token);

            System.out.println(decodedJWT);

            String payload = decodedJWT.getPayload();
            System.out.println("payload = " + payload);

            return true;
        }catch (TokenExpiredException e){
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 从JWT里的获取用户编号
     */
    public Integer getUserId(String token){
        try{
            // 使用秘钥创建一个解析对象
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
            //验证JWT
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            Claim userId = decodedJWT.getClaim("userId");
            return userId.asInt();
        }catch (TokenExpiredException e){
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 从JWT里的获取用户名
     */
    public static String getUsername(String token){
        try{
            // 使用秘钥创建一个解析对象
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
            //验证JWT
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            Claim username = decodedJWT.getClaim("username");
            return username.asString();
        }catch (TokenExpiredException e){
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 从JWT里的获取用户权限
     */
    public List<String> getAuth(String token){
        try{
            // 使用秘钥创建一个解析对象
            JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
            //验证JWT
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            Claim auth = decodedJWT.getClaim("auth");
            return auth.asList(String.class);
        }catch (TokenExpiredException e){
            e.printStackTrace();
        }
        return null;
    }

}

单元测试

    @Autowired
    private JWTUtil jwtUtil;

    @Test
    void testjwt(){

        List<String> authList = Arrays.asList("student:query", "student:add");
        String obamaToken = jwtUtil.createToken(19, "obama", authList);

        jwtUtil.verifyToken(obamaToken);
    }

2. base64

2.1 什么是Base64

  • 所谓Base64,就是说选出64个字符:小写字母a-z、大写字母A-Z、数字0-9、符号"+“、”/“(再加上可能的”=",实际上是使用65个字符),作为一个基本字符集。然后,其他所有符号都转换成这个字符集中的字符。
  • base64是一个编码方案. 并非加密方案
  • base64的编码方案是:每遇到三个字节就按照特定规则编码为4个字节, 因此最后文件体积一定是变大了
  • 既然每三个字节才编码为一次为4个字节, 那么最后可能会剩下1/2个字节没法编码, 此时用1/2个"=表示"

2.2 linux base64命令

2.2.1 Linux下用base64命令编解码字符串

#编码:
#echo 命令自带换行符, 相当于用Hello World加上一个换行符进行base64编码
echo 'Hello World' | base64
SGVsbG8gV29ybGQK
#-n参数指定去掉换行符,这样才是对Hello World本身进行编码
echo -n 'Hello World' | base64
SGVsbG8gV29ybGQ=

#解码:同理也要去掉换行符
echo -n 'SGVsbG8gV29ybGQ=' | base64 -d
Hello World

备注:

echo -n ‘{“alg”:“HS256”,“typ”:“JWT”}’ | base64

2.2.1 Linux下用base64命令编解码文件

#base64编码
# base64 待编码的文件名 > 编码后的文件名
base64  1.mp3 > mymp3

#base64 -d 待解码的文件名 >解码后的文件名
base64 -d mymp3>88.mp3

2.3 在网络中使用baseUrl

为了能在http传输中也使用base64的基本编码规则, 对其进行一定改造:
1、明文使用BASE64进行编码
2、在Base64编码的基础上进行以下的处理:
1)去除尾部的"="
2)把"+“替换成”-"
3)斜线"/“替换成下划线”_"

这就是baseUrl

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

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

相关文章

【动态规划】【C++算法】2742. 给墙壁刷油漆

作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 本文涉及知识点 动态规划汇总 LeetCode2742. 给墙壁刷油漆 给你两个长度为 n 下标从 0 开始的整数数组 cost 和 time &#xff0c;分别表示给 n 堵不同的墙刷油漆需要的开销和时间。你有…

多行重定向,expect,字符串处理,高级变量,脚本工具

一&#xff0c;多行重定向 &#xff08;一&#xff09;含义 使用I/O重定向的方式将命令列表提供给交互式程序 标准输入的一种替代品 Here Document 是标准输 入的一种替代品&#xff0c;可以帮助脚本开发人员不必使用临时文件来构建输入信息&#xff0c;而是直接就地 生产出…

Unity类银河恶魔城学习记录7-6 P72 Bouncy sword源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili Sword_Skill_Controller.cs using System.Collections; using System.Colle…

Github 2024-02-18 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-18统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目5PowerShell项目1Rust项目1PHP项目1Jupyter Notebook项目1TypeScript项目1 Black&#xff1a;不妥…

力扣51. N 皇后(回溯)

Problem: 51. N 皇后 文章目录 题目描述思路解决方法复杂度Code 题目描述 思路 1.决策路径&#xff1a;board中小于row的那些行都已经成功放置了皇后&#xff1b; 2.选择列表&#xff1a;第row行的所有列都是放置皇后的选择&#xff08;则根据N皇后相互攻击的股则编写判断当前决…

JVM常见问题笔记分享

文章目录 1 JVM组成1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f;1.2 什么是程序计数器&#xff1f;1.3 你能给我详细的介绍Java堆吗?元空间(MetaSpace)介绍 1.4 什么是虚拟机栈1.5 堆和栈的区别1.6 能不能解释一下方法区&#xff1f;1.5.1 概述1.5.2 常量池1…

鸿蒙应用模型开发-更新SDK后报错解决

更新SDK后提示 “ohos.application.Ability”/“ohos.application.AbilityStage”等模块找不到或者无相关类型声明 问题现象 更新SDK后报错“Cannot find module ‘ohos.application.AbilityStage’ or its corresponding type declarations”&#xff0c;“Cannot find modu…

【springboot+vue项目(十五)】基于Oauth2的SSO单点登录(二)vue-element-admin框架改造整合Oauth2.0

Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架&#xff0c;提供了丰富的组件和功能&#xff0c;可以帮助开发者快速搭建现代化的后台管理系统。 一、基本知识 &#xff08;一&#xff09;Vue-element-admin 的主要文件和目录 vue-element-admin/ |…

裁员不可怕,可怕的是软件测试行业在发生巨变,而你却原地踏步

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Android Studio 的 Gradle Task 没有显示

问题 Android Studio 的 Gradle Task 没有显示 详细问题 笔者需要获取android应用MD5&#xff0c;一种方法是 但是笔者的Android Studio 的 Gradle Task 没有显示 解决方案 依次点击&#xff1a;File -> Settings -> Experimental -> 取消勾选 “Do not build …

轻松采集商品 全称免滑块 精准实时数据采集|全平台电商API接口

随着电子商务的快速发展&#xff0c;越来越多的商家和消费者选择在淘宝上进行交易。 淘宝商品详情API接口轻松采集淘宝上的商品信息&#xff0c;全程免滑块&#xff0c;实现精准实时数据采集。 一、采集商品信息 商品详情API接口采用先进的爬虫技术&#xff0c;能够快速准确…

Python学习路线图

防止忘记&#xff0c;温故知新 进阶路线

医学超声模式简介

B模式 B是英文单词“亮度”&#xff08;Brightness&#xff09;的第一个字母&#xff0c;因为被观察结构的回声或亮度取决于反射信号的强度&#xff0c;它用平面图形的形式来显示被探查组织的具体情况。检查时&#xff0c;首先将人体界面的反射信号转变为强弱不同的光点&#x…

Python实现时间序列分析使用LOESS(STL)模型进行季节性趋势分解(STL算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 时间序列分析中&#xff0c;LOESS&#xff08;局部加权回归平滑&#xff09;和STL&#xff08;Seasona…

Python第十七章(面向对象总结)

一。面向对象三大特征 1.封装&#xff1a;将属性和方法写到类里面&#xff0c;且可以添加私有属性和方法 2.继承&#xff1a;子类默认继承父类的所有属性和方法&#xff0c;子类可以重写父类的属性和方法 3.多态&#xff1a;传入不同的对象&#xff0c;产生不同的结果 二。多…

用连续自然数之和来表达整数

文章目录 题目描述&#xff1a;用连续自然数之和来表达整数输入描述输出描述用例1说明用例2代码运行举例注意&#xff1a;1、sprintf 和 strcat区别2、qsort 题目描述&#xff1a;用连续自然数之和来表达整数 一个整数可以由连续的自然数之和来表示。 给定一个整数&#xff0c…

鸿蒙语言ArkTS(更好的生产力与性能)

ArkTS是鸿蒙生态的应用开发语言 ArkTS提供了声明式UI范式、状态管理支持等相应的能力&#xff0c;让开发者可以以更简洁、更自然的方式开发应用。 同时&#xff0c;它在保持TypeScript&#xff08;简称TS&#xff09;基本语法风格的基础上&#xff0c;进一步通过规范强化静态检…

自动驾驶中的 DCU、MCU、MPU、SOC 和汽车电子架构

自动驾驶中的 DCU、MCU、MPU 1. 分布式电子电气架构2. 域集中电子电气架构架构2.1 通用硬件定义 3. 车辆集中电子电气架构4. ADAS/AD系统方案演变进程梳理4.1 L0-L2级别的ADAS方案4.2 L2以上级别的ADAS方案 5. MCU和MPU区别5.1 MCU和MPU的区别5.2 CPU与SoC的区别5.3 举个例子 R…

陶建国教授谈中西方文化的差异与交融

龙年到来&#xff0c;这个春节里&#xff0c;“龙”字的英文翻译引发关注&#xff0c;冲上了热搜&#xff0c;网友发现&#xff0c;“龙”不再翻译为“dragon”&#xff0c;而是龙字的谐音“loong”。原来&#xff0c;在西方人的眼里&#xff0c;龙是凶猛的怪兽&#xff0c;具有…

Spring 事务原理总结六

不知不觉&#xff0c;关于Spring事务的文章已经写了五篇了。老实讲我自己不断质疑过自己&#xff1a;现在写这些文章还有意义吗&#xff1f;当前的市场已经成什么样了&#xff0c;为什么还要固守这落后的技术&#xff1f;但是贝索斯一次接受访谈的回答&#xff0c;让我写下去的…