学习笔记-Cookie、Session、JWT

news2024/9/22 10:03:41

目录

一、验证码的生成与校验

1. 创建生成验证码的工具类

2. 写一个 Controller

3. 实现验证码验证

1. 获取验证码

2. 验证码请求过程

3. 验证码的校验

4. 原理说明

5. 验证

 6. 总结

二、JWT登录鉴权

1. 为什么要做登录鉴权?

2. 什么是 JWT 

3. JWT相比传统的鉴权方式的优点

1. Session 认证

2. Session 认证的缺点

3. JWT 的优点

4. 创建 JWT

1. 引入依赖

2. 创建JWT 工具类

3. 生成一个token

5. 使用 JWT

1. Vue 导入依赖

2. 前端创建 Cookie 工具类

3. 在登录组件设置 token

4. 判断是否有 token

5. 发送 token

6. 服务器获取token


在登录页面加入图形验证码

一、验证码的生成与校验

1. 创建生成验证码的工具类

package com.Util;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;


public class ImageCodeUtils {

    /**
     * 图片的宽度
     */
    private int width = 160;
    /**
     * 图片的高度
     */
    private int height = 40;
    /**
     * 验证码字符个数
     */
    private int codeCount = 4;
    /**
     * 验证码干扰线数
     */
    private int lineCount = 20;
    /**
     * 验证码
     */
    private String code = null;

    private BufferedImage buffImg = null;
    Random random = new Random();

    public ImageCodeUtils() {
        createImage();
    }

    public ImageCodeUtils(int width, int height) {
        this.width = width;
        this.height = height;
        createImage();
    }

    public ImageCodeUtils(int width, int height, int codeCount) {
        this.width = width;
        this.height = height;
        this.codeCount = codeCount;
        createImage();
    }

    public ImageCodeUtils(int width, int height, int codeCount, int lineCount) {
        this.width = width;
        this.height = height;
        this.codeCount = codeCount;
        this.lineCount = lineCount;
        createImage();
    }


    /**
     * 生成图片
     */
    private void createImage() {
        // 字体的宽度
        int fontWidth = width / codeCount;
        // 字体的高度
        int fontHeight = height - 5;
        int codeY = height - 8;

        // 图像buffer
        buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = buffImg.getGraphics();

        // 设置背景色
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);



        // 设置字体
        //Font font1 = getFont(fontHeight);
        Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
        g.setFont(font);

        // 设置干扰线
        for (int i = 0; i < lineCount; i++) {
            int xs = random.nextInt(width);
            int ys = random.nextInt(height);
            int xe = xs + random.nextInt(width);
            int ye = ys + random.nextInt(height);
            g.setColor(getRandColor(1, 255));
            g.drawLine(xs, ys, xe, ye);
        }

        // 添加噪点
        float yawpRate = 0.01f;
        int area = (int) (yawpRate * width * height);
        for (int i = 0; i < area; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);

            buffImg.setRGB(x, y, random.nextInt(255));
        }

        // 得到随机字符
        String str1 = randomStr(codeCount);
        this.code = str1;
        for (int i = 0; i < codeCount; i++) {
            String strRand = str1.substring(i, i + 1);
            g.setColor(getRandColor(1, 255));

            // a为要画出来的东西,x和y表示要画的东西最左侧字符的基线位于此图形上下文坐标系的 (x, y) 位置处
            g.drawString(strRand, i*fontWidth+3, codeY);
        }
    }

    /**
     * 得到随机字符串
     * @param n
     * @return
     */
    private String randomStr(int n) {
        String str1 = "ABCDEFGHJKMNOPQRSTUVWXYZabcdefghjkmnopqrstuvwxyz1234567890";
        String str2 = "";
        int len = str1.length() - 1;
        double r;
        for (int i = 0; i < n; i++) {
            r = (Math.random()) * len;
            str2 = str2 + str1.charAt((int) r);
        }
        return str2;
    }

    /**
     * 得到随机颜色
     * @param fc
     * @param bc
     * @return
     */
    private Color getRandColor(int fc, int bc) {
        if (fc > 255){
            fc = 255;
        }
        if (bc > 255){
            bc = 255;
        }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }

    /**
     * 产生随机字体
     */
    private Font getFont(int size) {
        Random random = new Random();
        Font[] font = new Font[5];
        font[0] = new Font("Ravie", Font.PLAIN, size);
        font[1] = new Font("Antique Olive Compact", Font.PLAIN, size);
        font[2] = new Font("Fixedsys", Font.PLAIN, size);
        font[3] = new Font("Wide Latin", Font.PLAIN, size);
        font[4] = new Font("Gill Sans Ultra Bold", Font.PLAIN, size);
        return font[random.nextInt(5)];
    }

    /**
     * 扭曲方法
     * @param g
     * @param w1
     * @param h1
     * @param color
     */
    private void shear(Graphics g, int w1, int h1, Color color) {
        shearX(g, w1, h1, color);
        shearY(g, w1, h1, color);
    }

    private void shearX(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(2);

        boolean borderGap = true;
        int frames = 1;
        int phase = random.nextInt(2);

        for (int i = 0; i < h1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(0, i, w1, 1, (int) d, 0);
            if (borderGap) {
                g.setColor(color);
                g.drawLine((int) d, i, 0, i);
                g.drawLine((int) d + w1, i, w1, i);
            }
        }

    }

    private void shearY(Graphics g, int w1, int h1, Color color) {

        int period = random.nextInt(40) + 10;

        boolean borderGap = true;
        int frames = 20;
        int phase = 7;
        for (int i = 0; i < w1; i++) {
            double d = (double) (period >> 1)
                    * Math.sin((double) i / (double) period
                    + (6.2831853071795862D * (double) phase)
                    / (double) frames);
            g.copyArea(i, 0, 1, h1, 0, (int) d);
            if (borderGap) {
                g.setColor(color);
                g.drawLine(i, (int) d, i, 0);
                g.drawLine(i, (int) d + h1, i, h1);
            }

        }

    }

    public void write(OutputStream sos) throws IOException {
        ImageIO.write(buffImg, "png", sos);
        sos.close();
    }

    public BufferedImage getBuffImg() {
        return buffImg;
    }

    public String getCode() {
        return code.toLowerCase();
    }
}

2. 写一个 Controller

方法:前端访问,调用一下工具类生成验证码图片并返回

引入一下依赖

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

3. 实现验证码验证

1. 获取验证码

已经获取到验证码了,那么下一步怎么验证验证码呢?

2. 验证码请求过程

游览器那么多,服务器怎么知道是哪个游览器请求的?

这就可以使用到 Cookie 和 Session 

3. 验证码的校验

把字符串形式的验证码存到 Session

从Session 中获取验证码

4. 原理说明

5. 验证

提示验证码错误

先查看验证验证码的SessionID是否和获取验证码的SessionID 一样

可以看到,两个是不同的SessionID ,服务器根据ID去获取的值是不同的,所以验证码错误

那么为什么是不同的呢?

这是因为 Vue 开启了代理转发,所以每次请求的 Cookie中JSESSIONID 值会发生变化

如何解决呢?

官方的解决方案是代理转发的 请求 URL 前缀和服务器请求路径的项目名相同就没问题了

原来的请求路径

修改后的请求路径

注意:vue 的配置文件修改后需要重启

修改服务器的 URL ,修改后重启服务器

可以看到SessionID值是一样的了

验证成功

 6. 总结
  1. 会话开始:当一个用户首次访问一个网站,服务器会创建一个新的HttpSession对象,并生成一个唯一的会话ID(通常称为JSESSIONID)。
  2. 会话ID存储在Cookie中:服务器会在响应中包含一个Set-Cookie头,将这个会话ID作为cookie的一部分发送给浏览器。浏览器会存储这个cookie。
  3. 后续请求:当用户继续与网站交互时,浏览器会自动在每个后续请求中包含这个会话ID的cookie。这使得服务器能够识别出请求来自于哪个具体的会话。
  4. 服务器识别会话:服务器接收到请求后,会读取请求头中的cookie,提取出会话ID,然后使用这个ID在服务器端的会话存储中查找相应的HttpSession对象。
  5. 会话数据访问:一旦找到了HttpSession对象,服务器就可以从这个对象中读取或写入数据,比如用户信息、登录状态等。
  6. 会话结束:当用户关闭浏览器或会话超时,HttpSession对象会被销毁,除非服务器端有特别的配置来延长会话的存活时间。

二、JWT登录鉴权

JWT 全程 JSON Web Token

1. 为什么要做登录鉴权?

安全考虑,需要登录之后有操作权限了之后才能访问API接口

2. 什么是 JWT 

JWT(JSON Web Token)是一种在网络应用中用于身份验证和授权的令牌。你可以把它想象成一张电子版的“身份证”或“通行证”。

当你登录一个网站后,服务器会生成一个JWT,JWT 本质就是一条字符串,它把你的身份信息(比如用户名)保存到一个JSON字符串中,然后进行编码得到一个token 令牌,然后把这个令牌发回给你的浏览器。之后,每当你的浏览器想要访问受保护的资源时,它都会带上这个JWT。

3. JWT相比传统的鉴权方式的优点

1. Session 认证

我们知道 HTTP 是一种无状态的协议,HTTP协议在设计上不保留任何两次请求之间的信息。换句话说,当你向一个网站发送请求时,比如浏览一个网页,这个请求是独立的,它并不依赖于你之前对该网站做的任何事情。一旦服务器处理完你的请求并返回了响应,它就会忘记这次交互,就像从来没有发生过一样。

所以为了让服务器知道是谁在访问,我们会在游览器第一次登陆成功的时候,创建一个SessionID,然后把用户信息保存在 Session 对象中,最后把SessionID放到 Cookie 返回给游览器,这样下次游览器再访问的时候就知道是谁了,这就是基于Session 认证的过程

2. Session 认证的缺点

由于基于Cookie,而cookie无法跨域,所以session的认证也无法跨域

Session 是保存在服务器的,会使服务器的开销增大

3. JWT 的优点

简洁,

无状态存储,以加密的形式保存在客户端,

时效性,可以设置多少时间失效

4. 创建 JWT

1. 引入依赖
    <dependency>
      <groupId>io.github.qyg2297248353.components</groupId>
      <artifactId>jsonwebtoken-jjwt</artifactId>
      <version>2.0.0</version>
    </dependency>

2. 创建JWT 工具类
package com.Util;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Map;
public class JwtUtil {
    // 私钥
    private static String privateKey = "12345678901234567890123456789012abcdefghijklmn";
    /*
    * 生成token
    * @param claims 要加密的数据
    * @return
    * */
    public static String generateToken(Map<String, Object> claims) {
        // 使用JWT构建器构建令牌
        // 添加负载(claims),存储用户信息
        // 使用HS256算法和私钥进行签名,确保令牌的完整性和安全性
        // 最后将令牌以JSON格式编码,并压缩为紧凑字符串格式
        String token = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, privateKey) // HS256加密,密钥长度必须大于等于256 bit
                .compact();
        return token;
    }
}
3. 生成一个token

在 Service 层调JWT工具类生成 token

登录成功后返回的 token

那么如何在每次请求都带上这个 token 呢?

可以把token 放到 Cookie 里

5. 使用 JWT

1. Vue 导入依赖
npm i js-cookie@2.2.0 -S 
2. 前端创建 Cookie 工具类

3. 在登录组件设置 token

引入token 工具类的方法

4. 判断是否有 token

在路由器文件的路由守卫中判断,如果有 token 则放行

验证

5. 发送 token

在自定义 axios 文件的请求拦截器中添加 token         自定义 axios

可以看到在登录成功后,请求头里有 token

6. 服务器验证token

服务器在哪个 Controller 里判断?在所有 Controller 都判断一下比较麻烦

这就可以使用拦截器,在拦截器中统一判断

1. 创建登录拦截器

2. 配置拦截器

在spring 配置文件配置拦截器

    <!--配置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>   <!--拦截所有请求-->
            <mvc:exclude-mapping path="/Admin/Login"/>  <!--放行登录操作-->
            <mvc:exclude-mapping path="/ImageCode/Captcha"/>   <!--放行请求验证码-->
            <bean class="com.Interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

这样就可以在每次请求资源时都带有token,服务器根据 token 判断是否有权限访问数据

目前,在 Vue 的路由守卫有判断是否登录(token)以及在服务器的拦截器也有判断

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

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

相关文章

MATLAB优化模型(2)

一、前言 在MATLAB中实现动态规划、图论、网络流模型&#xff08;如最短路、最大流、最小生成树&#xff09;的优化模型&#xff0c;可以通过多种方法完成&#xff0c;但通常会依赖于MATLAB内置的函数或工具箱&#xff0c;比如Optimization Toolbox、Graph Theory Toolbox等。以…

Python 实现股票指标计算——SKDJ

SKDJ (Stochastic KDJ) - 慢速随机指标 1 公式 LOWV:LLV(LOW,N); HIGHV:HHV(HIGH,N); RSV:EMA((CLOSE-LOWV)/(HIGHV-LOWV)*100,M); K:EMA(RSV,M); D:MA(K,M); 2 数据准备 我们以科创50指数 000688 为例&#xff0c;指数开始日期为2019-12-31&#xff0c;数据格式如下&#…

Leetcode 第 135 场双周赛题解

Leetcode 第 135 场双周赛题解 Leetcode 第 135 场双周赛题解题目1&#xff1a;3222. 求出硬币游戏的赢家思路代码复杂度分析 题目2&#xff1a;3223. 操作后字符串的最短长度思路代码复杂度分析 题目3&#xff1a;3224. 使差值相等的最少数组改动次数思路代码复杂度分析 题目4…

SQL注入 报错注入、文件上传、布尔盲注、时间盲注

第7关 文件上传 ---面试官常问 1、MySQL上传shell的满足条件 如果面试官问你如何通过MySQL向网站上传一个shell脚本或者其他语言的一些脚本 ---就可以通过outfile导出的方式进行上传&#xff1b; outfile导出的前提条件&#xff1a;1、必须知道网站的物理路径&#xf…

Java每日一练_模拟面试题2(循环依赖)

一、啥事Spring里面的循环依赖 SpringBoot 循环依赖通常发生在两个或多个Bean相互依赖对方时&#xff0c;例如&#xff1a;A依赖B&#xff0c;同时B也依赖A。 二、如何解决&#xff1f; 解决方案&#xff1a; 构造器注入&#xff1a;如果循环依赖发生在构造器中&#xff0c;S…

[YashanDB认证]YashanDB个人版安装

为什么选择YashanDB? 崖山数据库系统YashanDB是深圳计算科学研究院完全自主研发设计的新型数据库系统&#xff0c;经工信部下属机构权威检测&#xff0c;内核代码自主率100%。在经典数据库理论基础上&#xff0c;融入原创的有界计算理论、近似计算理论、并行可扩展理论和跨模融…

Taro学习记录(具体项目实践)

一、安装taro-cli 二、项目文件 三、项目搭建 1、Eslint配置 在项目生成的 .eslintrc 中进行配置 {"extends": ["taro/react"], //一个配置文件&#xff0c;可以被基础配置中的已启用的规则继承"parser": "babel/eslint-parser…

荒原之梦考研:专科考研成功的可能性大吗?

专科还是本科不是决定考研能否成功的关键因素&#xff0c;决定考研能否成功的关键因素是自己是否有清晰的规划、是否有足够的专注能力&#xff0c;以及是否能够吃得了考研的“苦”。 首先要有清晰的规划&#xff0c;比如说&#xff0c;不是我们每个人足够努力就都能考上 TOP1 …

electron-updater实现electron全量更新和增量更新——主进程部分

同学们可以私信我加入学习群&#xff01; 正文开始 前言更新功能所有文章汇总一、更新插件选择二、在main.js中引入我们的更新模块三、更新模块UpdateController.js暴露的方法checkUpdate四、更新模块UpdateController.js中的监听4.1监听是否有新版本需要更新&#xff1f;4.2 监…

红黑树与平衡二叉树的相同之处与不同之处

红黑树很多资料上写的非常繁杂&#xff0c;初次接触真的难以理解。写本文也就是为了记录一些思考和想法&#xff0c;并不会记录如何使用代码实现。 不记录代码还有个原因&#xff1a;黑红树的算法就是根据各种情况进行一些操作&#xff0c;情况很复杂&#xff0c;分插入的和删…

数据结构 二叉树和堆总结

树 概念 树是一种层次结构非线性的数据结构&#xff0c;其是由节点和边组成&#xff0c;可以用来表示层次关系的数据。 树的相关概念 节点&#xff1a;树的基本组成单位&#xff0c;每个节点都包含数据&#xff0c;同时与其他节点相互连接根节点&#xff1a;树的顶层节点&…

SpringBoot_第十一章(Thymeleaf模板引擎)

目录 1&#xff1a;什么是Thymeleaf模板引擎 2&#xff1a;springboot怎使用Thymeleaf 2.1&#xff1a;导入pom文件 2.2&#xff1a;查看ThymeleafAutoConfiguration 3&#xff1a;Thymeleaf核心语法 4&#xff1a;使用Thymeleaf 5&#xff1a;具体语法练习 1&#xff1a…

数据集划分方法

数据集划分是机器学习和数据科学中的一个重要步骤&#xff0c;主要目的是为了确保模型的有效性和可靠性。 留出法&#xff08;简单交叉验证&#xff09; 将数据集划分为互斥的子集&#xff1a;训练集和测试集。 训练集: 用于训练模型。 测试集: 用于评估模型的性能和验证其准确…

图神经网络揭秘:视觉和实用指南

目录 一、说明 二、图如何网络化&#xff1f; 三、你需要知道的 3.1 进入图神经网络 3.2 消息传递 3.3 我们如何处理最终的向量表示&#xff1f; 四、图神经网络&#xff0c;总结 4.1 为什么选择图形神经网络&#xff1f; 4.2 简而言之 一、说明 了解图神经网络的世界&#xff…

C#中投影运算的深入解析与实例应用

文章目录 1、投影运算的基本语法2、投影运算的高级用法3、投影运算在向量空间中的运用4、投影运算在数据库和XML中的实际应用5、投影运算能用于哪些实际场景&#xff1f;6、结论 在C#编程中&#xff0c;投影运算是一种常用的数据操作技术&#xff0c;它可以将一个数据集合转换成…

开放式耳机推荐?时尚潮流品牌:悠律ringbud pro开放式耳机实测测评

作为一位音乐发烧友&#xff0c;什么类型的耳机都体验过&#xff0c;有些几百上千的耳机音质还是差点意思&#xff0c;还是会有听久了感觉不舒服的情况&#xff0c;低音量感不够的问题&#xff0c;直到用了悠律ringbud pro开放式耳机&#xff0c;才算真正打开新世界的大门&…

C语言程序设计-[2] 数据类型、常量和变量

1、数据类型 C语言支持的数据类型如下&#xff1a; 2、常量 常量就是不同数据类型下的值。这里主要讲整型、实型和字符型常量。 &#xff08;1&#xff09;整型常量&#xff1a;用十进制、八进制和十六进制三种形式表示。 &#xff08;1&#xff09;实型常量&#xff1a;由整…

HCIP实验-MGRE

实验拓扑&#xff1a; 实验要求&#xff1a; 1.R2为ISP&#xff0c;其上只能配置IP地址 2.R1-R2之间为HDLC封装 3.R2-R3之间为PPP封装&#xff0c;pap认证&#xff0c;R2为主认证方 4.R2-R4之间为PPP分装&#xff0c;chap认证&#xff0c;R2为主认证方 5.R1、R3、R4构建MG…

unity拖拽物品遇到的bug及解决思路

记录一下拖拽实现过程中遇到的bug RectTransform 专门用在UI中transform 判断点击是否在UI中 使用这个函数就可以判断点击的是否是UI面板&#xff0c;返回true表明在UI面板中 EventSystem.current.IsPointerOverGameObject()值得一提的是&#xff0c;如果发现了有UI穿透效…

【C语言】分支与循环(分支篇)

前言 C语言是一种结构化的计算机语言&#xff0c;这里指的通常是顺序结构、选择结构、循环结构&#xff0c;掌握这三种结构之后我们就可以解决大多数问题。 分支结构可以使用if、switch来实现&#xff0c;而循环可以使用for、while、do while来实现。 1. if语句 1.1 if if…