当前位置: 首页 > article >正文

Spring Boot项目中成功集成了JWT

JWT 原理解释

什么是 JWT?

JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在网络应用环境间安全地将信息作为JSON对象传输。JWT通常用于身份验证和信息交换。

JWT 的结构

JWT由三部分组成,以点.分隔:

  1. Header(头部):包含令牌的类型(typ)和签名算法(alg)。
  2. Payload(负载):包含声明(claims),即实体(通常是用户)和其他数据。
  3. Signature(签名):通过将Base64Url编码的Header和Payload连接起来,并使用密钥进行签名得到。
生成与验证流程
  1. 生成JWT

    • 构建Header和Payload。
    • 使用指定的签名算法和密钥生成签名。
    • 将Header、Payload和Signature拼接成JWT字符串。
  2. 验证JWT

    • 解码Header和Payload。
    • 根据Header中的签名算法和提供的密钥重新计算签名。
    • 比较新计算的签名与JWT中的签名是否一致。

实现步骤

以下是详细的代码实现步骤,包括生成密钥、配置JWT、创建工具类和控制器,并加入JWT自动续期功能。

1. 准备工作

添加依赖

首先,在 pom.xml 中添加 com.auth0java-jwt 依赖:

<dependencies><!-- Spring Boot Starter Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Auth0 JWT Library --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.10.0</version></dependency>
</dependencies>

2. 生成密钥文件

对称密钥(HMAC)

我们可以使用 Java 代码生成一个对称密钥并保存到文件中:

import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;public class KeyGenerator {public static void main(String[] args) throws Exception {// 定义一个32字节的数组用于存储密钥byte[] secretKey = new byte[32];// 使用强随机数生成器填充密钥数组SecureRandom.getInstanceStrong().nextBytes(secretKey);// 将生成的密钥写入名为 jwt_secret.key 的文件Files.write(Paths.get("jwt_secret.key"), secretKey);// 输出提示信息System.out.println("密钥已生成并保存到 jwt_secret.key 文件中");}
}
非对称密钥(RSA)

使用 OpenSSL 生成 RSA 密钥对,并将其保存到文件中:

# 生成私钥
openssl genpkey -algorithm RSA -out private.key -aes256 -pass pass:your_password -pkeyopt rsa_keygen_bits:2048# 从私钥中提取公钥
openssl rsa -pubout -in private.key -out public.key -passin pass:your_password

3. 在Spring Boot中集成JWT

创建配置类

加载密钥文件并配置 JWT 相关的参数。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;@Configuration
public class JwtConfig {/*** 读取对称密钥文件* @return 密钥字符串* @throws IOException 如果读取文件失败*/@Beanpublic String getSecretKey() throws IOException {// 从 jwt_secret.key 文件读取密钥字节数组byte[] keyBytes = Files.readAllBytes(Paths.get("jwt_secret.key"));// 使用Base64编码将字节数组转换为字符串return Base64.getEncoder().encodeToString(keyBytes);}/*** 读取私钥文件* @return 私钥对象* @throws Exception 如果解析私钥失败*/@Beanpublic PrivateKey getPrivateKey() throws Exception {// 从 private.key 文件读取私钥字节数组byte[] keyBytes = Files.readAllBytes(Paths.get("private.key"));// 创建PKCS8EncodedKeySpec对象PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);// 获取RSA算法的KeyFactory实例KeyFactory kf = KeyFactory.getInstance("RSA");// 生成私钥对象return kf.generatePrivate(spec);}/*** 读取公钥文件* @return 公钥对象* @throws Exception 如果解析公钥失败*/@Beanpublic PublicKey getPublicKey() throws Exception {// 从 public.key 文件读取公钥字节数组byte[] keyBytes = Files.readAllBytes(Paths.get("public.key"));// 创建X509EncodedKeySpec对象X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);// 获取RSA算法的KeyFactory实例KeyFactory kf = KeyFactory.getInstance("RSA");// 生成公钥对象return kf.generatePublic(spec);}
}
创建JWT工具类

实现 JWT 的生成、验证和自动续期功能。

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Date;@Component
public class JwtUtil {@Autowiredprivate String secretKey; // 对称密钥@Autowiredprivate PrivateKey privateKey; // 私钥@Autowiredprivate PublicKey publicKey; // 公钥private static final long EXPIRATION_TIME = 1000 * 60 * 60; // 1小时private static final long REFRESH_THRESHOLD = 1000 * 60 * 55; // 55分钟/*** 使用对称密钥生成JWT* @param username 用户名* @return 生成的JWT字符串*/public String generateToken(String username) {// 使用对称密钥创建算法实例Algorithm algorithm = Algorithm.HMAC256(secretKey);// 创建JWT构建器return JWT.create()// 设置主题为用户名.withSubject(username)// 设置签发时间.withIssuedAt(new Date())// 设置过期时间为1小时后.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))// 使用指定算法签名并生成JWT字符串.sign(algorithm);}/*** 使用私钥生成JWT* @param username 用户名* @return 生成的JWT字符串*/public String generateTokenWithRsa(String username) {// 使用RSA密钥创建算法实例Algorithm algorithm = Algorithm.RSA256((com.auth0.jwt.impl.crypto.RSAPublicKey) publicKey, (com.auth0.jwt.impl.crypto.RSAPrivateKey) privateKey);// 创建JWT构建器return JWT.create()// 设置主题为用户名.withSubject(username)// 设置签发时间.withIssuedAt(new Date())// 设置过期时间为1小时后.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))// 使用指定算法签名并生成JWT字符串.sign(algorithm);}/*** 验证JWT(使用对称密钥)* @param token 要验证的JWT字符串* @return 解析后的DecodedJWT对象*/public DecodedJWT validateToken(String token) {// 使用对称密钥创建算法实例Algorithm algorithm = Algorithm.HMAC256(secretKey);// 创建验证器并构建验证器return JWT.require(algorithm).build()// 验证并返回解码后的JWT.verify(token);}/*** 使用公钥验证JWT* @param token 要验证的JWT字符串* @return 解析后的DecodedJWT对象*/public DecodedJWT validateTokenWithRsa(String token) {// 使用公钥创建算法实例Algorithm algorithm = Algorithm.RSA256((com.auth0.jwt.impl.crypto.RSAPublicKey) publicKey, null);// 创建验证器并构建验证器return JWT.require(algorithm).build()// 验证并返回解码后的JWT.verify(token);}/*** 判断JWT是否需要刷新* @param decodedJWT 解码后的JWT* @return 是否需要刷新*/public boolean shouldRefresh(DecodedJWT decodedJWT) {// 获取JWT的过期时间Date expiresAt = decodedJWT.getExpiresAt();// 计算当前时间和过期时间的差值long timeLeft = expiresAt.getTime() - System.currentTimeMillis();// 如果剩余时间小于刷新阈值,则返回true,表示需要刷新return timeLeft <= REFRESH_THRESHOLD;}/*** 刷新JWT* @param username 用户名* @return 新的JWT字符串*/public String refreshToken(String username) {// 使用对称密钥创建算法实例Algorithm algorithm = Algorithm.HMAC256(secretKey);// 创建JWT构建器return JWT.create()// 设置主题为用户名.withSubject(username)// 设置签发时间.withIssuedAt(new Date())// 设置过期时间为1小时后.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))// 使用指定算法签名并生成JWT字符串.sign(algorithm);}
}
创建过滤器

在请求到达控制器之前检查JWT的有效性,并根据需要刷新JWT。

import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtil jwtUtil;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 从请求头中获取Authorization字段String header = request.getHeader("Authorization");if (header == null || !header.startsWith("Bearer ")) {// 如果没有Authorization字段或格式不正确,直接放行filterChain.doFilter(request, response);return;}try {// 提取JWT字符串String token = header.substring(7);// 验证JWTDecodedJWT decodedJWT = jwtUtil.validateToken(token);// 判断是否需要刷新JWTif (jwtUtil.shouldRefresh(decodedJWT)) {// 获取用户名String username = decodedJWT.getSubject();// 刷新JWTString newToken = jwtUtil.refreshToken(username);// 将新的JWT放入响应头response.setHeader("New-Token", newToken);}// 放行请求filterChain.doFilter(request, response);} catch (Exception e) {// 处理异常情况,如无效的JWTresponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("Invalid JWT");}}
}

创建控制器

提供 API 接口来测试 JWT 的生成、验证和刷新功能。

import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api")
public class JwtController {@Autowiredprivate JwtUtil jwtUtil;/*** 生成JWT(使用对称密钥)* @param username 用户名* @return 生成的JWT字符串*/@GetMapping("/generate-token")public String generateToken(@RequestParam String username) {// 调用JwtUtil生成JWTreturn jwtUtil.generateToken(username);}/*** 使用私钥生成JWT* @param username 用户名* @return 生成的JWT字符串*/@GetMapping("/generate-token-rsa")public String generateTokenWithRsa(@RequestParam String username) {// 调用JwtUtil使用私钥生成JWTreturn jwtUtil.generateTokenWithRsa(username);}/*** 验证JWT(使用对称密钥)* @param request 包含要验证的JWT字符串的请求体* @return 解析后的DecodedJWT对象*/@PostMapping("/validate-token")public DecodedJWT validateToken(@RequestBody TokenRequest request) {// 调用JwtUtil验证JWTreturn jwtUtil.validateToken(request.getToken());}/*** 使用公钥验证JWT* @param request 包含要验证的JWT字符串的请求体* @return 解析后的DecodedJWT对象*/@PostMapping("/validate-token-rsa")public DecodedJWT validateTokenWithRsa(@RequestBody TokenRequest request) {// 调用JwtUtil使用公钥验证JWTreturn jwtUtil.validateTokenWithRsa(request.getToken());}
}/*** 请求体类,用于接收包含JWT字符串的请求*/
class TokenRequest {private String token;public String getToken() {// 返回token字段值return token;}public void setToken(String token) {// 设置token字段值this.token = token;}
}

4. 完整的Spring Boot启动类

为了完整性,这里是一个简单的 Spring Boot 启动类,并注册过滤器:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;@SpringBootApplication
public class JwtDemoApplication {public static void main(String[] args) {// 运行Spring Boot应用SpringApplication.run(JwtDemoApplication.class, args);}@Beanpublic FilterRegistrationBean<JwtAuthenticationFilter> loggingFilter(){FilterRegistrationBean<JwtAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();registrationBean.setFilter(new JwtAuthenticationFilter());registrationBean.addUrlPatterns("/api/*");return registrationBean;}
}

测试API

生成JWT
  • 使用对称密钥生成JWT

    GET /api/generate-token?username=john_doe
  • 使用私钥生成JWT

    GET /api/generate-token-rsa?username=john_doe
验证JWT
  • 使用对称密钥验证JWT

    POST /api/validate-token
    Content-Type: application/json
    {"token": "your_jwt_token"
    }
  • 使用公钥验证JWT

    POST /api/validate-token-rsa
    Content-Type: application/json
    {"token": "your_jwt_token"
    }
自动续期

当JWT接近过期时(例如剩余有效期少于55分钟),过滤器会在响应头中返回一个新的JWT,客户端可以从中获取新的JWT并更新本地存储的JWT。

自动续期原理

工作流程
  1. 检查JWT有效期:每次请求到来时,过滤器会从请求头中提取JWT,并解析出其有效期。
  2. 判断是否需要续期:如果JWT的有效期剩余时间小于设定的阈值(例如55分钟),则认为该JWT即将过期,需要续期。
  3. 生成新JWT:调用 JwtUtil 类中的 refreshToken 方法生成一个新的JWT,并将其放入响应头中返回给客户端。
  4. 更新客户端JWT:客户端接收到带有新JWT的响应后,更新本地存储的JWT。
关键代码解释
判断是否需要续期
public boolean shouldRefresh(DecodedJWT decodedJWT) {// 获取JWT的过期时间Date expiresAt = decodedJWT.getExpiresAt();// 计算当前时间和过期时间的差值long timeLeft = expiresAt.getTime() - System.currentTimeMillis();// 如果剩余时间小于刷新阈值,则返回true,表示需要刷新return timeLeft <= REFRESH_THRESHOLD;
}

此方法计算JWT的剩余有效时间,如果剩余时间小于设定的阈值(如55分钟),则返回 true 表示需要刷新。

刷新JWT
public String refreshToken(String username) {// 使用对称密钥创建算法实例Algorithm algorithm = Algorithm.HMAC256(secretKey);// 创建JWT构建器return JWT.create()// 设置主题为用户名.withSubject(username)// 设置签发时间.withIssuedAt(new Date())// 设置过期时间为1小时后.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))// 使用指定算法签名并生成JWT字符串.sign(algorithm);
}

此方法重新生成一个新的JWT,并设置其过期时间为1小时后。客户端在收到新的JWT后应更新本地存储的JWT。

过滤器处理逻辑
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 从请求头中获取Authorization字段String header = request.getHeader("Authorization");if (header == null || !header.startsWith("Bearer ")) {// 如果没有Authorization字段或格式不正确,直接放行filterChain.doFilter(request, response);return;}try {// 提取JWT字符串String token = header.substring(7);// 验证JWTDecodedJWT decodedJWT = jwtUtil.validateToken(token);// 判断是否需要刷新JWTif (jwtUtil.shouldRefresh(decodedJWT)) {// 获取用户名String username = decodedJWT.getSubject();// 刷新JWTString newToken = jwtUtil.refreshToken(username);// 将新的JWT放入响应头response.setHeader("New-Token", newToken);}// 放行请求filterChain.doFilter(request, response);} catch (Exception e) {// 处理异常情况,如无效的JWTresponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("Invalid JWT");}
}

此过滤器负责在每个请求到来时检查JWT的有效性,并在需要时刷新JWT。

注意事项

  1. 密钥管理

    • 对称密钥应妥善保管,避免泄露。
    • 非对称密钥中的私钥应严格保密,公钥可以公开。
  2. 安全性

    • 设置合理的过期时间,防止长时间有效的令牌被滥用。
    • 使用HTTPS传输JWT,防止中间人攻击。
  3. 错误处理

    • 在验证JWT时捕获异常,处理无效或过期的令牌。
  4. 性能优化

    • 缓存解析后的Claims对象,减少重复解析开销。

通过上述步骤,我们已经在Spring Boot项目中成功集成了JWT,并实现了自动续期功能。

相关文章:

Spring Boot项目中成功集成了JWT

JWT 原理解释 什么是 JWT&#xff1f; JSON Web Token&#xff08;JWT&#xff09;是一种开放标准&#xff08;RFC 7519&#xff09;&#xff0c;用于在网络应用环境间安全地将信息作为JSON对象传输。JWT通常用于身份验证和信息交换。 JWT 的结构 JWT由三部分组成&#xff…...

DeepSeek 3FS集群化部署临时笔记

DeepSeek 3FS集群化部署临时笔记 一、3FS集群化部署1、环境介绍2、对应的软件包安装3、编译4、部署4.1 部署monitor_collector_mainStep 2: Admin clientStep 3: Mgmtd serviceStep 4: Meta serviceStep 5: Storage serviceStep 6: Create admin user, storage targets and cha…...

专题|Python贝叶斯金融数据应用实例合集:随机波动率SV模型、逻辑回归、参数更新、绩效比较BEST分析亚马逊股票、普尔指数...

原文链接&#xff1a;https://tecdat.cn/?p41020 本专题合集系统梳理了贝叶斯方法在金融数据分析与分类建模中的前沿应用。合集聚焦于PyMC3概率编程框架&#xff0c;深度探讨了共轭先验参数更新、贝叶斯逻辑回归、贝叶斯夏普比率等核心算法在实际场景中的落地实践&#xff08;…...

RocketMQ企业应用篇

在现代企业级应用中&#xff0c;分布式消息队列系统如RocketMQ发挥着至关重要的作用。本文将深入探讨RocketMQ在电商和物联网场景中的应用&#xff0c;结合实际案例和代码示例&#xff0c;展示如何利用RocketMQ解决企业级应用中的关键问题。 一、电商场景应用 1. 秒杀抢购解决…...

vue-常用指令 | 常用指令的修饰符

目录 什么是vue指令 v-cloak v-text v-html v-pre v-show /v-if v-else/v-else-if v-on v-bind v-for v-model 常用指令的修饰符 v-model 指令修饰符 事件修饰符 按键修饰符 什么是vue指令 指令就是带有 v- 前缀 的特殊 属性&#xff0c;不同的属性对应不…...

Git提交前时间检查

为了防止在本地看日志的时候&#xff0c;由于本地时间被修改&#xff0c;导致日志的时间存在非正确时间。通过以下脚本在提交前进行时间验证&#xff0c;只有是正确的时间才可以提交。 使用方法如下&#xff1a; 复制如下脚本&#xff0c;命名为 pre-commit &#xff0c;放到 …...

Linux调度器 --- 负载均衡的存在的问题

文章目录 前言一、简介二、Linux 调度器2.1 在单核系统上&#xff0c;CFS 非常简单2.2 在多核系统上&#xff0c;CFS 变得非常复杂2.2.1 负载均衡算法2.2.2 优化措施 三、Linux调度器负载均衡的存在的问题3.1 组负载不均衡问题&#xff08;Group Imbalance Bug&#xff09;3.2 …...

从零开始用AI开发游戏(三)背景故事

《迷域回响》背景故事 第一章&#xff1a;失落的符文纪元 在远古的“艾瑟兰”大陆&#xff0c;掌握空间魔法的「筑界者文明」曾建造了连通万界的回响迷宫——这座迷宫既是试炼场&#xff0c;也是囚笼。文明巅峰时期&#xff0c;筑界者将禁忌知识刻入虚空符文&#xff0c;嵌于…...

IXTUR气控永磁铁:以高精度气控和稳定磁场,为机器人应用提供稳定抓取力

在现代工业生产和物流领域&#xff0c;物料的抓取与搬运是影响生产效率和成本控制的重要环节。传统夹爪在面对不同材质、形状和重量的物体时&#xff0c;常常存在适应性差、抓取不稳定、操作复杂等问题&#xff0c;导致生产流程中频繁出现停机调整&#xff0c;增加了人工干预成…...

硬件驱动——51单片机:寄存器、LED、动态数码管

目录 一、51单片机 1.寄存器 二、LED点灯 1.原理 2.封装函数 3.顺序点灯 4.特定位点灯 三、动态数码管 1.原理 2.封装函数 3.0~9跳变 4.顺序移位0~9跳变 一、51单片机 1.寄存器 51单片机共40个引脚&#xff0c;其中P0,P1,P2,P3是四个有8引脚的寄存器&#xff0…...

2025 香港 Web3 嘉年华:全球 Web3 生态的年度盛会

自 2023 年首届香港 Web3 嘉年华成功举办以来&#xff0c;这一盛会已成为全球 Web3 领域规模最大、影响力最深远的行业活动之一。2025 年 4 月 6 日至 9 日&#xff0c;第三届香港 Web3 嘉年华将在香港盛大举行。本届活动由万向区块链实验室与 HashKey Group 联合主办、W3ME 承…...

【MySQL】多表查询(笛卡尔积现象,联合查询、内连接、左外连接、右外连接、子查询)-通过练习快速掌握法

在DQL的基础查询中&#xff0c;我们已经学过了多表查询的一种&#xff1a;联合查询&#xff08;union&#xff09;。本文我们将系统的讲解多表查询。 笛卡尔积现象 首先&#xff0c;我们想要查询emp表和stu表两个表&#xff0c;按照我们之前的知识栈&#xff0c;我们直接使用…...

Leetcode-132.Palindrome Partitioning II [C++][Java]

目录 一、题目描述 二、解题思路 【C】 【Java】 Leetcode-132.Palindrome Partitioning IIhttps://leetcode.com/problems/palindrome-partitioning-ii/description/132. 分割回文串 II - 力扣&#xff08;LeetCode&#xff09;132. 分割回文串 II - 给你一个字符串 s&…...

在 macOS 上优化 Vim 用于开发

简介 这篇指南将带你通过一系列步骤&#xff0c;如何在 macOS 上优化 Vim&#xff0c;使其具备 代码补全、语法高亮、代码格式化、代码片段管理、目录树等功能。此外&#xff0c;我们还会解决在安装过程中可能遇到的常见错误。 1. 安装必备工具 在开始 Vim 配置之前&#xff…...

SOME/IP-SD -- 协议英文原文讲解8

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.4.4 S…...

【Agent实战】货物上架位置推荐助手(RAG方式+结构化prompt(CoT)+API工具结合ChatGPT4o能力Agent项目实践)

本文原创作者:姚瑞南 AI-agent 大模型运营专家,先后任职于美团、猎聘等中大厂AI训练专家和智能运营专家岗;多年人工智能行业智能产品运营及大模型落地经验,拥有AI外呼方向国家专利与PMP项目管理证书。(转载需经授权) 目录 结论 效果图示 1.prompt 2. API工具封…...

Spring Boot使用线程池创建多线程

在 Spring Boot 2 中&#xff0c;可以使用 Autowired 注入 线程池&#xff08;ThreadPoolTaskExecutor 或 ExecutorService&#xff09;&#xff0c;从而管理线程的创建和执行。以下是使用 Autowired 方式注入线程池的完整示例。 1. 通过 Autowired 注入 ThreadPoolTaskExecuto…...

PyTorch 深度学习实战(11):强化学习与深度 Q 网络(DQN)

在之前的文章中&#xff0c;我们介绍了神经网络、卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;、Transformer 等多种深度学习模型&#xff0c;并应用于图像分类、文本分类、时间序列预测等任务。本文将介绍强化学习的基本概念&#xff0…...

在Eclipse 中使用 MyBatis 进行开发,通常需要以下步骤:

在Eclipse 中使用 MyBatis 进行开发&#xff0c;通常需要以下步骤&#xff1a; 1. 创建 Maven 项目 首先&#xff0c;在 Eclipse 中创建一个 Maven 项目。如果你还没有安装 Maven 插件&#xff0c;可以通过 Eclipse Marketplace 安装 Maven 插件。 打开 Eclipse&#xff0c;选…...

Python学习第十九天

Django-分页 后端分页 Django提供了Paginator类来实现后端分页。Paginator类可以将一个查询集&#xff08;QuerySet&#xff09;分成多个页面&#xff0c;每个页面包含指定数量的对象。 from django.shortcuts import render, redirect, get_object_or_404 from .models impo…...

Adobe Premiere Pro2023配置要求

Windows 系统 最低配置 处理器&#xff1a;Intel 第六代或更新版本的 CPU&#xff0c;或 AMD Ryzen™ 1000 系列或更新版本的 CPU&#xff0c;需要支持 Advanced Vector Extensions 2&#xff08;AVX2&#xff09;。操作系统&#xff1a;Windows 10&#xff08;64 位&#xff…...

面试求助:接口测试用例设计主要考虑哪些方面?

一、基础功能验证 1. 正常场景覆盖 关键点&#xff1a;验证接口在合法输入下的正确响应&#xff08;状态码、数据结构、业务逻辑&#xff09;。 案例&#xff1a; json 复制 // 用户登录接口 输入&#xff1a;{"username": "合法用户", "password…...

C语言——变量与常量

C语言中的变量与常量&#xff1a;简洁易懂的指南 在C语言编程中&#xff0c;变量和常量是最基本的概念之一。理解它们的区别和使用方法对于编写高效、可维护的代码至关重要。本文将详细介绍C语言中的变量和常量&#xff0c;并通过图表和代码示例帮助你更好地理解。 目录 什么…...

考研408-数据结构完整代码 线性表的顺序存储结构 - 顺序表

线性表的顺序存储结构 - 顺序表 1. 顺序表的定义 ​ 用一组地址连续的存储单元依次存储线性表的数据元素&#xff0c;从而使逻辑上相邻的两个元素在物理位置上也相邻 2. 顺序表的特点 随机访问&#xff1a; 即通过首地址和元素序号可以在O(1) 时间内找到指定元素&#xff0…...

Windows环境下安装部署dzzoffice+onlyoffice的私有网盘和在线协同系统

安装前需要准备好Docker Desktop环境&#xff0c;可查看我的另一份亲测安装文章 https://blog.csdn.net/qq_43003203/article/details/146283915?spm1001.2014.3001.5501 1、安装配置onlyoffice 1、Docker 拉取onlyoffice容器镜像 管理员身份运行Windows PowerShell&#x…...

解决 Docker 镜像拉取超时问题:配置国内镜像源

在使用 Docker 的过程中&#xff0c;经常会遇到镜像拉取超时的问题&#xff0c;尤其是在国内网络环境下。这不仅会浪费大量的时间&#xff0c;还可能导致一些项目无法顺利进行。今天&#xff0c;我将分享一个简单而有效的解决方法&#xff1a;配置国内镜像源。 环境 操作系统 c…...

如何解决 Three.js 物体渲染的锯齿问题

在 Three.js 中&#xff0c;如果模型看起来不够平滑&#xff0c;或者在旋转视角时出现锯齿&#xff08;aliasing&#xff09;&#xff0c;可以通过以下方法来优化渲染效果。 1. 启用抗锯齿&#xff08;MSAA&#xff09; 默认情况下&#xff0c;Three.js 渲染器不会开启抗锯齿&…...

嵌入式八股,为什么单片机中不使用malloc函数

1. 资源限制 单片机的内存资源通常非常有限&#xff0c;尤其是RAM的大小可能只有几KB到几十KB。在这种情况下&#xff0c;使用 malloc 进行动态内存分配可能会导致内存碎片化&#xff0c;使得程序在运行过程中逐渐耗尽可用内存。 2. 内存碎片问题 malloc 函数在分配和释放内…...

【计量地理学】实验一 地理数据的基本统计分析

阅前提示&#xff1a; 计量地理学实验课的实验报告为当堂提交&#xff0c;相较以往实验报告缺少打磨与整理的时间&#xff0c;因此内容中不可避免出现相关错误&#xff01;&#xff01;&#xff01; 出于个人完美主义的原则本不愿发布&#xff08;其实就是黑历史&#xff09;…...

ChatPromptTemplate的使用

ChatPromptTemplate 是 LangChain 中专门用于管理多角色对话结构的提示词模板工具。它的核心价值在于&#xff0c;开发者可以预先定义不同类型的对话角色消息&#xff08;如系统指令、用户提问、AI历史回复&#xff09;&#xff0c;并通过数据绑定动态生成完整对话上下文。 1.…...