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

双Token方案实现Token自动续期(基于springboot+vue前后端分离项目)

文章目录

  • 前言
  • 一、双Token方案介绍
    • 1. 令牌类型与功能
    • 2.双Token方案的优点
    • 3.实现流程
  • 二、具体实现
    • 1.后端实现
      • 1.1 jwt工具类
      • 1.2 响应工具类
      • 1.3 实体类
      • 1.4 过滤器
      • 1.5 controller
      • 1.6 启动类
    • 2、前端实现
      • 2.1 登录页面
      • 2.2 index页面
      • 2.3 请求拦截器和响应拦截器
  • 效果展示


前言

更多jwt相关文章:
jwt理论介绍
springboot+vue项目中使用jwt实现登录认证

本篇文章的代码是在springboot+vue项目中使用jwt实现登录认证的基础上实现的Token自动续期的功能。


一、双Token方案介绍

双token解决方案是一种用于增强用户登录安全性和提升用户体验的认证机制。它主要涉及两个令牌:访问令牌(accessToken)和刷新令牌(refreshToken)。以下是对双token解决方案的详细介绍:

1. 令牌类型与功能

  • 访问令牌(accessToken):
    有效期较短,通常设置为较短的时间,如两小时或根据业务需求自定义(如10分钟)。 储存用户信息权限等,包含用户相关信息,如UserID、Username等。 用于前端与后端之间的通信认证,前端在每次请求时携带此令牌进行校验。
  • 刷新令牌(refreshToken):
    有效期较长,可以设置为一星期、一个月或更长时间,具体根据业务需求自定义。 不储存额外信息,只储存用户id,用于在accessToken过期后重新生成新的accessToken。 由于有效期长,因此降低了用户需要频繁登录的频率。
  • 2.双Token方案的优点

  • 增强安全性:通过短期有效的accessToken和长期有效的refreshToken的结合,即使accessToken泄露,攻击者也只能在有限时间内进行模拟用户行为,降低了安全风险。
  • 提升用户体验:由于refreshToken的存在,用户无需频繁登录,特别是在长时间操作或后台服务场景下,提高了用户体验。
  • 3.实现流程

  • 登录:用户输入用户名和密码进行登录,后端验证成功后生成accessToken和refreshToken,并发送给前端。
  • 请求校验:前端在每次请求时携带accessToken进行校验,如果accessToken有效,则允许请求继续;如果无效但refreshToken有效,则使用refreshToken重新生成accessToken。
  • 令牌刷新:当accessToken过期但refreshToken未过期时,前端可以使用refreshToken向后端请求新的accessToken,无需用户重新登录。
  • 登出:用户登出时,后端需要同时使accessToken和refreshToken失效,以确保用户登出后的安全性。
  • 二、具体实现

    1.后端实现

    1.1 jwt工具类

    package com.etime.util;import io.jsonwebtoken.*;import java.util.Date;
    import java.util.Map;
    import java.util.UUID;/*** @Date 2024/6/10 10:04* @Author liukang**/
    public class JwtUtil {
    //    private static long expire = 1000*60*5;// 单位是毫秒private static String secret = "secret";/*** 创建jwt* @author liukang* @date 10:36 2024/6/10* @param expire* @param map* @return java.lang.String**/public static String generateToken(long expire, Map map){// 床jwt构造器JwtBuilder jwtBuilder = Jwts.builder();// 生成jwt字符串String jwt = jwtBuilder//头部.setHeaderParam("typ","JWT").setHeaderParam("alg","HS256")// 载荷.setClaims(map) // 设置多个自定义数据  位置只能放在前面,如果放在后面,那前面的载荷会失效.setId(UUID.randomUUID().toString())// 唯一标识.setIssuer("liukang")// 签发人.setIssuedAt(new Date())// 签发时间.setSubject("jwtDemo")// 主题.setExpiration(new Date(System.currentTimeMillis()+expire))//过期时间// 自定义数据
    //                .claim("uname","liukang")// 签名.signWith(SignatureAlgorithm.HS256,secret).compact();return jwt;}/*** 创建jwt* @author liukang* @date 10:36 2024/6/10* @param expire* @return java.lang.String**/public static String generateToken(long expire){// 床jwt构造器JwtBuilder jwtBuilder = Jwts.builder();// 生成jwt字符串String jwt = jwtBuilder//头部.setHeaderParam("typ","JWT").setHeaderParam("alg","HS256")// 载荷.setId(UUID.randomUUID().toString())// 唯一标识.setIssuer("liukang")// 签发人.setIssuedAt(new Date())// 签发时间.setSubject("jwtDemo")// 主题.setExpiration(new Date(System.currentTimeMillis()+expire))//过期时间// 自定义数据
    //                .claim("uname","liukang")// 签名.signWith(SignatureAlgorithm.HS256,secret).compact();return jwt;}/*** 解析jwt* @author liukang* @date 10:36 2024/6/10* @param jwt* @return io.jsonwebtoken.Claims**/public static Claims parseToken(String jwt){Jws<Claims> claimsJws = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt);Claims playload = claimsJws.getBody();return playload;}
    }

    1.2 响应工具类

    代码如下(示例):

    package com.etime.util;import com.etime.vo.ResponseModel;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.http.MediaType;import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;/*** @Date 2024/6/10 10:00* @Author liukang**/
    public class ResponseUtil {public static void write(ResponseModel rm, HttpServletResponse response) throws IOException {// 构造响应头response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("utf-8");// 解决跨域问题 设置跨域头response.setHeader("Access-Control-Allow-Origin","*");// 输出流PrintWriter out = response.getWriter();// 输出out.write(new ObjectMapper().writeValueAsString(rm));// 关闭流out.close();}
    }

    1.3 实体类

    登录用户实体类

    package com.etime.entity;import lombok.Data;/*** @Date 2024/6/10 10:39* @Author liukang**/
    @Data
    public class User {private String username;private String password;
    }

    响应vo类

    package com.etime.vo;import lombok.Data;import java.util.Objects;/*** @Date 2024/6/10 10:37* @Author liukang**/
    @Data
    public class ResponseModel {private Integer code;private String msg;private Object token;public ResponseModel(Integer code, String msg, Object token) {this.code = code;this.msg = msg;this.token = token;}
    }

    1.4 过滤器

    package com.etime.filter;import com.etime.util.JwtUtil;
    import com.etime.util.ResponseUtil;
    import com.etime.vo.ResponseModel;
    import com.sun.deploy.net.HttpResponse;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.HttpRequest;
    import org.springframework.util.StringUtils;import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;/*** @Description jwt过滤器* @Date 2024/6/10 9:46* @Author liukang**/
    @WebFilter(urlPatterns = "/*") // 过滤所有路径
    public class JwtFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// 得到两个对象HttpServletRequest request =  (HttpServletRequest) servletRequest;HttpServletResponse response =  (HttpServletResponse) servletResponse;//直接放行if(HttpMethod.OPTIONS.toString().equals(request.getMethod())){filterChain.doFilter(request,response);return;}String requestURI = request.getRequestURI(); // 不含主机和端口号if(requestURI.contains("/login")){filterChain.doFilter(request,response);return;}// 得到请求头的信息(accessToken)String token = request.getHeader("accessToken");if(!StringUtils.hasText(token)){//响应前端错误的消息提示ResponseModel responseModel = new ResponseModel(500,"failure","令牌缺失!");ResponseUtil.write(responseModel,response);return;}// 解析Token信息try {JwtUtil.parseToken(token);}catch (Exception e){//响应前端错误的消息提示ResponseModel responseModel = new ResponseModel(401,"failure","令牌过期!");ResponseUtil.write(responseModel,response);return;}filterChain.doFilter(request,response);}
    }

    1.5 controller

    登录Controller

    package com.etime.controller;import com.etime.entity.User;
    import com.etime.util.JwtUtil;
    import com.etime.vo.ResponseModel;
    import org.springframework.web.bind.annotation.*;import java.util.HashMap;
    import java.util.Map;/*** @Date 2024/6/10 10:38* @Author liukang**/
    @RestController
    @CrossOrigin
    public class LoginController {@PostMapping("/login")public ResponseModel login(@RequestBody User user){Integer code = 200;String msg = "success";String accessToken = null;String refreshToken = null;Map tokenMap = new HashMap();if(user.getUsername().equals("admin")&&user.getPassword().equals("123")){// 生成jwtaccessToken = JwtUtil.generateToken(1000*10);// 设置有效期为10srefreshToken = JwtUtil.generateToken(1000*30);// 设置有效期为30stokenMap.put("accessToken",accessToken);tokenMap.put("refreshToken",refreshToken);}else {code = 500;msg = "failure";}return new ResponseModel(code,msg,tokenMap);}}

    测试请求Controller

    package com.etime.controller;import com.etime.vo.ResponseModel;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;/*** @Date 2024/6/10 12:51* @Author liukang**/
    @CrossOrigin
    @RestController
    public class TestController {@PostMapping("/test")public ResponseModel test() {return new ResponseModel(200,"success","测试请求接口成功!");}}

    刷新Token的Controller

    package com.etime.controller;import com.etime.util.JwtUtil;
    import com.etime.vo.ResponseModel;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
    import java.util.Map;/*** @Date 2024/6/10 15:48* @Author liukang**/
    @CrossOrigin
    @RestController
    public class NewTokenController {@GetMapping("/newToken")public ResponseModel newToken(){String accessToken = JwtUtil.generateToken(1000*10);String refreshToken = JwtUtil.generateToken(1000*30);Map tokenMap = new HashMap();tokenMap.put("accessToken",accessToken);tokenMap.put("refreshToken",refreshToken);return new ResponseModel(200,"success",tokenMap);}
    }

    1.6 启动类

    package com.etime;import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.web.servlet.ServletComponentScan;/*** @Author liukang* @Date 2022/7/4 11:32*/
    @SpringBootApplication
    @ServletComponentScan(basePackages = "com.etime.filter")// 这个包下激活WebFilter这个注解
    public class Application {public static void main(String[] args) {SpringApplication.run(Application.class);}
    }

    2、前端实现

    2.1 登录页面

    <template><div class="hello"><form>用户名:<input v-model="username"/><br>密码<input v-model="password" /><br><button @click="login">登录</button></form></div></template><script>export default {data () {return {username:'',password:'',}},methods:{login(){this.axios.post('http://localhost:8088/login',{username:this.username,password:this.password,}).then(response => {console.log(response.data);if(response.data.code==200){sessionStorage.setItem("accessToken",response.data.token.accessToken)sessionStorage.setItem("refreshToken",response.data.token.refreshToken)this.$router.push({ path: 'index'});}}).catch(error => {console.error(error);});}},}</script><style scoped></style>

    2.2 index页面

    <template><div><button @click="test">请求受保护的接口</button></div></template><script>
    import intercepterConfig from './js/config'export default {data () {return {}},methods:{test(){const accessToken = sessionStorage.getItem('accessToken')let token = nullif(accessToken){token = accessToken}// console.log(token)this.axios.post('http://localhost:8088/test',{},/*{headers:{accessToken:'token'}}*/).then(response => {// if(response.data.code==200){console.log(response.data);// }}).catch(error => {console.error(error);});},},}</script><style scoped></style>

    2.3 请求拦截器和响应拦截器

    import axios from "axios";
    //axios请求拦截器
    axios.interceptors.request.use(config=>{// 正确的请求拦截器let token = null;let url = config.url// url.indexOf('/newToken')==-1  如果是刷新Token的请求 不用在拦截器里面加accessToken 这个请求已经在请求头中设置accessToken,加了会覆盖if(sessionStorage.getItem('accessToken')!=null && url.indexOf('/newToken')==-1){token = sessionStorage.getItem('accessToken')config.headers['accessToken'] = token}// 加入头信息的配置return config // 这句没写请求会发不出去},error=>{ // 出现异常的请求拦截器return Promise.reject(error)})
    // axios响应拦截器
    axios.interceptors.response.use(async res => {// 判断 401状态码 自动续期if (res.data.code == 401 &&!res.config.isRefresh) {//!res.config.isRefresh  不是刷新Token的请求才拦截 是则不拦截// 1.自动续期const res2 = await getNewToken()if(res2.data.code == 200){console.log('自动续期成功'+new Date().toLocaleString())// 2.更新sessionStorage里面的Token   没有这一步会死循环sessionStorage.setItem('accessToken',res2.data.token.accessToken)sessionStorage.setItem('refreshToken',res2.data.token.refreshToken)//3.重新发送请求res = await axios.request(res.config)// res.config 代表请求的所有参数(这里是上一次请求的所有参数),包括url和携带的所有数据}}return res     // 将重新请求的响应作为响应返回},error=>{return Promise.reject(error)})function getNewToken(){let url = "http://localhost:8088/newToken"let token = nullif(sessionStorage.getItem('refreshToken')!=null){token = sessionStorage.getItem('refreshToken')}return  axios.get(url,{headers:{accessToken:token},isRefresh:true})// 注意这里参数是accessToken:token  因为后端过滤器里面获取的是accessToken,所以要写这个,不然过滤器通不过过滤器
    }

    效果展示

    1.登录页面
    在这里插入图片描述
    2.输入用户名和密码点击【登录】
    在这里插入图片描述
    3.点击【请求受保护的资源】按钮
    在这里插入图片描述
    3.等待10秒,accessToken过期,但refreshToken未过期时,点击【请求受保护的资源】按钮
    在这里插入图片描述
    4.等待30秒后,refreshToken和accessToken都过期,再次点击【请求受保护的资源】按钮

    在这里插入图片描述


相关文章:

双Token方案实现Token自动续期(基于springboot+vue前后端分离项目)

文章目录 前言一、双Token方案介绍1. 令牌类型与功能2.双Token方案的优点3.实现流程 二、具体实现1.后端实现1.1 jwt工具类1.2 响应工具类1.3 实体类1.4 过滤器1.5 controller1.6 启动类 2、前端实现2.1 登录页面2.2 index页面2.3 请求拦截器和响应拦截器 效果展示 前言 更多j…...

别太小看“静态免杀“

0x01 简述 免杀总体来说可分为两种&#xff0c;静态免杀/动态免杀。往往来说&#xff0c;我们更注重于在内部代码层面实现一些免杀技巧&#xff0c;但在有些时候&#xff0c;动态免杀静态免杀以"打组合拳"的方式效果往往会更出人所料。 当我们的程序生成后&#xf…...

SQL server 内连接 左连接 右连接 全连接 语句

在SQL Server中&#xff0c;连接&#xff08;JOIN&#xff09;操作用于从两个或多个表中检索相关数据。内连接、左连接、右连接和全连接是最常用的几种连接类型。下面详细介绍每种连接的用法和区别&#xff1a; 1. 内连接 (INNER JOIN) 内连接只返回两个表中满足连接条件的匹…...

k8s中的pod域名解析失败定位案例

问题描述 我在k8s中启动了一个Host网络模式的pod&#xff0c;这个pod的域名解析失败了。 定位步骤 敲kubectl exec -it [pod_name] -- bash进入pod后台&#xff0c;查看/etc/resolv.conf&#xff0c;发现nameserver配的有问题。这里我预期的nameserver应该使用宿主机的&…...

jingxiang制作

文章目录 jingxiang制作为什么需要jingxiang制作如何进行jingxiang制作 快照方式制作jingxiang制作命令do cker commit 快照制作jingxiang创建临时工作目录编写一个实例代码启动一个容器替换国内软件源安装编译软件源代码拷贝到容器中编译运行提交为一个jingxiang测试是否可以正…...

【数据结构】线性表之《顺序表》超详细实现

顺序表 一.数据结构1.逻辑结构2.物理结构 二.顺序表的分类1.静态顺序表2.动态顺序表 三.顺序表的实现1.创建顺序表2.初始化顺序表3.判断是否扩容4.打印顺序表5.插入操作1.头插2.尾插3.按照下标插入 6.删除操作1.头删2.尾删3.按照下标删除 7.查找数据8.修改数据9.清空顺序表10.销…...

开源模型应用落地-音乐生成模型-suno/bark深度使用-AIGC应用探索(六)

一、前言 学习音乐生成模型具有极其重要的价值。通过对音乐生成模型的深入学习,我们能够探索到音乐创作的全新边界和可能性。它不仅可以开启一扇通往无限音乐创意的大门,让我们领略到科技与艺术完美融合所带来的震撼与惊喜,还能帮助我们在音乐领域实现前所未有的突破和创新。…...

为何选择Xinstall?告别邀请码,让App推广更便捷!

在互联网日益繁荣的今天&#xff0c;App的推广和运营成为了各大企业关注的重点。然而&#xff0c;传统的推广方式如邀请码限制&#xff0c;往往会给用户带来不便&#xff0c;同时也限制了App的快速增长。在这个背景下&#xff0c;Xinstall凭借其独特的功能和服务&#xff0c;成…...

JavaScript基础入门

目录 任务描述 相关知识 JavaScript语言简介 JavaScript注释 搭建JavaScript的运行环境 JavaScript输出 弹出对话框模式 控制台模式 页面输出模式 编程要求 任务描述 要了解一门编程语言&#xff0c;要从输出开始。 本关任务&#xff1a;采用相关知识中介绍的任意一…...

windows11子系统Ubuntu 22.04.4子安装图形化界面

1、windows11家庭版本设置 打开虚拟机安装许可 2、Microsoft Store下载安装ubuntu 我使用的是22.04.4 LTS版本 3、 打开ubuntu 命令窗口 1、打开win11的命令行&#xff0c;在下拉三角下标&#xff0c;打开&#xff0c;可以看到有Ubuntu 的选项&#xff0c;点击即可进入linux命…...

对 2024 年美赛选题的建议

对2024年美赛选题的建议包括&#xff1a; 1. 深入探讨当下全球面临的重大问题和挑战&#xff1a;鉴于美赛通常聚焦于全球性议题&#xff0c;如气候变化、可持续发展、数据分析等&#xff0c;参赛学生应关注这些议题&#xff0c;并深入研究相关数据与背景信息&#xff0c;以提出…...

PyTorch tutorials:快速学会使用PyTorch

准备深入学习transformer&#xff0c;并参考一些资料和论文实现一个大语言模型&#xff0c;顺便做一个教程&#xff0c;今天是番外篇&#xff0c;介绍下PyTorch&#xff0c;后面章节实现代码主要使用这个框架。 本系列禁止转载&#xff0c;主要是为了有不同见解的同学可以方便联…...

【CT】LeetCode手撕—手撕快排

目录 题目1-思路-快排1-1 快排的核心思想快速排序算法步骤优美的调整区间 1-2 ⭐快排的实现 2- 实现⭐912. 排序数组——题解思路 3- ACM 实现 题目 原题连接&#xff1a;912. 排序数组 1-思路-快排 1-1 快排的核心思想 选择一个基准 基准左侧的元素都小于该元素基准右侧的元…...

使用ARK工具ATool清除典型蠕虫MyDoom

1 概述 在长期的日常安全事件监测过程中&#xff0c;安天CERT经常捕获到大量的MyDoom蠕虫样本和传播该蠕虫的钓鱼邮件。受害主机感染MyDoom后会被放置后门&#xff0c;以便攻击者下发后续恶意软件&#xff0c;进行攻击或窃密等操作。MyDoom蠕虫最早发现于2004年&…...

在hue中使用ooize调度ssh任务无法执行成功,无法查看错误

ssh执行失败&#xff0c;但是hue没有给出明确的错误原因&#xff1a; 经过经验分析&#xff0c;原来是服务器上的sh文件用的是doc/window格式&#xff0c;需要使用notepad将格式改为unix之后就可以正常执行。 特此记录&#xff0c;避免遗忘知识点...

一套轻量、安全的问卷系统基座,提供面向个人和企业的一站式产品级解决方案

大家好&#xff0c;今天给大家分享的是一款轻量、安全的问卷系统基座。 XIAOJUSURVEY是一套轻量、安全的问卷系统基座&#xff0c;提供面向个人和企业的一站式产品级解决方案&#xff0c;快速满足各类线上调研场景。 内部系统已沉淀 40种题型&#xff0c;累积精选模板 100&a…...

3秒生成!这个AI模型画风也太治愈了,新手也能轻松驾驭

还在为不会画画而苦恼吗&#xff1f;别担心&#xff0c;今天给大家介绍一个超好用的AI模型——Soft and Squishy Linework&#xff0c;即使是小白也能轻松生成可爱的动漫图像&#xff01; Soft and Squishy Linework&#xff1a;专门生成柔和的、低保真&#xff08;lofi&#…...

数字人全拆解:如何构建一个基于大模型的实时对话3D数字人?

简单地说&#xff0c;数字人就是在数字世界的“人”。当前语境下我们谈到的数字人通常指的是借助AI技术驱动的虚拟世界人物&#xff0c;具备与真实人类相似甚至接近的外形、感知、交互与行为能力。 AI技术在智能数字人的应用中举足轻重&#xff0c;特别是随着大模型能力的涌现…...

实战 | 基于YOLOv10的车辆追踪与测速实战【附源码+步骤详解】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…...

2024北京智源大会

北京智源大会是年度国际性人工智能高端学术交流的盛会&#xff0c;定位于内行的AI盛会。智源大会紧密围绕当前人工智能学术领域迫切需要解决的问题&#xff0c;以及产业落地过程中存在的诸多挑战&#xff0c;开展深入探讨。智源研究院是2018年11月份成立的一家人工智能领域的新…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)

cd /home 进入home盘 安装虚拟环境&#xff1a; 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境&#xff1a; virtualenv myenv 3、激活虚拟环境&#xff08;激活环境可以在当前环境下安装包&#xff09; source myenv/bin/activate 此时&#xff0c;终端…...