手拉手后端Springboot整合JWT

news2024/11/30 12:27:20

环境介绍

技术栈

springboot+mybatis-plus+mysql+java-jwt

软件

版本

mysql

8

IDEA

IntelliJ IDEA 2022.2.1

JDK

1.8

Spring Boot

2.7.13

mybatis-plus

3.5.3.2

Json Web令牌简称JWT

Token是在服务端产生的一串字符串是客户端访问资源接口(AP)时所需要的资源凭证。

Token认证

Token是在服务端产生的一串字符串是客户端访问资源接口(AP)时所需要的资源凭证。

Token认证流程

1、客户端使用用户名跟密码请求登录,服务端收到请求,验证用户名与密码验证成功后,服务端会签发一个 token并把这个 token发送给客户端,客户端收到 token后,会把它存储起来,比如放在cookie里或者localStorage里

2、客户端每次向服务端请求资源的时候需要带着服务端签发的 token

3、服务端收到请求,然后去验证客户端请求里面带着的 token,如果验证成功就向客户端返回请求的数据

token用户认证是一种服务端无状态的认证方式,服务端不用存放token数据。

用解析 token的计算时间换取 session的存储空间,从而减服务器的力,减少频繁的查询数据库

token完全由应用管理,所以它可以避开同源策略

JWT的使用

JSON Web Token(简称JWT)是一个 token的具体实现方式,是目前最流行的跨域认证解决方案。JWT的原理是:服务器认证以后,生成一个JSON对象,发回给用户。

{

       “name” :”张三”,

       “time”:”2022年10月10日”

}

用户与服务端通信时,都要发回该JSON对象。服务器完全只靠这个对象认定用户身份。

为防止用户篡改数据,服务器在生成对象时,会加上签名

JWT由三个部分组成:Header(头部)、Payload(负载)、Signature(签名)

Header.Payload.Signature

官方描述

Header

JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存

{

  "alg": "HS256",

  "typ": "JWT"

}

Payload

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

iss:发行人

exp:到期时间

sub:主题

aud:用户

nbf:在此之前不可用

iat:发布时间

jti:JWT ID用于标识该JWT

Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。

加入依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.3.0</version>
</dependency>

数据库

实体类

package com.example.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;

/**
 * 
 * @TableName user
 */
@TableName(value ="user")
@Data
public class User implements Serializable {
    /**
     * 用户id
     */
    @TableId(type = IdType.AUTO)
    private Integer uid;

    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 盐值
     */
    private String salt;

    /**
     * 电话号码
     */
    private String phone;

    /**
     * 电子邮箱
     */
    private String email;

    /**
     * 性别:0-女,1-男
     */
    private Integer gender;

    /**
     * 头像
     */
    private String avatar;

    /**
     * 是否删除:0-未删除,1-已删除
     */
    private Integer isDelete;

    /**
     * 日志-创建人
     */
    private String createdUser;

    /**
     * 日志-创建时间
     */
    private Date createdTime;

    /**
     * 日志-最后修改执行人
     */
    private String modifiedUser;

    /**
     * 日志-最后修改时间
     */
    private Date modifiedTime;

    @TableField(exist = false)
    private static final long serialVersionUID = 1L;
}

mapper(dao)

package com.example.mapper;

import com.example.domain.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.Date;
@Mapper
public interface UserMapper extends BaseMapper<User> {
    /**
     * 插入用户数据
     * @param user 用户数据
     * @return 受影响的行数
     */
    int insert(User user);

    /**
     * 根据用户名查询用户是否存在
     * @param username
     * @return  成功返回单个用户数据,否返回null
     */
    User findByUserName(@Param("username") String username);

    /**
     * 根据uid查询
     * @param uid
     * @return
     */
    User findByUid(@Param("uid") Integer uid);

    /**
     * 更新用户个人资料信息
     * @param user
     * @return
     */
    Integer updateUserInfoByUid(User user);
    /**
     * 根据用户id修改密码
     * @param uid
     * @return password=?,modified_user=?,modified_time=?
     */
    Integer updatePasswordByUid(@Param("uid")Integer uid,
                                @Param("password")String password,
                                @Param("modified_user")String modified_user,
                                @Param("modified_time") Date modified_time);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">

    <resultMap id="UserPojoMap" type="com.example.domain.User">
        <id column="uid" property="uid"></id>
        <result column="is_delete" property="isDelete"></result>
        <result column="created_user" property="createdUser"></result>
        <result column="created_time" property="createdTime"></result>
        <result column="modified_user" property="modifiedUser"></result>
        <result column="modified_time" property="modifiedTime"></result>
    </resultMap>
    <!--    useGeneratedKeys="true" 开启主键自增 keyProperty="uid" 指定uid字段-->
    <insert id="insert" useGeneratedKeys="true" keyProperty="uid">
        insert into user(username,password,salt,phone,email,gender,avatar,is_delete,
                         created_user,created_time,modified_user,modified_time)
        values(#{username},#{password},#{salt},#{phone},#{email},#{gender},#{avatar},#{isDelete},
               #{createdUser},#{createdTime},#{modifiedUser},#{modifiedTime})
    </insert>

    <select id="findByUserName" resultMap="UserPojoMap">
        select * from user where username=#{username}
    </select>

    <select id="findByUid" resultMap="UserPojoMap">
        select * from user where uid=#{uid}
    </select>

    <update id="updatePasswordByUid">
        update user set password=#{password},
                        modified_user=#{modified_user},
                        modified_time=#{modified_time} where uid=#{uid}
    </update>

    <update id="updateUserInfoByUid">
        update user set
        <if test="phone!=null">phone=#{phone},</if>
        <if test="email!=null">email=#{email},</if>
        <if test="gender!=null">gender=#{gender},</if>
        modified_user=#{modifiedUser}, modified_time=#{modifiedTime} where uid=#{uid}
    </update>

    <sql id="Base_Column_List">
        uid,username,password,
        salt,phone,email,
        gender,avatar,is_delete,
        created_user,created_time,modified_user,
        modified_time
    </sql>
</mapper>

service

package com.example.service;

import com.example.domain.User;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {
    /**
     * 用户注册方法
     * @param user
     */
    void reg(User user);

    /**
     * 用户登入方法
     * @param username
     * @param password
     * @return
     */
    User login(String username,String password);

    /**
     * 根据uid查询
     * @param uid
     * @return User
     */

    /**
     *
     * @param uid
     * @param username
     * @param oldPassword
     * @param newPassword
     */
    void changePassword(Integer uid,
                        String username,
                        String oldPassword,
                        String newPassword);

    /**
     * 通过uid获取用户数据
     * @param uid
     * @return
     */
    User getUserInfoByUid(Integer uid);

    /**
     * 修改用户信息
     * @param user
     * @return
     */
    void changeUserInfo(Integer uid,String username,User user);

}

ServiceImpl

package com.example.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.domain.User;
import com.example.service.UserService;
import com.example.mapper.UserMapper;
import com.example.service.exception.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.UUID;
@Service
@DS("wms")
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{
    @Autowired
    private UserMapper userDao;
    @Override
    public void reg(User user) {
        //判断用户是否被注册过
        String username = user.getUsername();
        User byUserName = userDao.findByUserName(username);
        if (byUserName == null){
            //密码的加密处理:MD5算法
            //盐值+password+盐值
            String oldPassword =user.getPassword();
            //获取盐值
            String salt = UUID.randomUUID().toString().toUpperCase();
            //保存盐值
            user.setSalt(salt);
            String newPassword = getMD5Password(oldPassword,salt);
            user.setPassword(newPassword);
            //用户注册
            //        is_delete INT COMMENT '是否删除:0-未删除,1-已删除',
            //        created_user VARCHAR(20) COMMENT '日志-创建人',
            //        created_time DATETIME COMMENT '日志-创建时间',
            //        modified_user VARCHAR(20) COMMENT '日志-最后修改执行人',
            //        modified_time DATETIME COMMENT '日志-最后修改时间',
            Date nowTime=new Date();
            user.setIsDelete(0);
            user.setCreatedUser(user.getUsername());
            user.setCreatedTime(nowTime);
            user.setModifiedUser(user.getUsername());
            user.setModifiedTime(nowTime);
            Integer rows = userDao.insert(user);
            if (rows == 0 ){throw  new InsertException("注册失败(未知失败)请重新注册");
            };
        }else {
            throw new UsernameOccupyException("用户名被占用");
        }

    }

    @Override
    public User login(String username,String password) {
        User UserLogin = userDao.findByUserName(username);
        //盐值认证
        String md5Password = getMD5Password(password, UserLogin.getSalt());

        if (UserLogin == null){
            throw new UserNullException("用户不存在");
        }
        //检测密码是否匹配
        if (!UserLogin.getPassword().equals(md5Password)){
            throw new PasswordNotMatchException("用户密码错误");
        }

        //判断is_delete字段值是否为1表示被标记为删除
        if (UserLogin.getIsDelete() == 1){
            throw new UserNullException("用户已被删除");
        }

        User user = new User();
        user.setUid(UserLogin.getUid());
        user.setUsername(UserLogin.getUsername());
        user.setAvatar(UserLogin.getAvatar());
        //返回用户数据,是为了辅助页面

        return user;
    }
    @Override
    public void changePassword(Integer uid, String username, String oldPassword, String newPassword) {
        User user = userDao.findByUid(uid);
        if (user ==null){
            throw new UserNullException("用户不存在");
        }
        if (user.getIsDelete() ==1){
            throw new UserDeletedException("用户不存在或已被删除");
        }
        //密码对比
        String md5Password = getMD5Password(oldPassword, user.getSalt());
        if (!user.getPassword().equals(md5Password)){
            throw new PasswordNotMatchException("原密码错误");
        }
        //更新password
        String newPasswordMd5 = getMD5Password(newPassword, user.getSalt());
        Integer rows = userDao.updatePasswordByUid(uid, newPasswordMd5,
                username,
                new Date());
        if (rows !=1){
            throw new PasswordUpdateException("修改密码未知异常");
        }
    }

    //根据id获取userInfo
    @Override
    public User getUserInfoByUid(Integer uid) {
        User result = userDao.findByUid(uid);
        if (result == null || result.getIsDelete() ==1)
        {
            throw new UserNullException("用户不存在");
        }
        User user = new User();
        user.setUsername(result.getUsername());
        user.setUid(result.getUid());
        user.setPhone(result.getPhone());
        user.setEmail(result.getEmail());
        user.setGender(result.getGender());
        return user;
    }
    //修改用户信息
    @Override
    public void changeUserInfo(Integer uid, String username, User user) {
        User result = userDao.findByUid(uid);
        if (result == null || result.getIsDelete() ==1)
        {
            throw new UserNullException("用户不存在");
        }
        user.setUid(uid);
        user.setModifiedUser(username);
        user.setModifiedTime(new Date());
        Integer rows = userDao.updateUserInfoByUid(user);
        if (rows != 1){
            throw new InfoUpdateException("修改用户信息未知异常");
        }
    }
    //password加密方法
    private String getMD5Password(String password,String salt){
        for (int i =0;i<5;i++){
            password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
        }
        //返回加密之后的密码
        return password;
    }
}

JWT工具类

public class JWTUtil {
    private static final   String TOKENKey="qgs12345";

    /**
     * 生成token
     * @param map
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,7);//7天过期

        //添加payload
        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });

        builder.withExpiresAt(instance.getTime());//设置令牌过期时间
        //生成并返回token
       return builder.sign(Algorithm.HMAC256(TOKENKey)).toString();
    }

    /**
     * 验证token
     * @param token
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(TOKENKey)).build().verify(token);
    }

    /**
     * 获取token中payload
     * @param token
     * @return
     */
    public static DecodedJWT getTokenInfo(String token){
        return JWT.require(Algorithm.HMAC256(TOKENKey)).build().verify(token);
    }
}

UserController

@RestController
@RequestMapping("/users")
@CrossOrigin //表示都允许跨域访问
public class UserController extends BaseController{
    @Autowired
    private UserService userModuleService;
    @RequestMapping("/login")
    public Map<String,Object> login(String username, String password){

        Map<String,Object> map =new HashMap<>();
        try {
            User data = userModuleService.login(username, password);
            Map<String,String> payload =new HashMap<>();
            payload.put("username",data.getUsername());
            //生成JWT令牌
            String token =JWTUtil.getToken(payload);
            map.put("state",true);
            map.put("msg","登入成功");
            map.put("token",token);
        }catch (Exception e){
            map.put("state",false);
            map.put("msg","登入失败");
        }
     
        return map;
    }

}

登录,产生token

验证token方式一

认证代码中写token认证流程,过多的认证请求会导致代码冗余

@RestController
@RequestMapping("/users")
@CrossOrigin //表示都允许跨域访问
public class UserController extends BaseController{
    @Autowired
    private UserService userModuleService;
    @RequestMapping("/login")

    //验证token
    @RequestMapping("/loginVerify")
    public Map<String,Object> loginVerify(String token){

        System.out.println(token);
        Map<String,Object> map =new HashMap<>();
        try {
            //生成JWT令牌
            JWTUtil.verify(token);
            map.put("state",true);
            map.put("msg","验证成功");
        }catch (Exception e){
            map.put("state",true);
            map.put("msg","验证失败");
        }
  
        return map;
    }

}

验证token方式二 JWT拦截器

抛弃方式一的代码冗余

public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String,Object> map =new HashMap<>();

        // 获取请求头中的JWT令牌
         String token = request.getHeader("token");
        // 进行JWT令牌的验证逻辑
        try {
            //生成JWT令牌
            JWTUtil.verify(token);
            return true;//放行请求
        }catch (Exception e){
            map.put("state",false);
            map.put("msg","token验证失败");
            //map转json
            String msg =new ObjectMapper().writeValueAsString(map);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().println(msg);
        }
        return false;
    }
}

@Component
public class InterceptorConfig implements WebMvcConfigurer {

    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/users//loginVerify")//拦截路径,根据实际情况进行配置
                .excludePathPatterns("/users/login","/reg") ;//放行路径

    }

}

简化loginVerify后

//验证token
@RequestMapping("/loginVerify")
public Map<String,Object> loginVerify(HttpServletRequest request){
    Map<String,Object> map =new HashMap<>();

    String token =request.getHeader("token");
    System.out.println(token);

    map.put("state",true);
    map.put("msg","验证成功");
    return map;
}

请求头携带token效果

Session认证

session用户认证流程

1、用户向服务器发送用户名和密码。

2、服务器验证通过后,在当前对话( session)里面保存相关数据,比如用户角色登录时间等。

3、服务器向用户返回一个 session_id,写入用户的 Cookie。

4、用户随后的每一次请求,都会通过Cookie,将 session_id传回服务器。

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

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

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

相关文章

认识Linux指令之 “mv” 指令

01.mv指令&#xff08;重要&#xff09; mv命令是move的缩写&#xff0c;可以用来移动文件或者将文件改名&#xff08;move (rename) files&#xff09;&#xff0c;是Linux系统下常用的命令&#xff0c;经常用来备份文件或者目录。 语法: mv [选项] 源文件或目录 目标文件或…

【JavaFX】基于JavaFX11 构建可编辑、对象存储、修改立即保存、支持条件过滤的TableView

文章目录 效果设计思路二、使用步骤1. 创建实体类2.读取本地文件数据3. 定义表格TableView总结效果 如图所示,这是一个存储application.properties内容的表格。这里的文件application.properties是从Linux服务器上获取来的。 当点击检索按钮,并输入条件匹配字符时,TableVie…

从零开始的 dbt 入门教程 (dbt-core 基础篇)

最近一直在处理数据分析和数据建模的事情&#xff0c;所以接触了 dbt 等数据分析的工具&#xff0c;国内目前对于 dbt 比较详细的资料不多&#xff0c;所以打算写四道五篇 dbt 相关的文章&#xff0c;本文属于 dbt 系列的第一篇&#xff0c;本篇主要阐述 dbt 一些基本概念&…

Unity之地形的构建

PS&#xff1a;公司没活干&#xff0c;好无聊偷偷摸鱼学Unity&#xff0c;害怕自己学完之后忘记&#xff0c;写下这一篇博客 先来看一下效果图&#xff1a;有山有水有树有草地 创建一个新的Unity3D项目 这里要用到Unity官方的免费资源包&#xff08;现在好像已经下架了百度网盘…

【大数据面试知识点】分区器Partitioner:HashPartitioner、RangePartitioner

Spark HashParitioner的弊端是什么&#xff1f; HashPartitioner分区的原理很简单&#xff0c;对于给定的key&#xff0c;计算其hashCode&#xff0c;并除于分区的个数取余&#xff0c;如果余数小于0&#xff0c;则用余数分区的个数&#xff0c;最后返回的值就是这个key所属的…

自动化网络故障修复管理

什么是故障管理 故障管理是网络管理的组成部分&#xff0c;涉及检测、隔离和解决问题。如果实施得当&#xff0c;网络故障管理可以使连接、应用程序和服务保持在最佳水平&#xff0c;提供容错能力并最大限度地减少停机时间。专门为此目的设计的平台或工具称为故障管理系统。 …

【新手向】VulnHub靶场MONEYBOX:1 | 详细解析

MONEYBOX:1 安装靶机 作为一名新手&#xff0c;首先要配置好环境&#xff0c;才能进行下一步的操作。 将下载的ova文件导入VirtualBox。 VirtualBox下载地址&#xff1a;https://www.oracle.com/cn/virtualization/technologies/vm/downloads/virtualbox-downloads.html 选择…

C#编程-编写和执行C#程序

编写和执行C#程序 可以使用Windows记事本应用程序来编写C#程序。在记事本应用程序中创建C#程序后,您需要编译并执行该程序以获得所需的输出。编译器将程序的源代码转换为机器代码,这样计算机就能理解程序中的指令了。 注释 除了记事本,您还可以使用任何其他文本编辑器来编写…

Altium Designer20中遇到的问题和解决办法记录

最近二战考完研了&#xff0c;重新拾起之前学的一些项目&#xff0c;最近在优化以前话的四层PCB版的时候发现了在使用AD使碰到一些问题现在记录如下&#xff1a; 1.Altium Designer 中的 Clearance Constraint 错误如何修改 &#xff1a; 我遇到的报错如下&#xff1a;  这…

Django 后台与便签

1. 什么是后台管理 后台管理是网页管理员利用网页的后台程序管理和更新网站上网页的内容。各网站里网页内容更新就是通过网站管理员通过后台管理更新的。 2. 创建超级用户 1. python .\manage.py createsuperuser 2. 输入账号密码等信息 Username (leave blank to use syl…

【图像拼接】源码精读:Seam-guided local alignment and stitching for large parallax images

第一次来请先看这篇文章&#xff1a;【图像拼接&#xff08;Image Stitching&#xff09;】关于【图像拼接论文源码精读】专栏的相关说明&#xff0c;包含专栏内文章结构说明、源码阅读顺序、培养代码能力、如何创新等&#xff08;不定期更新&#xff09; 【图像拼接论文源码精…

fmincon函数的决策变量可以是二维矩阵,但不建议是高维矩阵

1&#xff09;二维矩阵代码 clear all clc% 定义目标函数 fun (x) sum(sum(x.^2));% 初始矩阵 x0 2 rand(2, 2);% 定义空的线性不等式约束 A []; b [];% 定义空的线性等式约束 Aeq []; beq [];% 定义变量的上下界 lb ones(2,2); ub [];% 使用 fmincon 求解 options …

YOLOv8主干改进 更换柱状神经网络RevCol

一、Reversible Column Networks论文 论文地址:2212.11696.pdf (arxiv.org) 二、Reversible Column Networks结构 Reversible Column Networks 是一种用于量子计算的新型结构。它由一系列可逆操作组成,可以在量子计算中进行高效的信息传递和处理,具有可扩展性、灵活性、…

编程式导航传参

(通过js代码实现跳转) 按照path进行跳转 第一步&#xff1a; 在app.vue中(前提是规则已经配置好) <template><div id"app">App组件<button clicklogin>跳转</button><!--路由出口-将来匹配的组件渲染地方--><router-view>&l…

LeetCode994腐烂的橘子(相关话题:矩阵dfs和bfs)

题目描述 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b;值 1 代表新鲜橘子&#xff1b;值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。 返回 直到单…

【Linux】Linux

Linux 文章目录 Linux1. 简介2. 目录结构3. vi/vim 的使用4. 网络配置4.1 配置网络ip地址4.2 配置主机名或ip映射4.3 远程登陆及上传下载 5. 系统管理5.1 service 服务管理&#xff08;CentOS 6 版本&#xff09;5.2 systemctl 服务管理&#xff08;CentOS 7 版本&#xff09;5…

计算机网络(6):应用层

每个应用层协议都是为了解决某一类应用问题&#xff0c;而问题的解决又往往是通过位于不同主机中的多个应用进程之间的通信和协同工作来完成的。 应用层的具体内容就是规定应用进程在通信时所遵循的协议。 应用层的许多协议都是基于客户服务器方式。即使是对等通信方式&#x…

【Linux学习笔记】解析Linux系统内核:架构、功能、工作原理和发展趋势

操作系统是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体&#xff0c;它们互相依赖&#xff0c;不可分割。计算机的硬件&#xff0c;含有外围设备、处理器、内存、硬盘和其他的电子设备组成计算机的发动机。但是…

(13)Linux 进程的优先级、进程的切换以及环境变量等

前言&#xff1a;我们先讲解进程的优先级。然后讲解进程的切换&#xff0c;最后我们讲解环境变量&#xff0c;并且做一个 "让自己的可执行程序不带路径也能执行"的实践&#xff0c;讲解环境变量的到如何删除&#xff0c;最后再讲几个常见的环境变量。 一、进程优先级…

STL——vector容器

目录 1.vector基本概念 2.vector构造函数 3.vector赋值操作 4.vector容量和大小 5.vector插入和删除 6.vector数据存取 7.vector互换容器 8.vector预留空间 1.vector基本概念 功能&#xff1a;vector数据结构和数组非常相似&#xff0c;也称为单端数组。 vector与普通数…