JWT的简单说明与使用

news2024/11/15 23:52:31

简要

JWT是"JSON Web Token"的缩写,是一种用于在不同系统之间传输信息的开放标准。它通过将信息进行加密后生成一个安全的令牌,以便在网络请求中进行身份验证和授权。

具体来说,JWT可以用于以下几个方面:

  1. 身份验证:当用户登录系统时,服务器会生成一个JWT并将其发送给客户端。客户端在后续的请求中携带JWT作为身份凭证,服务器可以根据JWT验证用户的身份信息,从而实现无状态的身份验证。

  2. 授权:JWT中可以包含一些声明信息,如用户角色、权限等。服务器可以根据这些信息决定用户是否具有访问某些资源或执行某些操作的权限。

  3. 单点登录:JWT可以跨多个系统进行认证和授权,用户只需要登录一次,即可在不同系统中使用同一个JWT进行访问控制,简化了用户的登录流程。

  4. 信息交换:JWT中的信息可以被加密和签名,确保信息在传输过程中不被篡改。各个系统可以通过验证JWT的签名来确认信息的完整性和真实性。

总之,JWT提供了一种安全、可靠且灵活的方式,用于在不同系统之间进行身份验证和授权,使得系统的集成和数据的传输更加便捷和可信。

jwt 组成结构

JWT由三部分组成,它们分别是头部(Header)、载荷(Payload)和签名(Signature)。

  1. 头部(Header):头部通常由两部分信息组成,它们是令牌的类型(即"JWT")和所使用的加密算法。这些信息以JSON格式表示,并进行Base64编码后放置在JWT的第一部分。

  2. 载荷(Payload):载荷是JWT的第二部分,它包含了一些声明性质的信息。这些信息可以是标准的声明(例如发行人、过期时间、主题等),也可以是自定义的声明(根据具体应用的需要)。载荷也是以JSON格式表示,并进行Base64编码后放置在JWT的第二部分。

  3. 签名(Signature):签名是JWT的第三部分,它由头部、载荷和一个私钥(或者称为密钥)进行加密生成。签名的目的是验证JWT是否被篡改过。接收JWT的服务端会使用相同的私钥解密签名,并与接收到的头部和载荷计算出来的签名进行比对,从而确认JWT的完整性和真实性。

JWT的结构示例如下:

xxxxx.yyyyy.zzzzz

其中,xxxxx代表经过Base64编码的头部信息,yyyyy代表经过Base64编码的载荷信息,zzzzz代表签名。

通过这种组成结构,JWT实现了在不同系统间传输认证和授权信息的安全性和可靠性。

所以令牌组成为三部分,就是头部header,载荷 payload,签名 signature
eg:header

{
“alg”: “HS256”, //签名算法
“typ”: “JWT” //jwt令牌
}

payload

{
“sub”: “1234567890”, //这里载荷可以自定义一些不敏感的信息
“name”: “John Doe”,
“admin”: true
}

signature

HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret);
用头部指定的签名算法对经过base64编码的头部和base64编码的载荷以及提供的一个秘钥进行加密组成签名。

JWT token 生成与解析

@SpringBootTest
class AuthJwtApplicationTests {

    //生成token
	@Test
	 String  buildToken() {
		Calendar instance = Calendar.getInstance();
		instance.add(Calendar.SECOND,90);
		String token = JWT.create()
				.withClaim("username","lll")
				.withClaim("id",1) //设置负载
				.withExpiresAt(instance.getTime()) //设置过期时间
				.sign(Algorithm.HMAC256("jgdabc"));//指定签名算法和签名所用秘钥串
		System.out.println(token);
		return  token;
	}
//	token验证
	@Test
	void VerifyToken()
	{
		JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("jgdabc")).build();
		DecodedJWT decodedJWT = jwtVerifier.verify(buildToken());
		String username = decodedJWT.getClaim("username").asString();//获取负载
		System.out.println(username);

	}



}

测试运行第二个人测试verifyToken。
在这里插入图片描述
第二个测试代码调用了第一个测试的代码方法。
我们可以看到生成的token串一共是三部分,前两部分是头部和负载,都是采用base64编码1,这两部分可以经过base64很轻松的解码获得信息,后一部分是签名,这一部分内容是十分关键的,我们指定的秘钥是十分关键的,秘钥一定不能让别人知道。
这是jwt简单的token生成和token解析的过程。

封装工具类方法

考虑前后端分离的情况下,我们会使用到它,所以我们将jwt的一些使用封装,然后进行调用

public class JWTUtils {
    private static String masterKey = "jgdabc";//指定秘钥
    /**
     * 生成token
     * @param map  //传入payload
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,7);
        builder.withExpiresAt(instance.getTime());
        return builder.sign(Algorithm.HMAC256(masterKey)).toString();
    }
    /**
     * 验证token
     * @param token
     * @return
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(master_key)).build().verify(token);
    }
   
}

一个简单的案例

该案例只是作为本次jwt的一个简单使用的说明,不会结合安全框架。
建立数据库的用户表,然后存放用户登录信息。

create database auth_jwt;
use auth_jwt;
drop table if exists user;
create table user(

 id int(11) primary key  auto_increment comment "主键" ,
 name varchar(80) default null  comment "姓名",
 password  varchar(40) default null comment "密码"
 



)

然后在idea里面搭建起来框架,可以使用mybatis或者plus也可以。我使用的是mybatis。
实体类

package com.jgdabc.entity;

import lombok.Data;
import lombok.experimental.Accessors;


/**
 * @author 兰舟千帆
 * @version 1.0
 * @date 2023/7/8 9:04
 * @Description 功能描述:实体类
 */
@Data

public class User {
    private String id;
    private String name;
    private String password;
}

dao层,也就是mapper接口

package com.jgdabc.dao;


import com.jgdabc.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author 兰舟千帆
 * @version 1.0
 * @date 2023/7/8 9:19
 * @Description 功能描述:
 */
@Mapper
public interface UserDao {
    User login(User user);
}

mapper xml

<mapper namespace="com.jgdabc.dao.UserDao">
    <select id="login" parameterType="User" resultType="User">
        select * from user where name = #{name} and password = #{password}
    </select>
</mapper>

在springboot的配置文件中你需要指定一些扫描,其他的就是数据源哪些和端口

mybatis:
  type-aliases-package: com.jgdabc.entity 
  mapper-locations: classpath:/mapper/*xml

我们需要写点业务逻辑
service层

package com.jgdabc.service;

import com.jgdabc.entity.User;

/**
 * @author 兰舟千帆
 * @version 1.0
 * @date 2023/7/8 9:59
 * @Description 功能描述:
 */
public interface UserService {
    User login(User user);
}

impl

package com.jgdabc.impl;

import com.jgdabc.Utils.JwtUtil;
import com.jgdabc.dao.UserDao;
import com.jgdabc.entity.User;
import com.jgdabc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 兰舟千帆
 * @version 1.0
 * @date 2023/7/8 10:06
 * @Description 功能描述:
 */
@Service

public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Override
    public User login(User user) {
        User user_login = userDao.login(user);
        if (user_login!=null)
        {
            return user_login;
        }
        throw  new RuntimeException("登录失败");

    }

}

前后端分离的情况下,其实是这样一种逻辑。我们可以去设置一个登录接口,然后用户登录成功之后后端将返回一个令牌,也就是token,那么用户再访问其他的接口的时候将携带这个token去访问,后端对token进行校验成功后授权访问。这是最简单的一种逻辑。当然后面结合springsecurity会变得复杂些。
controller
首先登录接口


    @PostMapping ("/user/login/")
    public Map<String, Object> login( @RequestBody User user) {
        log.info("获取请求");

        HashMap<String, String> map = new HashMap<>();
        HashMap<String, Object> result = new HashMap<>();
        try {

            User userDB = userService.login(user);
            map.put("id", userDB.getId());
            map.put("username", userDB.getName());
            String token = JwtUtil.getToken(map);

            result.put("state",200);
            result.put("message","登录成功");
            result.put("token",token);
        } catch (Exception e) {
            result.put("state",false);
            result.put("msg",e.getMessage());
        }
        return  result;


    }

以上只是简单的写,可以自己做出适当的封装。登录这里可以看到,我们登录成功后是会返回一段token的。然后我们再做一个接口需要去经过token校验才可以访问。

我们可以做一个简单的拦截器

public class JwtHander implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        Map<String,Object> map = new HashMap<>();
        try {
            JwtUtil.verify(token);
            return true;
        } catch (TokenExpiredException e) {
            map.put("state", false);
            map.put("msg", "Token已经过期!!!");
        } catch (SignatureVerificationException e){
            map.put("state", false);
            map.put("msg", "签名错误!!!");
        } catch (AlgorithmMismatchException e){
            map.put("state", false);
            map.put("msg", "加密算法不匹配!!!");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("state", false);
            map.put("msg", "无效token~~");
        }
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}

然后配置并让它成为容器组件

/**
 * @author 兰舟千帆
 * @version 1.0
 * @date 2023/7/8 12:58
 * @Description 功能描述:
 */
@Component
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       registry.addInterceptor(new JwtHander())
               .excludePathPatterns("/user/**").addPathPatterns("/");


    }
}

这样我们就可以展开测试了
postman 测试
在这里插入图片描述
这样就返回一点token,然后呢我们可以定义别的接口,比如这样一个

  @GetMapping("/users/test")
    public String test_demo()
    {
        return "接口token校验成功";
    }




    }

接口的token要放到header这里,将来实际的前端给后端token的时候也可以这样去做。
在这里插入图片描述
案例代码地址auth_jwt

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

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

相关文章

CMakeLists.txt 语法介绍

CMake编译原理 CMake是一种跨平台编译工具,主要编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件&#xff0c;最后用make命令编译源码生成可执行程序或共享库.因此CMake的编译基本就两个步骤&#xff1a;cmake && make cm…

MySQL基础练习

Ⅰ Ⅱ 3.1 3.2 3.3 3.4 -- 单表查询练习 /* 素材 CREATE TABLE emp ( empno int(4) NOT NULL, ename varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, job varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, …

细数那些Compose新手容易犯的错误

作者&#xff1a;晴天小庭 笔者作为一个日常Jetpack Compose开发者&#xff0c;对Compose的理解也在逐渐加深中&#xff0c;最近回顾当初学习和实践的过程中&#xff0c;犯了不少错误和踩了很多坑&#xff0c;本篇文章作为小总结分享给大家&#xff0c;同时文章会持续更新&…

Leetcode-每日一题【1669.合并两个链表】

题目 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。 请你将 list1 中下标从 a 到 b 的全部节点都删除&#xff0c;并将list2 接在被删除节点的位置。 下图中蓝色边和节点展示了操作后的结果&#xff1a; 请你返回结果链表的头指针。 示例 1…

Unity游戏C# dll注入

案例游戏下载 首先在网上下载个游戏案例&#xff0c;我就以Captain Molly游戏为例。 这个游戏玩家默认生命值有5点&#xff0c;咱们通过dll注入修改为10点。 dnSpy 我使用dnSpy来查看Unity游戏生成的dll代码&#xff0c;当然你们也可以使用其他工具。 Unity游戏脚本代码最终…

Codeforces-Round-883-Div-3

Codeforces Round 883 (Div. 3) 链接&#xff1a;https://codeforces.com/contest/1846 A. Rudolph and Cut the Rope There are n n n nails driven into the wall, the i i i-th nail is driven a i a_i ai​ meters above the ground, one end of the b i b_i bi​ m…

Pinia: vue的最新状态管理库

Pinia: vue的最新状态管理库&#xff0c;vuex状态管理工具的替代品。 pinia官方文档 注意defineStore()的返回值还是一个方法&#xff0c;所以useCounterStore是一个方法&#xff0c;执行该方法得到一个对象。 getters: 异步action: storeToRefs: 补充 vuex&#xff…

20中文字符识别(matlab程序)

1.简述 随着计算机科学的飞速发展&#xff0c;以图像为主的多媒体信息迅速成为重要的信息传递媒介&#xff0c;在图像中&#xff0c;文字信息(如新闻标题等字幕) 包含了丰富的高层语义信息&#xff0c;提取出这些文字&#xff0c;对于图像高层语义的理解、索引和检索非常有帮助…

Zero-Shot Node Classification

零样本节点分类(Zero-shot node classification) 谱图卷积 图卷积网络 GCN的分解

pandas中比较两个对象相等性 .eq()函数

在使用pandas做数据分析时&#xff0c;往往我们会有这样的数据需求&#xff1a;为某有某一属性的客户打标签。此刻&#xff0c;.eq()函数&#xff0c;就可以实现它自身的价值。 Lets go&#xff0c;一起去探索它的神秘力量吧&#xff01; 先讲讲它的用途&#xff1a;可以用于…

银河麒麟高级服务器系统部署-尚文网络xUP楠哥

进Q群11372462领取专属报名福利! # 什么是银河麒麟 银河麒麟高级服务器操作系统是针对企业级关键业务&#xff0c;适应虚拟化、云计算、大数据、工业互联网时代对主机系统可靠性、安全性、性能、扩展性和实时性等需求&#xff0c;依据CMMI5级标准研制的提供内生本质安全、云原…

Pandas理论与实战(一)

目录 一、Series对象 1.1 认识Series对象 1.2 Series对象的索引 1.3 获取Series的索引和值 二、DataFrame对象 2.1 认识DataFrame对象 2.2 DataFrame重要属性和函数 ​三、导入外部数据 3.1 导入.xls或.xlsx文件 3.2 导入csv文件 3.3 导入.txt文本文件 3.4 导入HTML网页…

Sentinel组件限流降级

官网: home | Sentinel 文档不是很全, 关于nacos的配置中心的使用完全没有 常见的限流算法 静态窗口限流: 即规定1秒内只能固定处理多少请求动态窗口限流: 同样是规定1秒内处理多少请求, 但是统计方式与第一个不同, 比如2.5秒则是统计1.5秒到现在的请求数漏桶限流: 进来可以…

【全栈第三课】通过ChatGPT快速入门NodeJS

前言 往期全栈课程&#xff1a; Vue从入门到精通 微信小程序从入门到精通 Node.js基础 简介 Node.js是什么&#xff1f; Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O的模型&#xff0c;使其轻量又高效。Node.js …

迎接高考的倒计时网页(❤️好看好用❤️)HTML+CSS+JS

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

并发编程_jmm部分

1. JMM 理解 前提&#xff1a;并发编程有3大问题&#xff0c;可见性、有序性、原子性。 导致可见性的原因是缓存&#xff0c;有序性的原因是 编译器优化。解决方法就是直接禁用缓存和编译器优化&#xff0c;导致程序性能堪忧。 因此合理的方案就是按需禁用缓存和编译器优化。 …

MySQL数据库——单表查询练习

一、练习素材 创建表 CREATE TABLE emp (empno int(4) NOT NULL,ename varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,job varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,mgr int(4) NULL DEFAULT NULL,hireda…

遗传算法核心理解,python代码

遗传算法的核心&#xff0c;就在于&#xff0c;把待求的变量转化成二进制串&#xff0c;二进制串就像dna&#xff0c;可以对它的其中某几位进行交换&#xff0c;变异等操作&#xff0c;然后再转换回十进制&#xff0c;带入目标函数&#xff0c;计算适应度&#xff0c;保留适应度…

【lambda函数】lambda()函数

lambda&#xff08;&#xff09; lambda&#xff08;&#xff09;语法捕捉列表mutable lambda 底层原理函数对象与lambda表达式 lambda&#xff08;&#xff09;语法 lambda表达式书写格式&#xff1a; [capture-list] (parameters) mutable -> return-type{ statement }咱…

【数据结构】排序:插入排序与希尔排序详解

本章开始就要分享一些常用的排序方法&#xff0c;我们的日常生活中很多地方都要使用排序&#xff0c;比如电商平台可以按照你的需求进行排序&#xff0c;或者是你想了解大学的综合排名时 我们之前也学到过一些简单的排序比如冒泡排序&#xff0c;虽然他在时间复杂度上可以说是依…