JWT全面理解

        

目录

一、JWT是什么

1、身份认证(最核心用途)

2、信息交换

3、授权控制

二、JWT的核心价值

三、如何理解JWT的结构和工作原理

1、三部分结构解析

2、核心工作流程

四、JWT的使用步骤

1、添加依赖

2、添加配置文件

3、创建实体类

4、创建JWT工具类

5、创建JWT认证拦截器

7、配置拦截器和Redis

8、创建服务层

9、创建控制器

五、核心实现

六、核心知识点的回答

1、生成token时如何设置过期时间

2、JWT有哪些安全隐患?如何解决

3、JWT 和 Session 的区别是什么?​


JWT是一种基于JSON的轻量级身份认证与信息交换标准,广泛应用于前后端分离、分布式系统、跨服务通信等场景。它通过数字签名保证信息的完整性和可靠性,核心价值在于解决了传统认证方案(如 Session)在跨域、分布式架构下的局限性。

一、JWT是什么

        JWT 的本质是一串经过加密签名的 JSON 格式字符串,用于在客户端和服务器之间安全地传递 “声明”(Claims,即用户身份、权限等信息)。其核心作用可概括为三类:

1、身份认证(最核心用途)

        解决 “用户是谁” 的问题,替代传统的 Session 认证,流程如下:

  • 用户通过账号密码登录,服务器验证通过后,生成包含用户唯一标识(如 user_id)、过期时间等信息的 JWT;
  • 服务器将 JWT 直接返回给客户端(无需存储在服务器);
  • 后续客户端发起请求时,在 HTTP 头(如 Authorization: Bearer <JWT>)中携带 JWT
  • 服务器接收请求后,验证 JWT 的签名和有效性(如是否过期、是否被篡改),验证通过即可确认用户身份,无需查询数据库或缓存。
2、信息交换

        解决 “安全传递数据” 的问题。由于 JWT 带有数字签名,接收方可以通过签名验证信息是否来自合法发送方,且未被篡改。

        分布式系统中服务间通信(如微服务 A 向微服务 B 传递用户权限信息)、跨组织数据交换(如第三方登录后,平台间传递用户基本信息)。

3、授权控制

        解决 “用户能做什么” 的问题。可在 JWT 中嵌入用户权限信息(如 role: admin、permissions: ["read", "write"]),服务器验证 JWT 后,直接从载荷中读取权限,无需额外查询权限数据库。

        后台管理系统中,根据 JWT 中的 role 字段判断用户是否有权访问某接口(如仅管理员可调用 “删除用户” 接口)。

二、JWT的核心价值

对比传统的 Session 认证(服务器存储用户会话,客户端保存 Session ID),JWT 的价值主要体现在以下 4 点:

特性

JWT 方案

传统 Session 方案

价值体现

无状态(Stateless)

服务器不存储任何会话信息,仅通过 JWT 验证身份

服务器需存储 Session(内存 / 数据库 / Redis)

减轻服务器存储压力,支持高并发;无需考虑 Session 同步(分布式系统友好)

跨域 / 跨服务

基于 HTTP 头传递,支持跨域名、跨服务通信

依赖 Cookie,受 “同源策略” 限制,跨域需额外配置

适配前后端分离(前端部署在 CDN,后端在另一域名)、微服务架构

轻量化

字符串格式,体积小,传输速度快

需携带 Session ID,且服务器需查询 Session 详情

减少网络传输开销,提升接口响应速度(尤其移动端弱网场景)

自包含(Self-contained)

载荷中包含用户核心信息(如 ID、权限)

仅存储 Session ID,需查询数据库获取用户信息

减少数据库查询次数(验证身份时无需查库),提升系统性能

三、如何理解JWT的结构和工作原理

        JWT 由三部分组成,用英文句号(.)分隔,格式为 Header.Payload.Signature,例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJ1c2VyX2lkIjoiMTIzIiwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImV4cCI6MTY4OTAxNjQwMH0.

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1、三部分结构解析
  • Header(头部):声明 JWT 的类型(typ,固定为 JWT)和使用的签名算法(alg,如 HS256 哈希算法、RS256 非对称加密算法)。格式是JSON字符串,是可逆的,仅编码而非加密。
  • Payload(载荷):存储核心 “声明”(用户信息、过期时间等)。JSON 字符串,同样经过 Base64 编码(可逆!不要存储密码、手机号等敏感信息)。
  • Signature(签名):保证 JWT 不被篡改,是 JWT 安全性的核心。拼接编码后的 Header 和 Payload,使用 Header 中声明的算法(如 HS256),结合服务器端的密钥(Secret) 对拼接字符串进行加密,得到签名。
2、核心工作流程
  • 签发:用户登录成功 → 服务器根据用户信息构建 Payload → 结合 Header 密钥生成 Signature → 拼接三部分得到 JWT 并返回给客户端;
  • 存储:客户端将 JWT 存储在 localStorage、sessionStorage 或 Cookie 中;
  • 携带:客户端发起请求时,在 Authorization 头中携带 JWT(Bearer <JWT>);
  • 验证:服务器解析 JWT → 验证签名(防篡改)→ 检查过期时间(exp)→ 提取用户信息 → 处理业务逻辑。

四、JWT的使用步骤

例如我现在实现用户登录的功能,我要通过JWT进行安全认证的步骤如下:

1、添加依赖
 <dependencies><!-- Spring Boot Web 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- JWT 工具库 --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><!-- Redis 依赖 (用于存储刷新令牌) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Lombok 简化代码 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies>
2、添加配置文件
spring:redis:host: localhostport: 6379# JWT配置
jwt:secret: your-secret-key-1234567890-abcdefg # 密钥,生产环境要复杂且保密access-token-expire: 3600000  # 访问令牌过期时间(毫秒),1小时refresh-token-expire: 604800000  # 刷新令牌过期时间(毫秒),7天
3、创建实体类
package com.example.jwtdemo.entity;import lombok.Data;@Data
public class Result<T> {private int code; // 状态码:200成功,401未授权,500服务器错误等private String message; // 提示信息private T data; // 响应数据// 成功返回public static <T> Result<T> success(T data) {Result<T> result = new Result<>();result.setCode(200);result.setMessage("成功");result.setData(data);return result;}// 失败返回public static <T> Result<T> error(int code, String message) {Result<T> result = new Result<>();result.setCode(code);result.setMessage(message);return result;}
}
package com.example.jwtdemo.entity;import lombok.Data;@Data
public class TokenDTO {private String accessToken; // 访问令牌private String refreshToken; // 刷新令牌
}
package com.example.jwtdemo.entity;import lombok.Data;@Data
public class User {private Integer id;private String username;private String password;private String role; // 角色,如"USER"或"ADMIN"
}
4、创建JWT工具类
package com.example.jwtdemo.util;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;@Component
public class JwtUtil {@Value("${jwt.secret}")private String secretKey;@Value("${jwt.access-token-expire}")private long accessTokenExpire;@Value("${jwt.refresh-token-expire}")private long refreshTokenExpire;// 从令牌中获取用户名public String getUsernameFromToken(String token) {return getClaimFromToken(token, Claims::getSubject);}// 从令牌中获取过期时间public Date getExpirationDateFromToken(String token) {return getClaimFromToken(token, Claims::getExpiration);}// 从令牌中获取自定义声明public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {final Claims claims = getAllClaimsFromToken(token);return claimsResolver.apply(claims);}// 解析令牌,获取所有声明private Claims getAllClaimsFromToken(String token) {return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();}// 检查令牌是否过期private Boolean isTokenExpired(String token) {final Date expiration = getExpirationDateFromToken(token);return expiration.before(new Date());}// 生成访问令牌public String generateAccessToken(String username, String role) {Map<String, Object> claims = new HashMap<>();claims.put("role", role); // 存储角色信息return doGenerateToken(claims, username, accessTokenExpire);}// 生成刷新令牌public String generateRefreshToken(String username) {return doGenerateToken(new HashMap<>(), username, refreshTokenExpire);}// 实际生成令牌的方法private String doGenerateToken(Map<String, Object> claims, String subject, long expiration) {return Jwts.builder().setClaims(claims).setSubject(subject) // 用户名作为主题.setIssuedAt(new Date(System.currentTimeMillis())) // 签发时间.setExpiration(new Date(System.currentTimeMillis() + expiration)) // 过期时间.signWith(SignatureAlgorithm.HS512, secretKey) // 签名算法和密钥.compact();}// 验证令牌public Boolean validateToken(String token, String username) {final String tokenUsername = getUsernameFromToken(token);return (tokenUsername.equals(username) && !isTokenExpired(token));}
}
5、创建JWT认证拦截器
package com.example.jwtdemo.config;import com.example.jwtdemo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class JwtInterceptor implements HandlerInterceptor {@Autowiredprivate JwtUtil jwtUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从请求头中获取令牌String authorizationHeader = request.getHeader("Authorization");// 检查令牌是否存在且格式正确if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("未提供有效的令牌");return false;}// 提取令牌(去掉"Bearer "前缀)String token = authorizationHeader.substring(7);try {// 从令牌中获取用户名String username = jwtUtil.getUsernameFromToken(token);// 验证令牌if (!jwtUtil.validateToken(token, username)) {throw new Exception("令牌无效");}// 令牌有效,将用户名存入请求属性,供后续使用request.setAttribute("username", username);return true;} catch (Exception e) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("令牌验证失败:" + e.getMessage());return false;}}
}
7、配置拦截器和Redis
package com.example.jwtdemo.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class WebConfig implements WebMvcConfigurer {// 注册JWT拦截器@Beanpublic JwtInterceptor jwtInterceptor() {return new JwtInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 配置拦截器应用的路径registry.addInterceptor(jwtInterceptor()).addPathPatterns("/api/**") // 拦截/api/**路径的请求.excludePathPatterns("/api/login") // 不拦截登录接口.excludePathPatterns("/api/refresh-token"); // 不拦截刷新令牌接口}
}
8、创建服务层
package com.example.jwtdemo.service;import com.example.jwtdemo.entity.TokenDTO;
import com.example.jwtdemo.entity.User;public interface UserService {// 用户登录TokenDTO login(String username, String password);// 刷新访问令牌String refreshToken(String refreshToken);// 注销登录void logout(String username);// 根据用户名获取用户信息User getUserByUsername(String username);
}
package com.example.jwtdemo.service.impl;import com.example.jwtdemo.entity.TokenDTO;
import com.example.jwtdemo.entity.User;
import com.example.jwtdemo.service.UserService;
import com.example.jwtdemo.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class UserServiceImpl implements UserService {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate StringRedisTemplate redisTemplate;// 模拟数据库中的用户private static final User mockUser = new User();static {mockUser.setId(1);mockUser.setUsername("admin");mockUser.setPassword("123456");mockUser.setRole("ADMIN");}@Overridepublic TokenDTO login(String username, String password) {// 验证用户名密码(实际项目中应查询数据库)if (!mockUser.getUsername().equals(username) || !mockUser.getPassword().equals(password)) {throw new RuntimeException("用户名或密码错误");}// 生成令牌String accessToken = jwtUtil.generateAccessToken(username, mockUser.getRole());String refreshToken = jwtUtil.generateRefreshToken(username);// 将刷新令牌存入Redis,设置过期时间redisTemplate.opsForValue().set("refresh_token:" + username, refreshToken, 7, TimeUnit.DAYS);TokenDTO tokenDTO = new TokenDTO();tokenDTO.setAccessToken(accessToken);tokenDTO.setRefreshToken(refreshToken);return tokenDTO;}@Overridepublic String refreshToken(String refreshToken) {try {// 从刷新令牌中获取用户名String username = jwtUtil.getUsernameFromToken(refreshToken);// 验证刷新令牌是否有效if (!jwtUtil.validateToken(refreshToken, username)) {throw new RuntimeException("刷新令牌无效");}// 检查Redis中是否存在该刷新令牌String storedToken = redisTemplate.opsForValue().get("refresh_token:" + username);if (storedToken == null || !storedToken.equals(refreshToken)) {throw new RuntimeException("刷新令牌已过期或已被注销");}// 生成新的访问令牌User user = getUserByUsername(username);return jwtUtil.generateAccessToken(username, user.getRole());} catch (Exception e) {throw new RuntimeException("刷新令牌失败:" + e.getMessage());}}@Overridepublic void logout(String username) {// 从Redis中删除刷新令牌redisTemplate.delete("refresh_token:" + username);}@Overridepublic User getUserByUsername(String username) {// 实际项目中应查询数据库if (mockUser.getUsername().equals(username)) {return mockUser;}throw new RuntimeException("用户不存在");}
}
9、创建控制器
package com.example.jwtdemo.controller;import com.example.jwtdemo.entity.Result;
import com.example.jwtdemo.entity.TokenDTO;
import com.example.jwtdemo.entity.User;
import com.example.jwtdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletRequest;@RestController
@RequestMapping("/api")
public class UserController {@Autowiredprivate UserService userService;// 登录接口@PostMapping("/login")public Result<TokenDTO> login(@RequestParam String username, @RequestParam String password) {try {TokenDTO tokenDTO = userService.login(username, password);return Result.success(tokenDTO);} catch (Exception e) {return Result.error(401, e.getMessage());}}// 刷新令牌接口@PostMapping("/refresh-token")public Result<String> refreshToken(@RequestParam String refreshToken) {try {String newAccessToken = userService.refreshToken(refreshToken);return Result.success(newAccessToken);} catch (Exception e) {return Result.error(401, e.getMessage());}}// 注销接口@PostMapping("/logout")public Result<Void> logout(HttpServletRequest request) {try {String username = (String) request.getAttribute("username");userService.logout(username);return Result.success(null);} catch (Exception e) {return Result.error(500, e.getMessage());}}// 获取用户信息接口(需要认证)@GetMapping("/user/info")public Result<User> getUserInfo(HttpServletRequest request) {try {String username = (String) request.getAttribute("username");User user = userService.getUserByUsername(username);// 出于安全考虑,清除密码信息user.setPassword(null);return Result.success(user);} catch (Exception e) {return Result.error(500, e.getMessage());}}
}

五、核心实现

1、登录流程:用户提交用户名密码 → 验证通过 → 生成 accessToken 和 refreshToken → 返回给客户端
2、认证流程:客户端请求带 accessToken → 拦截器验证 token → 验证通过则允许访问
3、令牌刷新:accessToken 过期 → 用 refreshToken 获取新的 accessToken → 避免重新登录
4、安全机制:

  • 密码验证确保用户身份
  • 令牌签名防止篡改
  • 短期 accessToken 减少泄露风险
  • refreshToken 存储在 Redis 中便于注销

六、核心知识点的回答

1、生成token时如何设置过期时间

1.1、直接指定过期时间

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;public class JwtExample {private static final String SECRET_KEY = "your-secret-key";public static String generateToken(String username) {// 直接设置过期时间为 2024-12-31 23:59:59(硬编码,不推荐)Date expirationDate = new Date(1735689599000L); // 时间戳(毫秒)return Jwts.builder().setSubject(username) // 存储用户名.setExpiration(expirationDate) // 设置过期时间.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 签名.compact();}
}

1.2、动态计算过期时间

  • 获取当前时间戳(System.currentTimeMillis());
  • 加上有效期(毫秒单位,如 1 小时 = 3600 * 1000 毫秒);
  • 转换为 Date 对象传入 setExpiration()。
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;public class JwtExample {private static final String SECRET_KEY = "your-secret-key";// 有效期:1小时(3600秒 * 1000毫秒)private static final long EXPIRATION_TIME = 3600 * 1000;public static String generateToken(String username) {// 计算过期时间:当前时间 + 有效期Date expirationDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME);return Jwts.builder().setSubject(username) // 存储用户名.setIssuedAt(new Date()) // 签发时间(可选,便于追踪).setExpiration(expirationDate) // 设置过期时间.signWith(SignatureAlgorithm.HS256, SECRET_KEY) // 签名算法+密钥.compact();}
}

1.3、在配置文件中管理过期时间

  • 配置文件
  • 通过@Value注入配置,动态生成过期时间
    jwt:secret: your-secret-keyaccess-token-expire: 3600000  # 访问令牌有效期:1小时(毫秒)refresh-token-expire: 604800000  # 刷新令牌有效期:7天(毫秒)
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import java.util.Date;@Component
    public class JwtUtil {@Value("${jwt.secret}")private String secretKey;@Value("${jwt.access-token-expire}")private long accessTokenExpire; // 从配置文件注入有效期(毫秒)// 生成访问令牌(短期有效)public String generateAccessToken(String username) {Date expirationDate = new Date(System.currentTimeMillis() + accessTokenExpire);return Jwts.builder().setSubject(username).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS256, secretKey).compact();}
    }
    2、JWT有哪些安全隐患?如何解决
  • 隐患 1:Payload 可解码,泄露非敏感信息(如用户名);解决:不存储敏感信息,仅存用户 ID 等非敏感标识。
  • 隐患 2:令牌泄露后可被恶意使用(无法主动吊销);解决:短期有效期 + 刷新令牌机制 + 令牌黑名单(Redis)。
  • 隐患 3:密钥泄露导致令牌可伪造;解决:密钥通过环境变量存储,定期更换;使用 RS256 非对称加密(私钥签名,公钥验证)。
  • 隐患 4:传输过程中被劫持;解决:全程使用 HTTPS 加密传输。
3、JWT 和 Session 的区别是什么?

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

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

相关文章

量子文件传输系统:简单高效的文件分享解决方案

&#x1f310; 在线体验地址&#xff1a;https://share-file.narutogis.com/ &#x1f4e4; 项目概述 量子文件传输系统是一款基于Python Flask开发的高效文件管理与分享工具&#xff0c;致力于提供简单、安全、可靠的文件传输解决方案。系统支持用户管理、文件上传下载、自动…

基于 GitHub Actions 的零成本自动化部署:把 Vite/Vue3 项目一键发布到 GitHub Pages 的完整实战

1. 实现自动化部署1.1. 创建 vue 项目# 1. 安装/确认 Node.js&#xff08;>14&#xff09; node -v # 推荐 20.x# 2. 创建项目&#xff08;交互式&#xff0c;选 Vue3 Router 等&#xff09; npm init vuelatest github-actions-demo # 创建vite项目 # 或&#xff1a;v…

minio 文件批量下载

MinIO 批量下载功能说明 1. 功能描述 前端勾选多个对象文件后&#xff0c;一次性将这些对象从 MinIO 拉取并打包成 ZIP&#xff0c;通过浏览器直接下载。整体特性&#xff1a; 支持跨桶批量下载&#xff08;不同 bucket 的对象可同时下载&#xff09;。服务端采用流式压缩边…

机器学习11——特征选择与稀疏学习

上一章&#xff1a;机器学习10——降维与度量学习 下一章&#xff1a;【从 0 到 1 落地】机器学习实操项目目录&#xff1a;覆盖入门到进阶&#xff0c;大学生就业 / 竞赛必备[TOC] 机器学习实战项目&#xff1a;【从 0 到 1 落地】机器学习实操项目目录&#xff1a;覆盖入门到…

整理python快速构建数据可视化前端的Dash库

一.Dash框架# 导入 Dash 相关库 import dash from dash import dcc, html # dcc 是 Dash 核心组件库&#xff0c;html 是 HTML 组件库 from typing import Generic# 创建一个 Dash 应用实例 app dash.Dash(__name__)# 定义应用的布局 app.layout html.Div(children[# 添加一…

RNN循环神经网络(一):基础RNN结构、双向RNN

RNN循环神经网络 什么是循环神经网络&#xff1f; 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一类专门用于处理序列数据的神经网络架构。与传统的前馈神经网络不同&#xff0c;RNN具有"记忆"能力&#xff0c;能够捕捉数据中的时间依赖关系…

#C语言——刷题攻略:牛客编程入门训练(十):攻克 循环控制(二),轻松拿捏!

&#x1f31f;菜鸟主页&#xff1a;晨非辰的主页 &#x1f440;学习专栏&#xff1a;《C语言刷题合集》 &#x1f4aa;学习阶段&#xff1a;C语言方向初学者 ⏳名言欣赏&#xff1a;"代码行数决定你的下限&#xff0c;算法思维决定你的上限。" 目录 1. BC82 乘法表…

daily notes[16]

文章目录意大利语单词 **“bello”**一、核心含义二、变形规则&#xff1a;最重要的部分1. 当 “bello” 位于 **名词前面** 时2. 当 “bello” 位于 **名词后面** 或 **动词后面** 时三、用法总结与对比四、其他用法和常见表达references意大利语单词 “bello” 融合了 指示形…

【知识库】计算机二级python操作题(二)

文章目录基本操作题1基本操作题2基本操作题3简单应用题1简单应用题2综合应用题1基本操作题1考生文件夹下存在一个文件PY101.py&#xff0c;请写代码替换横线&#xff0c;不修改其他代码&#xff0c;实现以下功能&#xff0c;随机选择一个手机品牌屏幕输出。 # 请在...处使用一行…

Nginx 服务用户与防盗链配置

目录 Nginx 服务用户与防盗链配置 1. 隐藏版本号 1.1 配置方法 1.2 生效与验证 2. 修改当前程序账号 2.1 操作步骤 3. 缓存时间 3.1 配置方法 3.2 说明 4. 日志分割 4.1 实现方式&#xff08;脚本自动分割&#xff09; 5. 连接超时时间 5.1 核心超时指令&#xff0…

域格4G模块通信协议之HTTP(三):下载大文件的两种方式

域格ASR系列模块支持HTTP下载大文件&#xff0c;本文将提供两种方式。一、直接通过URC上报数据基础操作核心指令说明配置说明响应说明应用示例注意点二、HTTP Range分段下载核心指令说明注意点一、直接通过URC上报数据 若文件体积适中&#xff0c;且需要 MCU 即时处理数据&…

Android 图片 OOM 防护机制设计:大图加载、内存复用与多级缓存

1. 为什么图片加载总让 Android 开发抓狂? 图片是 Android 应用中不可或缺的元素,从用户头像到高清壁纸,从商品详情页到动态表情包,图片无处不在。然而,图片加载是内存管理的雷区,稍不留神就可能触发臭名昭著的 OutOfMemoryError(OOM)。为啥图片这么“吃内存”?原因很…

9月9日

TCP 服务器端#include <myhead.h> #define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.108.179" //服务器IP地址 int main(int argc, const char *argv[]) {//创建一个用于连接的套接字文件描述符int sfd socket(AF_INET, SOCK_STRE…

Docker生产部署

目录 一、准备工作&#xff1a;理解 Docker 与 Spring Boot 的关系 1. Docker 是什么&#xff1f; 2. Spring Boot 为什么适合 Docker&#xff1f; 二、编写Dockerfile 三、配置管理 挂载外部配置文件 四、用 docker-compose 编排多服务 一、准备工作&#xff1a;理解 Do…

ARM 基础(3)

ARM汇编与C语言函数的相互调用及参数传递汇编调用C函数参数传递规则 前4个参数通过寄存器 R0-R3 传递&#xff0c;超出部分从右向左压栈。32位返回值存于 R0&#xff0c;64位整数用 R0 和 R1&#xff0c;浮点数通过 S0/D0 返回。示例&#xff1a;ARM汇编调用C函数.global _star…

OpenCV计算机视觉笔记合集

参考课程&#xff1a; 【黑马程序员 OpenCV入门教程】 [https://www.bilibili.com/video/BV1Fo4y1d7JL] ZZHow(ZZHow1024) 学习路线 基本的图像处理方法&#xff1a;几何变换&#xff0c;形态学变换&#xff0c;图像平滑&#xff0c;直方图操作&#xff0c;模板匹配&#…

Mybatis-12 第三方缓存-EhCache

配置文档 Ehcache配置文件ehcache.xml Java Ehcache缓存的timeToIdleSeconds和timeToLiveSeconds区别 基本介绍 1.EhCache是一个纯Java的缓存框架&#xff0c;具有快速、精干等特点 2.MyBatis有自己默认的二级缓存&#xff08;前面我们已经使用过了&#xff09;&#xff0c;…

元器件--电容器

文章目录一、技术理论  1、电容定义  2、定义式  3、单位换算  4、电容作用  5、电容特性二、组成结构  1、极板&#xff08;电极&#xff09;​​  ​​2、介质&#xff08;绝缘层&#xff09;​​  3、引线&#xff08;电极引出端&#xff09;​​  4、封装…

【Leetcode hot 100】146.LRU缓存

问题链接 146.LRU缓存 问题描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关…