【Web开发技术】JWT令牌技术(信息安全)

news2024/11/24 10:28:48

文章目录

    • 一、描述
    • 二、依赖
    • 三、配置
    • 四、java文件中的准备
    • 五、开始使用

一、描述

说到JWT令牌技术,就需要提到cookie和session两种技术。这两种技术在跨域问题(计算机网络的知识,百度可以搜到,就回归重点)上存在一定的局限性,跟不上流行框架和新编程思想的脚步,自然而然就需要迎来进步。JWT令牌提升了用户信息在跨域上的安全性和独立性。
如果进行接口测试,建议是提前关闭令牌,这样就方便白盒测试,避免不必要的时间消耗。

在这里插入图片描述

二、依赖

<!-- jwt包 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt}</version>
</dependency>
<!-- 简化代码 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

三、配置

关于application.yml全局配置文件

# xpq是我的名字缩写,你可以改成你自己的。但得注意下面的文件中都得改
xpq:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置jwt过期时间
    admin-ttl: 7200000
    # 设置前端传递过来的令牌名称
    admin-token-name: token

四、java文件中的准备

令牌特性,用于标识令牌绑定的属性,可以是用户ID,也可以是手机号码。
作用:返回令牌知道ID,保证了用户ID的安全性

public class JwtClaimsConstant {

    public static final String EMP_ID = "empId";
    public static final String USER_ID = "userId";
    public static final String PHONE = "phone";
    public static final String USERNAME = "username";
    public static final String NAME = "name";

}

创建一个JwtProperties.java,用于配置文件Bean属性定义绑定

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "xpq.jwt")
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;

    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

}

创建一个公共属性JwtUtil.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JwtUtil {

    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();
    }

    /**
     * Token解密
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

}

定义一个JWT令牌校验的拦截器(JwtTokenAdminInterceptor.java)用于拦截指定Controller

@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        //2、校验令牌
        try {
            log.info("jwt令牌校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前用户id:{}", empId);
            // 放入ThreadLoad中
            BaseContext.setCurrentId(empId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

注册自定义的拦截器组件(WebMvcConfiguration.java)

@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Resource
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

	/**
     * 注册自定义拦截器
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)
                .addPathPatterns("/xpq/**")
                // 开放以下接口,不需要进行jwt令牌验证
                // 所以就需要在这些接口下向用户提供jwt令牌
                // 普通登录
                .excludePathPatterns("/xpq/user/login")
                // 二维码登录
                .excludePathPatterns("/xpq/qr/*");
    }
	
}

五、开始使用

Controller层。 用户之后的所有同Controller or 同根访问路径操作都需要此token令牌才能进行。确保了用户的数据安全性独立性,也保证了服务器资源的合理利用。

@RestController
@RequestMapping("/xpq/user")
@Slf4j
public class UserController {

    @Resource
    private JwtProperties jwtProperties;

	@PostMapping("/login")
    @ApiOperation("用户登录")
    public Result<UserLoginVO> login(User user) {
        log.info("用户登录(邮箱):{}", user.getEmail());

		// 登录逻辑 throw new Exception("多种错误")
        // User u = userService.login(user);

        // 登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, u.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);
        log.info("jwt令牌:{}", token);

		// 返回给用户的数据,token密匙
        UserLoginVO userLoginVO = UserLoginVO.builder()
                .token(token)
                .build();

        return Result.success(userLoginVO);
    }
}

用户端得到如下数据

在这里插入图片描述

服务器返回如下

在这里插入图片描述

前端配置(基于Vue框架axios方法的使用)

  • 1、普通用法

挂起axios全局操作,需要配置main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios'

const app = createApp(App)

app.config.globalProperties.$http = axios
app.use(store).use(router)
app.mount('#app')

接收到jwt令牌

// 存储jwt令牌
sessionStorage.setItem('jwt', res.token)

具体连接下再定义headers

// 使用令牌,最好是在App.vue这种最上层的Vue里使用,之后就无需配置
this.$http.defaults.headers.common['token'] = sessionStorage.getItem('jwt')
  • 2、api.js全局访问文件

直接配置api文件,只要在此文件下定义访问方法,可以一劳永逸。

import axios from 'axios'
import axiosExtra from 'axios-extra'

// 服务器端口
const baseUrl = 'http://localhost:8010'

const http = axios.create({
    baseURL: baseUrl,
    // 配置令牌
    headers: { 'token': sessionStorage.getItem('jwt') }
})

const httpExtra = axiosExtra.create({
    maxConcurrent: 5, //并发为1
    queueOptions: {
        retry: 3, //请求失败时,最多会重试3次
        retryIsJump: false //是否立即重试, 否则将在请求队列尾部插入重试请求
    }
})

http.interceptors.response.use(response => {
    return response.data
})

/**
 * 获取用户文件信息
 * @param {*} url 
 * @returns 
 */
const queryFileInfo = (url) => {
    return http.post('/xpq/file/page', url)
}

export {
    queryFileInfo,
    httpExtra
}

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

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

相关文章

《智能新工厂规划白皮书》下 | “四步”规划智能工厂

中国制造业有着最大制造产能、最强配套能力和最大消费市场三个无可比拟的优势&#xff0c;随着产能升级&#xff0c;众企业的新工厂会开展智能工厂规划布局&#xff0c;从而实现降本减耗、提高效益的经营目标&#xff0c;以增强企业市场竞争力。 新工厂规划时&#xff0c;企业…

【GitHub已开源】某博热点事件评论数据分析与用户情感分析平台完整项目

找遍全网无奈只能自己开发某博热点评论数据爬取与用户情感分析平台&#xff0c;这就是技术人的创新&#xff01; 最近想看一下微博热点评论的用户人群情感趋势&#xff0c;想到的就是去爬取某博的评论数据&#xff0c;然后进行一个可视化的情感分析。想想吧&#xff0c;这个项目…

RPC核心原理

大家好&#xff0c;我是易安&#xff0c;今天我们一起来研究下RPC的核心原理。 什么是RPC&#xff1f; RPC的全称是Remote Procedure Call&#xff0c;即远程过程调用。简单解读字面上的意思&#xff0c;远程肯定是指要跨机器而非本机&#xff0c;所以需要用到网络编程才能实现…

用Gmail邮箱注册任天堂日本区账号

任天堂是一家日本公司&#xff0c;日区的任天堂可以买到常驻的任亏券&#xff0c;兑换任天堂第一方游戏&#xff0c;而且经常搞活动&#xff0c;可以买到低价的游戏。 首先进入任天堂官网 https://accounts.nintendo.com/register 注册账号 比如我的Gmail邮箱为 zhaooleegma…

EBU6304 Software Engineering 知识点总结_3 requirements

requirements 确定需求是软工设计中最重要的部分。 feature to satisfy customer. indicates what should this sys do. 可能是高层抽象的需求 high-level abstract 或者底层具体的 low-level specific. Stakeholder 利益相关者&#xff1a;受系统影响的组织或个人&#x…

STM32驱动W25Q64---笔记

这次来分享最近经常用到的知识点----FLASH 初学者会疑惑&#xff0c;有了EEPROM为什么还要用W25Q64呢&#xff1f;&#xff08;笔者一开始就百思不得其解&#xff09; 答&#xff1a; EEPROM和W25Q64都是用于数据存储的存储器&#xff0c;它们各有优缺点&#xff0c;需要根据…

chatgpt赋能python:Python的修改及其对SEO的影响

Python的修改及其对SEO的影响 介绍 Python是一种高级编程语言&#xff0c;以其简单易学和功能强大而闻名。它被广泛用于开发各种应用程序&#xff0c;从网站到机器学习和大数据分析。Python不断更新和改进&#xff0c;新版本带来了许多新功能和改进&#xff0c;这些修改对SEO…

如何读取带空格的字符串?

scanf()函数在读取字符时&#xff0c;识别到空格就会终止读取&#xff0c;那么如何读取带空格的字符串呢&#xff1f; 一、gets()&#xff08;gets_s()&#xff09; 从标准输入(stdin)&#xff08;指的是键盘输入&#xff09;读取字符&#xff0c;并将它们作为 C 字符串存储到…

1688详情 sign签名分析

本文仅供学习交流&#xff0c;只提供关键思路不会给出完整代码&#xff0c;严禁用于非法用途&#xff0c;若有侵权请联系我删除&#xff01; 网站地址&#xff1a;aHR0cHM6Ly9kZXRhaWwuMTY4OC5jb20vb2ZmZXIvNzEzNDMzMDYyOTUzLmh0bWw 接口&#xff1a;aHR0cHM6Ly9oNWFwaS5tLjE…

软件工程学复习笔记

目录 软件工程学概述软件危机的典型表现、产生原因、消除途径软件的构成&#xff1a;程序、数据、文档软件工程的七点特性软件工程的七条基本原理软件工程方法&#xff1a;传统方法学&#xff0c;面向对象方法学软件的生命周期&#xff1a;三个时期&#xff0c;软件定义&#x…

MMPose学习笔记1

文章目录 摘要什么是人体姿态估计3D 姿态估计人体参数化模型下游任务2D姿态估计多人姿态估计&#xff1a;自顶向下方法基于回归的自顶向下方法基于热力图的自顶向下方法 多人姿态估计&#xff1a;自底向上方法单阶段方法基于Transformer的方法小结 3D姿态估计评估指标 Dense Po…

面向对象特征之一:封装和隐藏

为什么要引入封装性&#xff1f; ●我们程序设计追求“高内聚&#xff0c;低耦合” ➢高内聚:类的内部数据操作细节自己完成&#xff0c;不允许外部干涉; ➢低耦合:仅对外暴露少量的方法用于使用。 ●隐藏对象内部的复杂性&#xff0c;只对外公开简单的接口。便于外界调用&am…

ISIS路由渗透实验

1&#xff09;拓扑 2&#xff09;需求&#xff1a;ISIS全网互联互通 3&#xff09;原因分析&#xff1a; 因为&#xff0c;L1/2 路由器&#xff08;R4、R8&#xff09;学习到L1类型路由信息会装进L2-LSP&#xff0c;在泛洪给其他区域的L2和L1/2路由器&#xff0c;所以&#x…

【socket】从计算机网络基础到socket编程——Windows Linux C语言 + Python实现(TCP+UDP)

一、部分基础知识1.1 计算机网络的体系结构1.11 互联网简介1.12 计算机网络的分类1.13 协议与网络的分层体系结构▶ 协议▶ 网络的分层体系结构 1.14 OSI 七层模型&#xff08;重要&#xff09;▶ OSI 模型的结构▶ OSI 模型各层的功能 1.15 TCP/IP 的体系结构&#xff08;重要…

linux服务器彻底清除xmrig挖矿病毒

不想看前面的内容可直接进入第三点看解决方案。 一&#xff0c;事件起因 二&#xff0c;检查过程 三&#xff0c;解决方案 1&#xff0c;找到病毒文件 2 &#xff0c;杀死病毒进程&#xff0c;删除病毒文件 3&#xff0c;查看linux服务器上的定时任务 4&#xff0c;最后&…

【MySQL新手到通关】第七章 聚合函数使用详解

文章目录 0. 前置1. 聚合函数介绍1.1 AVG 和 SUM 函数1.2 MIN 和 MAX 函数1.3 COUNT函数 2. GROUP BY2.1 基本使用 3. HAVING3.1 基本使用3.2 WHERE和HAVING的对比 0. 前置 为了方便测试&#xff0c;我们导入一些数据 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ------…

编程(39)----------多线程中的锁

假设一个这样的场景: 在多线程的代码中, 需要在不同的线程中对同一个变量进行操作. 那此时就会出现问题: 多线程是并发进行的, 也就是说代码运行的时候, 俩个线程会同时对一个变量进行操作, 这样就会涉及到多线程的安全问题: class Counter{public int count;public void add…

RPC核心原理(整体架构/调用过程)

Server: Provider ,暴露服务,服务提供方 Client: Consumer ,服务消费,调用远程服务 Registry:服务注册与发现 RPC的调用过程如下&#xff1a; 第一步&#xff1a;server会将他需要暴露的服务以及他的地址信息注册到Registry这一注册中心。 第二步&#xff1a;client通过注册…

【VScode】ESLint :warning Delete `CR` prettier/prettier

一. ESLint 作用 检查 Javascript 编程时的语法错误。 新建或修改文件时报错 原因 Windows系统 &#xff0c;clone的代码会自动把换行符 LF转为回车符CRLF&#xff0c;这时本地的代码都是回车符。可在prettier.config.js中查看到 检查配置&#xff08;ESLint中是…

linux(system V标准)信号量

目录&#xff1a; 1.什么是信号量 2.信号量的本质 1.什么是信号量 2.信号量的本质 什么是临界资源呢?? 凡是倍多个执行流同时访问的资源就是临界资源&#xff01;&#xff01;&#xff01; 我们看一个问题&#xff0c;我们fork&#xff08;&#xff09;之后创建一个子进程&a…