安全认证--JWT介绍及使用

news2024/11/17 12:27:04

安全认证--JWT介绍及使用

  • 1.无状态登录原理
    • 1.1.什么是有状态?
    • 1.2.什么是无状态
    • 1.3.如何实现无状态
    • 1.4.JWT
      • 1.4.1.简介
      • 1.4.2.数据格式
  • 2.编写JWT工具
    • 2.1.添加JWT依赖
    • 2.2.载荷对象
    • 2.3.工具
    • 2.4.测试
      • 2.4.1.配置秘钥
      • 2.4.2.测试类
    • 2.5项目源码

1.无状态登录原理

有状态登录和无状态登录详解

1.1.什么是有状态?

有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。

例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。

缺点是什么?

  • 服务端保存大量数据,增加服务端压力
  • 服务端保存用户状态,无法进行水平扩展
  • 客户端请求依赖服务端,多次请求必须访问同一台服务器

1.2.什么是无状态

微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:

  • 服务端不保存任何客户端请求者信息
  • 客户端的每次请求必须具备自描述信息,通过这些信息识别客户端身份

带来的好处是什么呢?

  • 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
  • 服务端的集群和状态对客户端透明
  • 服务端可以任意的迁移和伸缩
  • 减小服务端存储压力

1.3.如何实现无状态

无状态登录的流程:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
  • 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
  • 以后每次请求,客户端都携带认证的token
  • 服务端对token进行解密,判断是否有效。

整个登录过程中,最关键的点是什么?

token的安全性

token是识别客户端身份的唯一标示,如果加密不够严密,被人伪造那就完蛋了。

采用何种方式加密才是安全可靠的呢?

我们将采用JWT来生成token,保证token的安全性

1.4.JWT

1.4.1.简介

JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io
GitHub上jwt的java客户端:https://github.com/jwtk/jjwt

1.4.2.数据格式

JWT包含三部分数据:

  • Header:头部,通常头部有两部分信息:

    • token类型,这里是JWT
    • 签名算法,自定义

    我们会对头部进行base64加密(可解密),得到第一部分数据

  • Payload:载荷,就是有效数据,一般包含下面信息:

    • 标准载荷:JWT规定的信息,jwt的元数据:
      • JTI: JWT的id,当前jwt的唯一标识(像身份证号)
      • IAT: issue at 签发时间
      • EXP:过期时间
      • SUB:签发人
    • 自定义载荷:
      • 用户身份信息,(注意,这里因为采用base64加密,可解密,因此不要存放敏感信息)

    这部分也会采用base64加密,得到第二部分数据

  • Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性

生成的数据格式:
在这里插入图片描述
可以看到分为3段,每段就是上面的一部分数据。
什么是 JWT – JSON WEB TOKEN
傻傻分不清之 Cookie、Session、Token、JWT
JWT详细教程与使用

2.编写JWT工具

我们会用到比较流行的java语言的JWT工具,jjw

2.1.添加JWT依赖

我们需要先在项目中引入JWT依赖:

 <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
                <!--json工具-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--日期时间工具类-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
              <!--redis-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.2.载荷对象

JWT中,会保存载荷数据,我们计划存储2部分:

  • jti:jwt的id
  • UserDetail:用户数据

为了方便后期获取,我们定义一个类来封装。

添加一个实体类,代表载荷信息


import lombok.Data;

//载荷对象
@Data
public class Payload {
    /**
     * tocken的id
     */
    private String jti;

    /**
     * 用户数据
     */
    private UserDetail userDetail;
}


载荷中的UserDetail信息,也需要一个实体类表示,这里我们定义一个UserDetail类。

这里我们假设用户信息包含2部分:

  • id:用户id
  • username:用户名
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class UserDetail {
    /**
     * 用户id
     */
    private Long id;
    /**
     * 用户名
     */
    private String username;
}

2.3.工具

我创建一个JwtUtils 工具类,用来封装几个方法:

  • createJwt() :生成JWT
  • parseJwt() :验证并解析JWT
import com.example.jwt.constants.RedisConstants;
import com.example.jwt.dto.Payload;
import com.example.jwt.dto.UserDetail;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;

import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class JwtUtils {

    /**
     * JWT解析器
     */
    private final JwtParser jwtParser;
    /**
     * 秘钥
     */
    private final SecretKey secretKey;

//   @Autowired
    private StringRedisTemplate redisTemplate;


    private final static ObjectMapper mapper = new ObjectMapper();

    public JwtUtils(String key) {
        // 生成秘钥
        secretKey = Keys.hmacShaKeyFor(key.getBytes(Charset.forName("UTF-8")));
        // JWT解析器
        this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
    }


    /**
     * 生成jwt,自己指定的JTI
     *
     * @param userDetails 用户信息
     * @return JWT
     */
    public String createJwt(UserDetail userDetails) {
        try {
            // 生成tokenid
            String jti=createJti();
            //存入redis中
//            this.redisTemplate.opsForValue().set(RedisConstants.JTI_KEY_PREFIX+userDetails.getUsername(),jti,30, TimeUnit.MINUTES);
            return Jwts.builder().signWith(secretKey)
                    .setId(jti)
                    .claim("user", mapper.writeValueAsString(userDetails))
                    .compact();
        } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
        }
    }

    /**
     * 解析jwt,并将用户信息转为指定的Clazz类型
     *
     * @param jwt   token
     * @return 载荷,包含JTI和用户信息
     */
    public Payload parseJwt(String jwt) {
        try {
            Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);
            Claims claims = claimsJws.getBody();
            Payload payload = new Payload();
            payload.setJti(claims.getId());
            payload.setUserDetail(mapper.readValue(claims.get("user", String.class), UserDetail.class));
            return payload;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private String createJti() {
        return StringUtils.replace(UUID.randomUUID().toString(), "-", "");
    }

    /**
     * 刷新jwt有效期
     * @param username
     */
    public void refreshJwt(String  username) {
        String key= RedisConstants.JTI_KEY_PREFIX+username;
        //重置key的过期时间
        redisTemplate.expire(key,30,TimeUnit.MINUTES);
    }
}

在这里插入图片描述

2.4.测试

2.4.1.配置秘钥

application.yml文件中配置秘钥:

yy:
  jwt:
    key: helloWorldJavaAuthServiceSecretKe

定义一个配置类,注册JwtUtils注入到Spring的容器。


import com.example.jwt.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JwtConfig {

    @Value("${yy.jwt.key}")
    private String key;

    @Bean
    public JwtUtils jwtUtils(){
        return new JwtUtils(key);
    }
}

2.4.2.测试类

    @Autowired
    private JwtUtils jwtUtils;

    @Test
    public void test()   {
        // 生成jwt
        String jwt = jwtUtils.createJwt(UserDetail.of(112L, "lele"));
        System.out.println("jwt = " + jwt);

        // 解析jwt
        Payload payload = jwtUtils.parseJwt(jwt);
        System.out.println("payload = " + payload);
    }

结果:

jwt = eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIyZTRkMGI1NjZiODY0YjUzODAwZTI3NGNhOWE0MTcxYSIsInVzZXIiOiJ7XCJpZFwiOjExMixcInVzZXJuYW1lXCI6XCJsZWxlXCJ9In0.NGa42tISwsLg_hyONasdGPGDigFFxkWbH04wd4ELztY
payload = Payload(jti=2e4d0b566b864b53800e274ca9a4171a, userDetail=UserDetail(id=112, username=lele))

2.5项目源码

源码地址

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

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

相关文章

G1D54-CRF

一、CRF的输入X是什么&#xff1f;是构造的特征吗&#xff1f; 如此&#xff0c;CRF的x只用于状态函数吗&#xff1f; CRF的例子解释调用代码 机器之心 知乎忆榛 此处线性链条件随机场的特征函数形式被统一了&#xff1f; BilstmCRF&#xff0c;强烈推荐&#xff01;&#x…

AM402和SV660N、IS620N运动控制

软件&#xff1a;InoProShop(V1.7.3) 1、添加EtherCAT伺服从站 2、PLC运动控制程序和ETHERCAT在一个任务中。 3、编码器脉冲设置。 注意电机转速值是以秒还是分钟计量单位。 SV660N IS620N 4、设置电机停机方式。使用sin停机效果比较圆滑&#xff0c;默认梯形。 5、库管理器…

广和通携手联发科技正式发布基于MediaTek T830 平台5G模组FG370的可快速落地FWA解决方案

2月28日&#xff0c;全球领先的物联网无线通信解决方案和无线通信模组提供商广和通正式宣布&#xff1a;新一代5G模组FG370已率先实现量产&#xff0c;并于2023世界移动通信大会&#xff08;MWC Barcelona 2023&#xff09;期间携手联发科技正式发布基于FG370的FWA解决方案&…

十三、MyBatis的缓存

缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高程序的执行效率。 mybatis的缓存&#xff1a;将select语句的查询结果放到缓存&#xff08;内存&#xff09;当中&#xff0c;下一次还是这条select语句的话&#xff0c;直接从缓存中取&#xf…

数字信号复习题纲

数字信号复习题纲一、希尔伯特变换器&#xff08;:heavy_check_mark: &#xff09;1. 什么是希尔伯特变换器&#xff1f;2. 试证明信号通过希尔伯特变换器后的输出二、能量信号的自相关函数、卷积运算与能量谱&#xff08;:heavy_check_mark:&#xff09;1. 能量信号2. 试证明自…

webpack配置完全指南

前言 对于入门选手来讲&#xff0c;webpack 配置项很多很重&#xff0c;如何快速配置一个可用于线上环境的 webpack 就是一件值得思考的事情。其实熟悉 webpack 之后会发现很简单&#xff0c;基础的配置可以分为以下几个方面&#xff1a; entry 、 output 、 mode 、 resolve …

深入理解Storm 之 TridentStrom

从Demo讲起: FixedBatchSpout spout new FixedBatchSpout(new Fields("sentence"), 3, new Values("the cow jumped over the moon"),new Values("the man went to the store and bought some candy"),new Values("four score and seven …

新库上线 | CnOpenData中国债券市场债券信息数据

中国债券市场债券信息数据 一、数据简介 债券是政府、企业、银行等债务人为筹集资金&#xff0c;按照法定程序发行并向债权人承诺于指定日期还本付息的有价证券。债券购买者或投资者与发行者之间是一种债权债务关系&#xff0c;债券发行人即债务人&#xff0c;投资者&#xff…

关于 python 的异常使用说明 (python 的文件和异常)

文章目录异常1. 处理异常 ZeroDivisionError 异常2. 使用 try-except 代码块3. 使用异常避免崩溃4. else 代码块5. 处理 FileNotFoundError 异常6. 分析文本7. 失败时一声不吭异常 pyhong 使用被异常成为异常的特殊对象来管理程序执行期间发生的错误。 每当发生让 python 不知所…

【计算机网络:自顶向下方法】Chapter5 网络层:控制平面

本系列文章为笔者在学习b站中科大郑烇老师的计算机网络课程时&#xff08;郑老师讲得很清晰&#xff01;&#xff01;&#xff09;&#xff0c;结合课程PPT与《计算机网络&#xff1a;自顶向下方法》&#xff08;第七版&#xff09;所作的学习笔记&#xff0c;部分图片源自课程…

gitee 奇安信代码卫士使用

注册 gitee 账号后&#xff0c;push 一个项目&#xff0c;或者 fork 一个别人的项目&#xff0c;这里 fork 了一个 java-sec-code 靶场&#xff0c;使用的是个人版&#xff0c;像是低配版的 fortify 在项目的 服务 项下&#xff0c;选择奇安信代码卫士 创建分析 新建分析&…

【Java|golang】2373. 矩阵中的局部最大值

给你一个大小为 n x n 的整数矩阵 grid 。 生成一个大小为 (n - 2) x (n - 2) 的整数矩阵 maxLocal &#xff0c;并满足&#xff1a; maxLocal[i][j] 等于 grid 中以 i 1 行和 j 1 列为中心的 3 x 3 矩阵中的 最大值 。 换句话说&#xff0c;我们希望找出 grid 中每个 3 x …

操作系统笔记、面试八股(一)—— 进程、线程、协程

文章目录1. 进程、线程、协程1.1 进程1.1.1 进程间的通信方式1.1.2 进程同步方式1.1.3 进程的调度算法1.1.4 优先级反转1.1.5 进程状态1.1.6 PCB进程控制块1.1.7 进程的创建和撤销过程1.1.8 为什么要有进程1.2 线程1.2.1 为什么要有线程1.2.2 线程间的同步方式1.3 协程1.3.1 什…

创建Firebase项目并接入Firebase推送: Firebase Cloud Messaging (FCM)

1.FCM简介&#xff1a;Firebase Cloud Messaging (FCM) 是一种跨平台消息传递解决方案&#xff0c;可供您可靠地传递消息&#xff0c;而且还是免费的服务。支持 Android&#xff0c;IOS,Web,Flutter,Unity.消息类型可以使用 FCM 向客户端发送两种类型的消息&#xff1a;通知消息…

CEC2017:鱼鹰优化算法(Osprey optimization algorithm,OOA)求解cec2017(提供MATLAB代码)

一、鱼鹰优化算法简介 鱼鹰优化算法&#xff08;Osprey optimization algorithm&#xff0c;OOA&#xff09;由Mohammad Dehghani 和 Pavel Trojovsk于2023年提出&#xff0c;其模拟鱼鹰的捕食行为。 鱼鹰是鹰形目、鹗科、鹗属的仅有的一种中型猛禽。雌雄相似。体长51-64厘米…

Allegro如何设置铜皮避让的优先级操作指导

Allegro如何设置铜皮避让的优先级操作指导 在用Allegro进行PCB设计的时候,时常需要使用动态铜皮进行设计,当两块动态铜皮存在交集的时候,避让就会存在一个优先级,如下图 上方的铜皮避让调了下方的铜皮,上方的铜皮被避让了 如何调整让下方的铜皮避让上方的铜皮,如下图 具…

入门JAVA第十六天 数据库

一 、数据库技术学习内容与方法 1.1学习内容 1 Oracle 数据库 目前最好的关系型数据库 基本的CRUD命令。 SQL语句。select(R),update(U),detele(D),insert(C) 2 MySQL数据库 中小型醒目非常好用的关系型数据库。 灵活&#xff0c;小巧。 3 扩展软件开发流程中数据库设计原则 …

严格模式和非严格模式下的this指向问题

一、全局环境 1.函数调用 非严格模式&#xff1a;this指向是Window // 普通函数 function fn () { console.log(this, this); } fn() // 自执行函数 (function fn () { console.log(this, this); })() 严格模式&#xff1a;this指向是undefined //…

866363-70-4,N3-C5-NHS ester,叠氮-C5-NHS 主要物理性质分享

●外观以及性质&#xff1a;Azido-Aca-NHS淡黄色或无色油状&#xff0c;叠氮化物可以与炔烃、DBCO和BCN进行铜催化的点击化学反应。NHS酯可以与胺基反应&#xff0c;形成稳定的酰胺键。●中文名&#xff1a;叠氮-C5-NHS ester&#xff0c;6-叠氮己酸活性酯●英文名&#xff1a;…

「TCG 规范解读」PC 平台相关规范(2)

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…