JWT 使用

news2024/11/26 0:36:47
  • 前端访问后台
  • a系统和b系统访问

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT的本质就是一个字符串,它是将用户信息保存到一个Json字符串中,然后进行编码后得到一个JWT token并且这个JWT token带有签名信息,接收后可以校验是否被篡改,所以可以用于在各方之间安全地将信息作为Json对象传输

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT的构成

第一部分我们称它为头部(header),

第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),

第三部分是签证(signature).

header

jwt的头部承载两部分信息:

  • 声明类型,这里是jwt
  • 声明加密的算法 通常直接使用 HMAC SHA256

完整的头部就像下面这样的JSON:

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

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

载荷就是存放有效信息的地方 这些有效信息包含三个部分

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

  • header (base64后的)
  • payload (base64后的)
  • secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

将这三部分用.连接成一个完整的字符串,构成了最终的jwt:

  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRta

如何应用

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IB6lV4WS-1689563766871)(http://qiniu.xiaotao.cloud/1821058-2e28fe6c997a60c9.png)]

img

优点

  • 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
  • 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
  • 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
  • 它不需要在服务端保存会话信息, 所以它易于应用的扩展

安全相关

  • 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
  • 保护好secret私钥,该私钥非常重要。
  • 如果可以,请使用https协议

使用

java 中 使用 JWT

<!--jwt 在java 中的使用-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>

生成签名

/**
 *  生成签名
 */
@Test
public void t1(){
    Calendar instance = Calendar.getInstance();
    instance.add(Calendar.SECOND,100);   // 100秒后过期

    String token = JWT.create()
            .withClaim("userId",21)   
            .withClaim("userName","xiaohuihui")  // playload
            .withExpiresAt(instance.getTime())   // 指定令牌过期时间
            .sign(Algorithm.HMAC256("kirtuicxmnhlkeqwem$#%$^%^$#$"));  // signature  签名内容自定义

    System.out.println(token);
}

image-20220104140816590

验证签名

/**
     * 验证签名
     */
    @Test
    public void t2(){
        JWTVerifier build = JWT.require(Algorithm.HMAC256("kirtuicxmnhlkeqwem$#%$^%^$#$")).build();
        DecodedJWT verify = build.verify("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6InhpYW9odWlodWkiLCJleHAiOjE2NDEyNzYwNzYsInVzZXJJZCI6MjF9.HhPEyOyQwamGAbZV0AEMMy24Rw7zKifpQxub4WfHpAU");   // 上面生成的字符串
        System.out.println(verify.getClaim("userName").asString());
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BB3mDiGA-1689563766873)(http://qiniu.xiaotao.cloud/image-20220104141233841.png)]

异常:

AlgorithmMismatchException: 算法不匹配异常
TokenExpiredException: 令牌过期时间

JWTUtil

public class JwtUtils {
    public static final String SIGN = "#%f4524df$^dsf23&**&^%$5dsf%^$fd245223sf#$";

    /**
     * 生成· token
     * @return
     */
    public static String getToken(Map<String,String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,7);   // 默认7天后过期
        // 创建 jwt Builder
        JWTCreator.Builder builder = JWT.create();
        // playload  使用 lambda 遍历 map
        map.forEach((k,v) -> builder.withClaim(k,v));

        String token = builder.withExpiresAt(instance.getTime())   // 指定令牌过期时间
                .sign(Algorithm.HMAC256(SIGN));  // signature  签名

        return token;
    }

    /**
     * 验证 token  只要不合法 就会抛出异常
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
    }

    /**
     * 获取 token 信息
     */
    public static DecodedJWT getDecodedJWT(String token){
        DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
        return verify;
    }
}

SpringBoot 整合 JWT

controller login

/**
     * 管理后台登录   JWT
     * @param username
     */
    @PostMapping(value = "/user/login")
    @ResponseBody
    public RestResponseBo doLogin(@RequestParam("loginName") String username,
                                  @RequestParam("password") String password,
                                  @RequestParam(required = false) String rember_me,
                                  HttpServletRequest request,
                                  HttpServletResponse response) {
        //读取缓存信息值:用于判断用户登录失败次数
        Integer error_count = cache.get("login_error_count");
        try {
            TUsers users = new TUsers();
            users.setUsername(username);
            users.setPassword(password);
            TUsers user = usersService.login(users);

            // JWT
            Map<String,String> payload = new HashMap<>();
            payload.put("userId",user.getUid()+"");
            payload.put("userName",user.getUsername());
            // 生成JWT 令牌
            String token = JwtUtils.getToken(payload);


            //将当前登录用户 存入session中
            request.getSession().setAttribute(WebConst.LOGIN_SESSION_KEY, user);
            if (StringUtils.isNotBlank(rember_me)) {
                //存入cook中 通过工具类
                TaleUtils.setCookie(response, user.getUid());
            }
            //登录信息 存入日志表中
            logService.insertLog(LogActions.LOGIN.getAction(), null, request.getRemoteAddr(), user.getUid());
        } catch (Exception e) {
            error_count = null == error_count ? 1 : error_count + 1;
            if (error_count > 3) {
                return RestResponseBo.fail("您输入密码已经错误超过3次,请10分钟后尝试");
            }
            //错误后存入 缓存中
            cache.set("login_error_count", error_count, 10 * 60);
            String msg = "登录失败";
            if (e instanceof TipException) {
                msg = e.getMessage();
            } else {
                LOGGER.error(msg, e);
            }
            return RestResponseBo.fail(msg);
        }
        return RestResponseBo.ok();
    }

后面需要保护的请求

 @RequestMapping("/user/test")
public Map<String,Object> test(String token){
    HashMap<String, Object> map = new HashMap<>();
    try {
        DecodedJWT verify = JwtUtils.getDecodedJWT(token);  // 验证令牌
        map.put("status",true);
        map.put("msg","请求成功");
    }catch (SignatureVerificationException e){
        e.printStackTrace();
        map.put("msg","无效签名");
    }catch (TokenExpiredException e){
        e.printStackTrace();
        map.put("msg","token 过期");
    }catch (AlgorithmMismatchException e){
        e.printStackTrace();
        map.put("msg","两次算法不一致");
    }catch (Exception e){
        map.put("msg","token 无效");
    }
    map.put("status",false);
    return map;
}

JWTInterceptor

/**
 * jwt 请求拦截处理
 */
public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HashMap<String, Object> map = new HashMap<>();
        // 获取请求头中的令牌
        String token = request.getHeader("token");

        if(token == null){
            request.setAttribute("msg","你没有权限访问 请先登录 ");
            request.getRequestDispatcher("/login.html").forward(request,response);
            return false;
        }

        try {
            JwtUtils.getDecodedJWT(token);  // 验证令牌
            return true;  // 发行
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg","无效签名");
        }catch (TokenExpiredException e){
            e.printStackTrace();
            map.put("msg","token 过期");
        }catch (AlgorithmMismatchException e){
            e.printStackTrace();
            map.put("msg","两次算法不一致");
        }catch (Exception e){
            map.put("msg","token 无效");
        }
        map.put("status",false);  // 设置状态
        // 将 map 转为 json 响应
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().print(json);
        return false;
    }
}

将拦截器注入到SpirngMVC

/**
     * 登录请求拦截转发
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**")           //拦截的请求
                .excludePathPatterns("/login.html","/","/user/login","/css/**","/js/**","/img/**");  //不拦截的请求
    }

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

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

相关文章

记录安装DragGAN遇到的问题

首先第一个是安装的问题 安装的envirenment.yml有很多包过时了下载不了&#xff0c;可以参考以下文章写一个requirement.txt来下载依赖环境 --extra-index-url https://download.pytorch.org/whl/cu118 numpy1.23.5 click scipy pillow9.5.0 requests tqdm4.65.0 ninja matpl…

哪些语句会被waf屏蔽?

什么是waf&#xff1a;Web应用防火墙&#xff0c;Web Application Firewall的简称。 waf的功能&#xff1a;WAF可以发现和拦截各类Web层面的攻击&#xff0c;记录攻击日志&#xff0c;实时预警提醒&#xff0c;在Web应 用本身存在缺陷的情况下保障其安全。 封IP &#xff1a;…

Qt应用开发——QLineEdit

目录 一、概述 二、属性和方法 三、信号 一、概述 QLineEdit允许用户输入和编辑单行纯文本&#xff0c;并可以使用快捷编辑功能&#xff0c;包括复制、粘贴、剪切和拖放。是项目开发中最常用的输入控件。 默认键绑定描述如下。 Left Arrow //将光标向左移…

Fourier变换极其应用(Brad G. Osgood)——第1章——Fourier级数

目录 第1章 Fourier级数 1.1 选择&#xff1a;“欢迎入局”(Choices: Welcome Aboard) 1.2 周期性现象(Periodic phenomena) 1.2.1 时间和空间(time and space) 1.2.1.1 时间和空间周期性在波动中最自然地结合在一起 1.2.1.2 更多关于空间的周期性例子 1.2.2 定义&…

11 简单的Thymeleaf语法

11.1 spring-boot环境准备 重要依赖&#xff1a; <!--thymeleaf--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 11.2 转发消息不转义 就是如…

picgo Request failed with status code 404

今天写picgo的时候&#xff0c;出现了一个错误&#xff0c;如何解决&#xff1a; 这里是repo的配置出现了问题&#xff0c;不过我的是因为粗心&#xff0c;把master写成了mater&#xff0c;emmmm 这里的repo要跟仓库的地址相同就是这一块&#xff1a;把这一块填到repo就行 然…

Linux查看二进制文件

Linux查看二进制文件 hexdump、hd、od、xxd hexdump、hd 可以使用16进制、10进制、8进制、ascii码的形式查看文件。 执行 ls -al which hd就会看到hd其实只是hexdump的一个软链接。 使用man hexdump&#xff0c;可以查看hexdump的各种参数。 -b, --one-byte-octal 单字节…

使用SQL JOIN语句把来自两个或多个表的行结合起来

先创建一个表&#xff1a; 创建一个名为 websites 的表 CREATE TABLE websites ( id int, name varchar(255), url varchar(255), alexa varchar(255), country varchar(255) ); 然后给它插入一些数据&#xff08;插入了四行新数据&#xff09;&#xff1a; insert into we…

详解go的hex.Encode原理

简言 今天看nsq的messageID生成的时候&#xff0c;发现它使用了hex.Encode函数来产生编码&#xff0c;那就顺道研究一下这个编码方式。 原理 hex是16进制的意思&#xff0c;encode是进行编码的意思&#xff0c;内部实现也很简单&#xff0c;就是 每4位计算出十六进制的值&a…

Idea项目初始化配置

Idea项目初始化配置 &#x1f4d4; 千寻简笔记介绍 千寻简笔记已开源&#xff0c;Gitee与GitHub搜索chihiro-notes&#xff0c;包含笔记源文件.md&#xff0c;以及PDF版本方便阅读&#xff0c;且是用了精美主题&#xff0c;阅读体验更佳&#xff0c;如果文章对你有帮助请帮我…

Node.js: express + MySQL实现修改密码

实现修改密码&#xff0c;本篇文章实现修改密码只考虑以下几个方面&#xff1a; &#xff08;1&#xff09;&#xff0c;获取旧密码 &#xff08;2&#xff09;&#xff0c;获取新密码 &#xff08;3&#xff09;&#xff0c;将获取到的旧密码与数据库中的密码进行比对&#xf…

IC 后端 corner 介绍

在数字IC后端&#xff0c;有对晶体管的偏差建模的PVT corner&#xff0c;以及对互连线偏差建模的RC corner。 芯片的延迟一般受到三个因素的影响&#xff1a;工艺&#xff08;Process&#xff09;、电压&#xff08;Voltage&#xff09;、温度&#xff08;Temperature&#xff…

rabbitmq是什么?rabbitmq安装、原理、部署

rabbitmq是什么&#xff1f; MQ的全称是Messagee Queue&#xff0c;因为消息的队列是队列&#xff0c;所以遵循FIFO 先进先出的原则是上下游传递信息的跨过程通信机制。 RabbitMQ是一套开源&#xff08;MPL&#xff09;新闻队列服务软件由 LShift 提供的一个 Advanced Messag…

linux学成之路(基础篇)(二十一)nfs服务器

前言 NFS是Network File System的缩写&#xff0c;它是一种在计算机网络中共享文件和文件系统的协议。NFS允许不同的计算机系统之间通过网络访问、读取和写入远程文件&#xff0c;就像访问本地文件一样。它是一种基于客户端-服务器体系结构的协议&#xff0c;其中一个计算机充当…

Python(三十五)pass语句

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

excel中的vlookup如何实现根据多个条件查找?

目录 简述问题公式思路通用公式三条件查找公式实例 简述 Excel 中根据一个条件查找非常方便&#xff0c;Excel 提供了内置函数 VLOOKUP。但是实际中往往有多种情形&#xff0c;需要根据多个条件进行查找操作&#xff0c;目前没有现成的内置函数。 本篇介绍 VLOOKPCHOOSE 组合…

SpringBoot读取配置的几种方式

SpringBoot读取配置的方式有多种&#xff0c;这里介绍6种 1.第一种Value 注意&#xff1a;static和final修饰的变量不生效 2.通过ConfigurationProperties&#xff08;prefix“”&#xff09; 适用于对对象多个变量统一绑定&#xff0c;比Value高效 3.通过Environment Sprin…

信创环境下,使用国产服务器如何进行文件高速可靠传输?

信创&#xff0c;即信息技术应用创新&#xff0c;2018年以来&#xff0c;受“华为、中兴事件”影响&#xff0c;国家将信创产业纳入国家战略&#xff0c;并提出了“28n”发展体系。从产业链角度&#xff0c;信创产业生态体系较为庞大&#xff0c;主要包括基础硬件、基础软件、应…

堆和栈的区别以及栈的顺序存储和链式存储—Python数据结构(三)

栈 一、栈 1. 定义 栈是限制在一端进行插入操作和删除操作的线性表(俗称堆栈)&#xff0c; 允许进行操作的一端称为”栈顶“&#xff0c;另一固定端称为”栈底“&#xff0c;当栈中没有元素时称为”空栈“。 2. 特点 栈只能在一端进行操作。 栈模型具有先进后出&#xff…

基于jeecg-boot的任务甘特图显示

更多功能看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/nbcio-boot 前端代码&#xff1a;https://gitee.com/nbacheng/nbcio-vue.git 在线演示&#xff08;包括H5&#xff09; &#xff1a; http://122.227.135.243:9888 基于项目的任务显…