001 登录(md5加密)

news/2024/5/20 1:56:31

文章目录

    • pom.xml
    • LoginController.java
    • UserMapper.java
    • User.java
    • UserServiceImpl.java
    • UserService.java
    • MD5Util.java
    • MD5UtilTest.java
    • ValidatorUtil.java
    • LoginVo.java
    • RespBean.java
    • RespBeanEnum.java
    • SeckillApplication.java
    • UserMapper.xml
    • login.html
    • application.yaml
    • sql

传统方式:
客户端发送一个密码明文,后端用md5加密明文,判断加密后的与数据库存放的密码是否一致

改进:客户端发送的时候将密码明文md5加密,后端再用md5加密一次,判断加密后的与数据库存放的密码是否一致

改进升级版:客户端发送的时候将密码明文和salt混起来再用md5加密,后端把发送过来的再加salt并用md5加密,判断加密后的与数据库存放的密码是否一致

在实际应用中,js-md5 库可以被用来生成 MD5 哈希值,这通常用于验证数据的完整性或在不需要高度安全性的场景下存储密码的哈希值(注意,由于 MD5 已知存在碰撞性漏洞,因此在需要高安全性的场景,如密码存储,推荐使用更安全的哈希算法,如 SHA-256

pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>seckill</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>seckill</name><description>seckill</description><properties><java.version>1.8</java.version></properties><dependencies><!-- 工程创建需要引入的相关jar --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><!--md5依赖--><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.11</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.7.2</version><scope>compile</scope></dependency><!-- 引入validation依赖,完成校验 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.4.5</version></dependency><!--pool2 对象池依赖--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.9.0</version></dependency><!--引入hutool依赖-工具类--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

LoginController.java


package com.example.controller;import com.example.service.UserService;
import com.example.vo.LoginVo;
import com.example.vo.RespBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Controller
@RequestMapping("/login")
@Slf4j
public class LoginController {//装配UserService@Resourceprivate UserService userService;//编写方法,可以进入到登录页面@RequestMapping("/toLogin")public String toLogin() {return "login";//到templates/login.html}//处理用户登录请求@RequestMapping("/doLogin")@ResponseBodypublic RespBean doLogin(LoginVo loginVo,HttpServletRequest request,HttpServletResponse response) {//log.info("{}",loginVo);return userService.doLogin(loginVo,request,response);}}

UserMapper.java


package com.example.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.pojo.User;public interface UserMapper extends BaseMapper<User> {}

User.java


package com.example.pojo;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.util.Date;@Data
@TableName("seckill_user")
public class User implements Serializable {private static final long serialVersionUID = 1L;/*** 用户ID,手机号码*/@TableId(value = "id", type = IdType.ASSIGN_ID)//不是自增长的private Long id;private String nickname;/*** MD5(MD5(pass明文+固定salt1)+salt2)*/private String password;private String salt;/*** 头像*/private String head;/*** 注册时间*/private Date registerDate;/*** 最后一次登录时间*/private Date lastLoginDate;/*** 登录次数*/private Integer loginCount;}

UserServiceImpl.java


package com.example.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.mapper.UserMapper;
import com.example.pojo.User;
import com.example.service.UserService;
import com.example.util.MD5Util;
import com.example.util.ValidatorUtil;
import com.example.vo.LoginVo;
import com.example.vo.RespBean;
import com.example.vo.RespBeanEnum;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Resourceprivate UserMapper userMapper;@Overridepublic RespBean doLogin(LoginVo loginVo, HttpServletRequest request, HttpServletResponse response) {//接收到mobile和password[midPass]String mobile = loginVo.getMobile();String password = loginVo.getPassword();//判断手机号/id 和密码是否为空if(!StringUtils.hasText(mobile) || !StringUtils.hasText(password)) {return RespBean.error(RespBeanEnum.LOGIN_ERROR);}//校验手机号码是否合格if(!ValidatorUtil.isMobile(mobile)) {return RespBean.error(RespBeanEnum.MOBILE_ERROR);}//查询DB,看看用户是否存在User user = userMapper.selectById(mobile);if(null == user) {//说明用户不存在return RespBean.error(RespBeanEnum.LOGIN_ERROR);}//如果用户存在,则比对密码//注意,从loginVo取出的密码是中间密码(即客户端经过一次加密加盐处理的密码)if(!MD5Util.midPassToDBPass(password,user.getSalt()).equals(user.getPassword())){return RespBean.error(RespBeanEnum.LOGIN_ERROR);}//登录成功return RespBean.success();}
}

UserService.java


package com.example.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.example.pojo.User;
import com.example.vo.LoginVo;
import com.example.vo.RespBean;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** 传统方式 在接口中 定义方法/声明方法,然后在实现类中进行实现* 在mybatis-plus中,我们可以继承父接口IService* 这个IService接口声明很多方法,比如crud* 如果默认提供方法不能满足需求,可以再声明需要的方法,然后在实现类中实现*/public interface UserService extends IService<User> {//方法 完成用户的登录校验RespBean doLogin(LoginVo loginVo, HttpServletRequest request,HttpServletResponse response);
}

MD5Util.java


package com.example.util;import org.apache.commons.codec.digest.DigestUtils;/*** 工具类,根据前面密码设计方案提供相应的方法*/
public class MD5Util {public static String md5(String src) {return DigestUtils.md5Hex(src);}//前端使用的盐private static final String SALT = "4tIY5VcX";//加密加盐 md5(password明文+salt1)public static String inputPassToMidPass(String inputPass) {System.out.println("SALT.charAt(0)->"+SALT.charAt(0));System.out.println("SALT.charAt(6)->"+SALT.charAt(6));String str = SALT.charAt(0) + inputPass + SALT.charAt(6);return md5(str);}//加密加盐  MidPass + salt2转成DB中的密码//md5(md5(password明文+salt1)+salt2)public static String midPassToDBPass(String midPass,String salt) {System.out.println("SALT.charAt(1)->"+salt.charAt(1));System.out.println("SALT.charAt(2)->"+salt.charAt(2));String str = salt.charAt(1) + midPass + salt.charAt(2);return md5(str);}//将password明文转成DB中的密码public static String inputPassToDBPass(String inputPass,String salt){String midPass = inputPassToMidPass(inputPass);String dbPass = midPassToDBPass(midPass,salt);return dbPass;}}

MD5UtilTest.java


package com.example.util;import org.junit.jupiter.api.Test;/*** 测试MD5Util*/
public class MD5UtilTest {@Testpublic void fun1(){//密码明文"123456"//获取到密码明文"123456"的中间密码[即客户端加密加盐后],在网络上传输的密码//第一次加密加盐处理//这个加密加盐会在客户端/浏览器完成System.out.println(MD5Util.inputPassToMidPass("123456"));//中间密码"d5c1b5290593325eebe18c9b7b146d31"的对应的DB密码System.out.println(MD5Util.midPassToDBPass("2028ad83f1997056c7d60e16c36d10a7","tjAyuopC"));
//
//
//        //密码明文 "123456"直接得到存放在DB密码System.out.println(MD5Util.inputPassToDBPass("123456","tjAyuopC"));}
}

ValidatorUtil.java


package com.example.util;import org.junit.jupiter.api.Test;
import org.springframework.util.StringUtils;import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** 完成一些校验,比如手机号格式是否正确* 正则*/
public class ValidatorUtil {//校验手机号码的正则表达式private static final Pattern mobile_pattern = Pattern.compile("^[1][3-9][0-9]{9}$");//编写方法public static boolean isMobile(String mobile) {if(!StringUtils.hasText(mobile)) {return false;}Matcher matcher = mobile_pattern.matcher(mobile);return matcher.matches();}//测试校验方法@Testpublic void t1() {String mobile = "0133333333";System.out.println(isMobile(mobile));String mobile2 = "13333333333";System.out.println(isMobile(mobile2));}}

LoginVo.java


package com.example.vo;import lombok.Data;/*** 接收用户登录时,发送的信息(moblie,password)*/
@Data
public class LoginVo {private String mobile;private String password;}

RespBean.java


package com.example.vo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class RespBean {private long code;private String message;private Object obj;//成功后 同时携带数据public static RespBean success(Object data) {return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(), data);}//成功后 不携带数据public static RespBean success() {return new RespBean(RespBeanEnum.SUCCESS.getCode(),RespBeanEnum.SUCCESS.getMessage(), null);}//失败 不携带数据public static RespBean error(RespBeanEnum respBeanEnum) {return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(), null);}//失败 携带数据public static RespBean error(RespBeanEnum respBeanEnum,Object data) {return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(), data);}}

RespBeanEnum.java


package com.example.vo;import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;/*** 枚举类*/
@Getter
@ToString
@AllArgsConstructor
public enum RespBeanEnum {//通用SUCCESS(200,"SUCCESS"),ERROR(500,"服务端异常"),//登录LOGIN_ERROR(500210,"用户id或者密码错误"),MOBILE_ERROR(500211,"手机号码格式不正确"),MOBILE_NOT_EXIST(500213,"手机号码不存在");private final Integer code;private final String message;}

SeckillApplication.java


package com.example;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.example.mapper")
public class SeckillApplication {public static void main(String[] args) {SpringApplication.run(SeckillApplication.class, args);}}

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="BaseResultMap" type="com.example.pojo.User"><id column="id" property="id"/><result column="nickname" property="nickname"/><result column="password" property="password"/><result column="salt" property="salt"/><result column="head" property="head"/><result column="register_date" property="registerDate"/><result column="last_login_date" property="lastLoginDate"/><result column="login_count" property="loginCount"/></resultMap>
</mapper>

login.html


<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录</title><script type="text/javascript" th:src="@{/js/jquery.min.js}"></script><!-- md5.js --><script type="text/javascript" th:src="@{/js/md5.min.js}"></script><!-- common.js --><script type="text/javascript" th:src="@{/js/common.js}"></script></head>
<body>
<div class="container"><div class="welcome"><div class="pinkbox"><!-- 登录表单 --><div class="signin"><h1>用户登录</h1><form class="more-padding" id="loginForm" method="post" autocomplete="off"><input style="background-color: darkgrey;" id="mobile" name="mobile" type="text" placeholder="手机号码"required="true"/><input style="background-color: darkgrey;" id="password" name="password" type="password"placeholder="密码" required="true"/><button class="button sumbit" type="button" onclick="login()">Login</button></form></div></div></div>
</div></body><script>function login() {doLogin();}function doLogin() {//得到用户在登录表单填写的密码var inputPass = $("#password").val();//客户端盐var salt = g_passsword_salt;var str = "" + salt.charAt(0) + inputPass + salt.charAt(6);var password = md5(str);console.log("inputPass->", inputPass)console.log("salt->", salt)console.log("password->", password)$.ajax({url: "/login/doLogin",type: "POST",data: {mobile: $("#mobile").val(),password: password},success: function (data) {if (data.code == 200) {alert(data.message)//如果code是200,说明登录成功., 就直接进入其他页面//window.location.href=""} else {alert(data.message)}},error: function () {alert("失败");}});}</script>
</html>

application.yaml


spring:thymeleaf:#关闭缓存cache: falsedatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seckill?useUnicode=true&characterEncoding=utf-8&useSSL=trueusername: rootpassword: 123456# 数据库连接池hikari:#连接池名pool-name: dd_Poll#最小空闲连接minimum-idle: 5#空闲连接存活最大时间,默认60000(10分钟)idle-timeout: 60000# 最大连接数,默认是10maximum-pool-size: 10#从连接池返回来的连接自动提交auto-commit: true#连接最大存活时间。0表示永久存活,默认180000(30分钟)max-lifetime: 180000#连接超时时间,默认30000(30秒)connection-timeout: 30000#测试连接是否可用的查询语句connection-test-query: select 1#mybatis-plus配置
mybatis-plus:#配置mapper.xml映射文件mapper-locations: classpath*:/mapper/*Mapper.xml#配置mybatis数据返回类型别名type-aliases-package: com.example.pojo
#mybatis sql 打印
#logging:
#  level:
#    com.example.mapper: debug
#server:
#  port: 9999

sql


DROP TABLE IF EXISTS `seckill_user`;
CREATE TABLE `seckill_user` (`id` BIGINT(20) NOT NULL COMMENT '用户ID,设为主键,唯一手机号',`nickname` VARCHAR(255) NOT NULL DEFAULT '',`password` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'MD5(MD5(pass明文+固定salt)+salt)',`salt` VARCHAR(10) NOT NULL DEFAULT '',`head` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '头像',`register_date` DATETIME DEFAULT NULL COMMENT '注册时间',`last_login_date` DATETIME DEFAULT NULL COMMENT '最后一次登录时间',`login_count` INT(11) DEFAULT '0' COMMENT '登录次数',PRIMARY KEY(`id`)) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

http://www.mrgr.cn/p/25383855

相关文章

2024-05-08:用go语言,给定一个由正整数组成的数组 nums, 找出数组中频率最高的元素, 然后计算该元素在数组中出现的总次数。 输入:nums = [1,2,2,3,1,4]。 输出:4。

2024-05-08:用go语言,给定一个由正整数组成的数组 nums, 找出数组中频率最高的元素, 然后计算该元素在数组中出现的总次数。 输入:nums = [1,2,2,3,1,4]。 输出:4。 答案2024-05-08: chatgpt 题目来自leetcode3005。 大体步骤如下: 1.创建一个空的字典 cnt 用于存储每个…

报错:Error: Cannot find module

报错详情: 解决方法: 1、正确安装版本号的nodejs 2、删除项目根文件夹下的node_modules和“package-lock.json”, 3、重新执行npm install

上课学习(无线网络)

考红色部分:如什么协议采用集中式架构

UG NX二次开发(C#)-获取Part中对象创建时的序号(*)

文章目录 1、前言2、UG NX的对象序号讲解3、采用UG NX二次开发或者建模序号4、注意事项1、前言 在UG NX中,我们创建任意一个对象,都会在模型历史中添加一个创建对象的编号,即是对象序号,这个是递增的,当删除中间产生的对象时,其序号会重新按照建模顺序重新排布。今天一个…

2024CVPR_Low-light Image Enhancement via CLIP-Fourier Guided Wavelet Diffusion(CFWD)

一、Motivation 1、单模态监督问题:大多数方法往往只考虑从图像层面监督增强过程,而忽略了图像的详细重建和多模态语义对特征空间的指导作用。这种单模态监督导致不确定区域的次优重建和较差的局部结构,导致视觉结果不理想的出现。------》扩散模型缺乏有效性约束,容易出现…

PDF批量编辑技巧:高效PDF转txt批量处理,轻松管理大量文档

随着信息技术的飞速发展&#xff0c;文档管理已成为日常工作中不可或缺的一部分。特别是当我们需要处理大量的PDF文件时&#xff0c;如何高效地进行编辑、转换和管理成为了一个重要的问题。本文将介绍一些PDF批量编辑的技巧&#xff0c;特别是如何将PDF批量转换为txt格式&#…

Redis单机安装

1.编译 cd redis安装目录 makemake install2.修改配置文件redis.conf #端口修改 port 6379 #后台进程启动 yes daemonize yes # daemonize no #注释掉 为了可以远程连接 #bind 127.0.0.1 #设置密码 requirepass pwd3.启动 ./redis-server ../redis.conf查看进程 [rootlocal…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布&#xff0c;浙江大学区块链协会收到WTF Academy的赞助与支持&#xff0c;未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学&#xff0c;旨在通过开源教育让100,000名开发者进入到Web3。截止目前&#xff0c;WTF开源教程在GitHub收获超15,000 ⭐&a…

【GPT调用】本地使用python调用GPT接口

python调用GPT接口 环境变量设置主调用方法执行结果 环境变量设置 .env文件中配置GPT环境变量 api_key"你的GPT-API-KEY" urlhttps://ai-proxy.ksord.com/wps.openai.azure.com/openai/deployments/gpt-4-32k/chat/completions?api-version2023-09-01-preview主调…

Jmeter-线程组下篇

线程组 线程组作为JMeter测试计划的核心组件之一,对于模拟并发用户的行为至关重要。线程组元件是整个测试计划的入口,所有的取样器和控制器必须放置在线程组下。 可以将线程组视为一个虚拟用户池,其中每个线程可被理解为一个虚拟用户,多个虚拟用户同时执行相同的一批任务。…

stable diffusion 之云端部署攻略

本文主要介绍stable diffusion云端产品以及使用步骤 ℹ️整合安装包、模型资源见文末~ megaease cloud&#xff08;强烈推荐&#xff09; 优点&#xff1a; 集成了常用大模型和插件、VAE3080显卡配置&#xff0c;费用大概0.48元/小时&#xff0c;可随时暂停&#xff0c;暂停…

【自然语言处理】seq2seq模型——机器翻译

seq2seq模型——机器翻译 1 任务目标 1.1 案例简介 seq2seq是神经机器翻译的主流框架&#xff0c;如今的商用机器翻译系统大多都基于其构建&#xff0c;在本案例中&#xff0c;我们将使用由NIST提供的中英文本数据训练一个简单的中英翻译系统&#xff0c;在实践中学习seq2se…

腾讯游戏海外扩张,增持芬兰游戏开发商股份持股比例增至14.8%

易采游戏网5月8日消息&#xff0c;近日腾讯再次出手&#xff0c;大幅增持了芬兰知名游戏开发商Remedy Entertainment的股份&#xff0c;持股比例猛增至14.8%。这一举动引起了业界和投资者的广泛关注。 据了解&#xff0c;腾讯此次增持是在2024年4月24日完成的。根据芬兰法律规…

Ubuntu下halcon软件的下载安装

由于工作需求&#xff0c;点云配准需要使用halcon进行实现&#xff0c;并且将该功能放入QT界面中 1.下载halcon 进入halcon官网进行下载 官网链接&#xff1a;https://www.mvtec.com/products/halcon/ 注意&#xff1a;要注册登陆之后才能进行下载 接着点击Downloads->H…

Axure实现菜单抽屉效果

Axure是怎么实现如下效果的&#xff1f; 菜单打开和收起侧边栏菜单抽屉效果 实现效果 两级菜单&#xff0c;点击菜单收起其他菜单&#xff0c;打开当前菜单。 实现原理 单击一级菜单时&#xff0c;1&#xff09;切换当下二季菜单的显示/隐藏状态 2&#xff09;隐藏其他菜单…

网络安全之ACL

ACL&#xff1a;访问控制列表——控制列表&#xff08;策略列表&#xff09;&#xff0c;是一个控制工具。 功能&#xff1a;&#xff01;、定义感兴趣路由&#xff08;控制层面&#xff09;。2、定义感兴趣流量&#xff08;数据层面&#xff09;。 例如&#xff1a; 假设在该…

【MsSQL】数据库基础 库的基本操作

目录 一&#xff0c;数据库基础 1&#xff0c;什么是数据库 2&#xff0c;主流的数据库 3&#xff0c;连接服务器 4&#xff0c;服务器&#xff0c;数据库&#xff0c;表关系 5&#xff0c;使用案例 二&#xff0c;库的操作 1&#xff0c;创建数据库 2&#xff0c;创建…

【攻防技术系列+Python】-- 用 Python 控制系统进程

用 Python 控制系统进程 由于注册表几乎可以决定整个操作系统的运行,因此它成为安全工具与恶意软件对抗的主要战场之一。除了注册表之外,对系统进程的控制也是安全工具和恶意软件的必争之地。这里我们首先要了解程序和进程的区别。程序是静态的,进程是动态的。进程可以分为系…

两个手机在一起ip地址一样吗?两个手机是不是两个ip地址

在数字时代的浩瀚海洋中&#xff0c;手机已经成为我们生活中不可或缺的一部分。随着移动互联网的飞速发展&#xff0c;IP地址成为了连接手机与互联网的桥梁。那么&#xff0c;两个手机在一起IP地址一样吗&#xff1f;两个手机是不是两个IP地址&#xff1f;本文将带您一探究竟&a…

【快速入门Linux】10_Linux命令—Vi编辑器

文章目录 一、vi 简介1.1 vi1.2 vim1.3查询软连接命令&#xff08;知道&#xff09; 二、打开和新建文件&#xff08;重点&#xff09;2.1 打开文件并且定位行2.2 异常处理 三、vi三种工作模式&#xff08;重点&#xff09;3.1 末行模式-命令 四、常用命令4.0 命令线路图4.1 移…