SpringBoot引入JWT实现用户校验

news/2025/3/19 18:53:05/文章来源:https://www.cnblogs.com/5ran2yl/p/18778741

一JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。这些信息以JSON对象的形式存储在令牌中,并且可以被签名和加密。JWT通常用于身份验证和信息交换
主要用途
  1. 身份验证:
    • 当用户登录成功后,服务器会生成一个JWT并返回给客户端。之后,客户端在每次请求时都会带上这个JWT,通常是通过HTTP头部的Authorization字段(例如:Bearer <token>)。服务器接收到请求后,会验证JWT的有效性(包括签名、过期时间等),从而确认用户的身份。
  2. 信息交换:
    • JWT可以在不同的系统或服务之间安全地传递声明(claims)。由于JWT可以被签名(使用HMAC算法或RSA公私钥对),接收方可以验证内容是否被篡改。

JWT的组成结构

Header(头部):

  • 包含令牌的类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。
{"alg": "HS256","typ": "JWT"
}

Payload(载荷):

  • 包含声明(claims)。声明是关于实体(通常是用户)和其他数据的声明
{"sub": "1234567890","name": "John Doe","iat": 1516239022
}

Signature(签名):

  • 签名用于验证消息在此期间没有被更改,并且对于使用私钥签名的情况,还可以验证发送者的身份。
  • 签名是通过将Base64编码后的Header和Payload与密钥进行加密生成的。
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

JWT导入

 如果您使用的是JDK1.8,那么您只需要导入:

 <!--        JWT依赖--><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>

 

如果您使用的是JDK1.8以上的,则需要多导入一些依赖:

 <!--        JWT依赖--><!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api --><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version></dependency><!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl --><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-impl</artifactId><version>2.3.0</version></dependency><!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core --><dependency><groupId>com.sun.xml.bind</groupId><artifactId>jaxb-core</artifactId><version>2.3.0</version></dependency><!-- https://mvnrepository.com/artifact/javax.activation/activation --><dependency><groupId>javax.activation</groupId><artifactId>activation</artifactId><version>1.1.1</version></dependency>

 

二.搭建一个基本环境

前端代码

前端环境可以自定义搭建,可以就使用HTML+ajax进行前后端交互,也可以使用Vue,这里我使用的Vue,需要四个页面就好了:login页面,index页面(登录成功的页面),其它页面(可随意),以及错误页面

我使用的是vur + axios + vue-router

 第一个登录页面,搭建的使用了elementUI框架,需要使用的可以去elementUI官网下载

<template><div id="app"><div id="formDiv"><el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"><el-form-item label="用户名" prop="pass"><el-input type="text" v-model="ruleForm.pass" autocomplete="off"></el-input></el-form-item><el-form-item label="密码" prop="checkPass"><el-input type="password" v-model="ruleForm.checkPass" autocomplete="off"></el-input></el-form-item><el-form-item><el-button type="primary" @click="submitForm('ruleForm')">提交</el-button><el-button @click="resetForm('ruleForm')">重置</el-button></el-form-item></el-form></div></div>
</template>
<script>
import axios from "axios";export default {data() {var validatePass = (rule, value, callback) => {if (value === '') {callback(new Error('请输入密码'));} else {if (this.ruleForm.checkPass !== '') {this.$refs.ruleForm.validateField('checkPass');}callback();}};var validatePass2 = (rule, value, callback) => {if (value === '') {callback(new Error('请再次输入密码'));}  else {callback();}};return {ruleForm: {pass: '',checkPass: '',},rules: {pass: [{ validator: validatePass, trigger: 'blur' }],checkPass: [{ validator: validatePass2, trigger: 'blur' }]}};},methods: {submitForm(formName) {this.$refs[formName].validate((valid) => {let thisL = this;if (valid) {axios.post("http://localhost:9000/login",{username:this.ruleForm.pass,password:this.ruleForm.checkPass}).then(function (resp) {if (resp.data!==null){window.localStorage.setItem('userInfo',JSON.stringify(resp.data));console.log(resp.data);thisL.$router.replace({path:"/main"})}})} else {console.log('error submit!!');return false;}});},resetForm(formName) {this.$refs[formName].resetFields();}}
}
</script>
<style scoped>
#app{}
#formDiv{width: 400px;height: 300px;margin: 300px auto 0;
}
</style>

 

 第二个页面,首页,也就是跳转成功的页面:

<script>
export default {name: 'Main',data(){return{user:''}},created() {this.user=JSON.parse(window.localStorage.getItem("userInfo"));}
}
</script><template>
<div id="app"><h1 style="text-align: center">欢迎来到首页!尊敬的{{user.username}}</h1><router-link to="/other">去其它页面</router-link>
</div>
</template>

 

第三个页面,其它的页面,就展示一段文本:

<template>
<h1 style="text-align: center">这是其它的页面</h1>
</template>

 

第四个页面,错误页面,也很简单,主要是能跳转就行:

<div id="app"><h1 style="text-align: center">这是错误页面</h1>
</div>

 

vue-router的配置如下:

import Vue from 'vue'
import Router from 'vue-router'
import Login from "../components/Login.vue";
import Main from "../components/Main.vue";
import Other from "../components/other.vue"
import error from "../components/error.vue";
import axios from "axios";
Vue.use(Router);
const router = new Router({mode:'history',routes:[{path:"/login",component:Login},{path:"/main",component:Main},{path:'/other',component:Other},{path:'/error',component:error}]
})
router.beforeEach((to,from,next)=>{if (to.path.startsWith('/login')){window.localStorage.removeItem('userInfo');next();}else {let admin = JSON.parse(window.localStorage.getItem('userInfo'))if (!admin){next({path:'login'})}else {//校验token的合法性
      axios({url:'http://localhost:9000/checkToken',method:'get',headers:{token:admin.token}}).then(resp=>{if (resp.data === 'fail'){console.log('校验失败');next({path:'/error'})}})next();}}
})
export default router;

 

上面有一段代码使用到了钩子函数,在请求进入路由的时候,需要先验证token(验证也是axios异步请求到后端验证的),才会转发

后端代码

 后端最重要的就是两个代码片,一个是controller中的代码,它负责逻辑处理,还有就是成成JWT的工具类

controller:

import jakarta.servlet.http.HttpServletRequest;
import org.cqust.jwt_springboot2.pojo.User;
import org.cqust.jwt_springboot2.utils.JwtUtil;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LoginCTRL {private final String USERNAME="admin";private final String PASSWORD="123456";@RequestMapping("/login")public User checkUser(@RequestBody User user){if (user.getUsername().equals(USERNAME) && user.getPassword().equals(PASSWORD)){//生成token装载String token = JwtUtil.getJWT(user.getUsername(), user.getPassword());user.setToken(token);user.setPassword(null);return user;}return null;}@RequestMapping("/checkToken")public String checkToken(HttpServletRequest req){String token = req.getHeader("token");Boolean result = JwtUtil.checkToken(token);System.out.println("校验结果:"+result);if (result){return "ok";}return "fail";}
}

 

JWT工具类:

import io.jsonwebtoken.*;import java.util.Date;
import java.util.UUID;public class JwtUtil {public static String getJWT(String username,String password){JwtBuilder builder = Jwts.builder();//设置HeaderString compact = builder.setHeaderParam("typ", "JWT").setHeaderParam("alg", "HS256")//payload.claim("username", username).claim("role", "root").claim("password", password).setSubject("admin-root").setExpiration(new Date(System.currentTimeMillis() + 1000 * 60*10))//十分钟过期
                .setId(UUID.randomUUID().toString())//签名signature.signWith(SignatureAlgorithm.HS256, "admin")//加密算法和密钥.compact();//启动连接三部分return compact;}public static Boolean checkToken(String token){try {//不抛出异常说明token存在,则token校验成功//反之,抛出异常说明token有问题,校验失败JwtParser parser = Jwts.parser();Jws<Claims> claimsJws = parser.setSigningKey("admin").parseClaimsJws(token);Claims body = claimsJws.getBody();}catch (Exception e){return false;}return true;}
}

 

测试

测试主要从三个方面:

  1. 正常注册一个admin用户,查看前端是否受到其返回的token并且正常登录
  2. 注册一个其它的用户,查看前端是否可以到首页
  3. 跳转到其它页面,查看后端是否进行了验证

测试一:

正常注册一个admin用户,发现可以到达首页,并且也返回了token

 测试二:

随便构造一个用户之后,我们发现登录不上去,控制台输出错误信息

 测试三:

登录admin用户,然后跳转到其它页面,查看后端是否输出验证信息

 我们可以发现进行了验证,而且验证了两次,第一次验证时登录成功后转发到了main页面,第二次时去其它页面的时候进行验证的

三.引入存储层

上面的实验环境使用的是final修饰的变量作为检验用户登录合法性的凭证,很显然在实际开发中用的很少,故而现在需要引入存储层进行校验,校验用户的信息是否合法应该和数据库存储的数据进行校对

这里使用MySQL + Redis来做,刚开始登录的时候查询数据库,后面校验token的合法性就交给Redis;

在数据库查询出来之后就需要将username信息和生成的token信息缓存到Redis中,后面前端会频繁的校验token的合法性,故而压力比较大所以就交给Redis做

环境安装:

MySQL + Redis

  <!--        mysql驱动器--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><!--        mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.10.1</version></dependency>
<!--        Redis 依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

 

同时填写application.properties文件:

#mysql连接,导入依赖之后就要填,不然启动不了spring容器
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis-plus?useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#redis的基本配置
spring.data.redis.host=127.0.0.1
spring.data.redis.port=6379
spring.data.redis.database=0

 

修改后端代码

mysql数据库表:

CREATE TABLE `user` (`id` bigint(20) NOT NULL,`username` varchar(255) NOT NULL,`password` varchar(255) NOT NULL,`created_time` timestamp NULL DEFAULT NULL,`updated_time` timestamp NULL DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

controller代码:

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.cqust.jwt_springboot2.dao.UserDao;
import org.cqust.jwt_springboot2.pojo.User;
import org.cqust.jwt_springboot2.utils.JwtUtil;
import org.cqust.jwt_springboot2.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class LoginCTRL {//注入UserDao
    @Autowiredprivate UserDao userDao;//注入RedisUtil
    @Autowiredprivate RedisUtil redisUtil;//登录接口@RequestMapping("/login")public User checkUser(@RequestBody User user){//获取用户名和密码String username = user.getUsername();String password = user.getPassword();//先查询redis缓存,缓存中有token可以解密出用户名和密码进行校对//redis中没有再去数据库查询String redisUser = redisUtil.getStr(username);User selectUser=null;//数据库查询if (redisUser == null){//查询数据库QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("username",username);wrapper.eq("password",password);selectUser = userDao.selectOne(wrapper);}//解密token,取出用户名和密码,进行校对//redis查询if (redisUser != null){//解密tokenUser willUser = JwtUtil.checkToken(redisUser);//token过期了,但是缓存还没有过期,也需要重新查询if (willUser == null){//查询数据库QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("username",username);wrapper.eq("password",password);selectUser = userDao.selectOne(wrapper);}//校对用户名和密码if (willUser!=null && username.equals(willUser.getUsername()) && password.equals(willUser.getPassword())){selectUser=willUser;selectUser.setPassword(null);}}//如果查询到用户if (selectUser!=null){//生成tokenString jwt = JwtUtil.getJWT(username, password);selectUser.setToken(jwt);selectUser.setPassword(null);//缓存到redis
            redisUtil.setStr(username,jwt);//设置缓存有效期:三十分钟//由于token的有效期是十分钟,如果用户一直在线可以一直刷新过期时间,则一直查询的是redis;如果用户不是一直在线,则会因为Redis缓存失效而查询数据库redisUtil.setTime(username,60*30);return selectUser;}return null;}//校验token接口@RequestMapping("/checkToken")public String checkToken(HttpServletRequest req){//由前端传递的用户名和tokenString token = req.getHeader("token");String username = req.getHeader("username");//解密tokenUser user = JwtUtil.checkToken(token);//从redis中查询是否真的有这个token和用户名String redisUser = redisUtil.getStr(username);if (redisUser != null){//校验token是否匹配,用户名是否配置if (redisUser.equals(token) && user.getUsername().equals(username)){return "ok";}}//这里返回包含了如下情况 :redis中没有缓存token,缓存的token和新的token不匹配,token中的用户名和登录的用户名不匹配return "fail";}
}

 

 RedisConfig:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Bean@SuppressWarnings("all") // 抑制所有警告public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {// 创建RedisTemplate实例,用于操作RedisRedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();// 设置Redis连接工厂,以便模板能够连接到Redis服务器
        redisTemplate.setConnectionFactory(redisConnectionFactory);// 创建Jackson2JsonRedisSerializer实例,用于将Java对象序列化为JSON格式存储在Redis中Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);// 创建ObjectMapper实例,用于处理JSON数据ObjectMapper mapper = new ObjectMapper();// 设置ObjectMapper的可见性策略,使得所有属性都能被序列化和反序列化
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 启用默认的类型信息,以便在反序列化时能够确定确切的类类型
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);// 将配置好的ObjectMapper设置到Jackson2JsonRedisSerializer中
        jackson2JsonRedisSerializer.setObjectMapper(mapper);// 创建StringRedisSerializer实例,用于将字符串格式的key序列化到Redis中StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();// 设置RedisTemplate的key序列化器为StringRedisSerializer
        redisTemplate.setKeySerializer(stringRedisSerializer);// 设置RedisTemplate的hash key序列化器为StringRedisSerializer
        redisTemplate.setHashKeySerializer(stringRedisSerializer);// 设置RedisTemplate的value序列化器为Jackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// 设置RedisTemplate的hash value序列化器为Jackson2JsonRedisSerializer
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// 初始化RedisTemplate,使配置生效
        redisTemplate.afterPropertiesSet();// 返回配置好的RedisTemplate实例return redisTemplate;}}

 

Redis存储层:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
@SuppressWarnings("all")
public class RedisUtil {@Autowiredprivate RedisTemplate redisTemplate;//使用set方法,插入key-value键值对public void setStr(String key,String value){redisTemplate.opsForValue().set(key,value);}//使用get方法,得到key-valuepublic String getStr(String key){return (String) redisTemplate.opsForValue().get(key);}//设置key的过期时间public Boolean setTime(String key,int seconds){// 设置过期时间Boolean result = redisTemplate.expire(key, seconds, TimeUnit.SECONDS);return result;}//查看剩余过期时间public Long getTTL(String key){return redisTemplate.getExpire(key);}
}

 

解决springBoot和Redis的跨源问题:

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;//解决前后端跨域配置
@Configuration
@MapperScan("org.cqust.jwt_springboot2.dao")
public class CrosConfig implements WebMvcConfigurer {// 重写addCorsMappings方法,用于配置跨域请求
    @Overridepublic void addCorsMappings(CorsRegistry registry) {// 添加映射,允许所有路径registry.addMapping("/**")// 允许所有来源.allowedOriginPatterns("*")// 允许所有请求方法.allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")// 允许发送Cookie.allowCredentials(true)// 预检请求的缓存时间.maxAge(3600)// 允许所有请求头.allowedHeaders("*");}

 

 开始测试

 测试点如下:

  1. 当redis中没有缓存时查询数据库
  2. 当redis中有缓存,查询redis
  3. 当token过期查询数据库

 测试一:redis没有缓存,直接查询数据

redis中为空:

 登录成功:

 后端输出,直接查询的就是数据库:

 测试二:redis缓存存在,直接查询redis登录

依旧登录成功:

 控制台只输出redis相关信息,因为没查询数据库,查询的是redis:

测试三:token过期查询数据库,重新查询数据库

前端依旧登录成功:

 查看后台控制台输出,由于token过期,会切换为查询数据库登录:

 原理剖析

1. 用户登录流程
  • 入口:用户访问登录页面,输入用户名和密码。
  • 前端校验:系统首先进行前端校验,检查输入是否符合格式要求(如用户名非空、密码长度等)。
    • 合法输入:跳转到后端处理。
    • 非法输入:返回登录页,提示用户重新填写。
2. 后端处理与缓存校验
  • 数据提交:前端提交的数据到达后端,触发后端处理逻辑。
  • 缓存检查:后端首先检查用户信息是否存在于Redis缓存中。
    • 缓存命中(用户信息存在):
      • 校验Token:
        • 从Redis中获取用户的Token,检查是否过期。
          • Token过期:生成新Token并更新缓存,跳转至首页。
          • Token未过期:校验Token与用户信息是否匹配。
            • Token匹配:直接跳转至首页。
            • Token不匹配:可能为非法访问,强制跳转至首页或重新登录。
    • 缓存未命中(用户信息不存在于Redis):
      • 数据库查询:后端从数据库中查询用户信息。
        • 用户存在:将用户信息存入Redis缓存,生成新Token并跳转至首页。
        • 用户不存在:跳转至首页或提示登录失败。
3. 路由切换与Token校验
  • 路由触发:当用户尝试访问其他页面或发生路由切换时,系统会触发异步请求,向后端校验当前Token的有效性。
  • 后端Token校验:
    • Token合法:允许路由跳转至目标页面。
    • Token非法或过期:
      • 若用户仍处于登录状态,可能重新生成Token并重试。
      • 若Token无效且未登录,强制跳转至登录页或首页。
4. 异常处理与循环逻辑
  • Token校验失败:若Token多次校验失败,系统会终止当前流程,引导用户重新登录。
  • 循环机制:当Token校验失败时,流程会回到登录页重新开始,形成闭环。
5. 流程终点
  • 成功路径:用户通过校验后,最终跳转至首页或目标页面,流程正常结束。
  • 失败路径:若校验失败或用户主动退出,流程结束于登录页或首页

 

 

------END------

 

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

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

相关文章

mcp-playwright测评

mcp-playwright介绍 mcp-playwright是一个使用 Playwright 提供浏览器自动化能力的模型上下文协议服务器。该服务器使 LLMs 能够与网页交互、截屏,并在真实的浏览器环境中执行 JavaScript。 GitHub地址:https://github.com/executeautomation/mcp-playwright。安装 npm insta…

传媒行业项目管理全解析:日事清在流程、文件、数据与风控中的一体化应用

一、关于森可文化 森可文化传媒有限公司(Senke Vision)占据了超过2800平方米的宽敞办公及拍摄场地,与众多内衣及服饰行业的上市巨头建立了不可动摇的深度合作关系。 Senke Vision汇聚了国内外顶尖的策划、设计、摄影摄像、服装搭配、化妆及品牌视觉顾问,形成了一支独一无二…

在 .NET 项目中使用 husky 完成 pre-commit 操作

将 husky 引入 .NET 项目Husky 是一个用于 Git 仓库的工具,主要用于管理 Git 钩子(hooks)。它可以帮助开发者在特定的 Git 操作(如提交、推送等)之前或之后执行自定义的脚本或命令,从而提高代码质量和团队协作效率。 主要用在前端项目中,可以通过 Husky.Net,将 Husky 的…

贸易企业数字化转型案例:基于日事清的目标管理、任务协作与流程可视化绩效优化实践

这家贸易公司如何提升内部协同效率?一、基本情况 所属行业:传统贸易行业 业务类型:国内贸易、货物及技术进出口 行业地位:拥有自己的研发人员,具备一站式解决方案能力。 合作概要:为解决组织提效,目标体系、协同体系、绩效体系的管理问题,客户与日事清达成合作,并将日…

使用nvm管理node.js版本

1.情景展示如上图所示,项目某个模块支持的node.js最高版本是17,我用的是20,所以只能降级。 2.具体分析 我现在把node.js降到16,那后续如果再需要20呢?能不能实现版本的随时切换? 3.安装nvm 我们可以使用nvm来管理node.js的版本。 下载nvm windows下载地址:https://githu…

3.19 学习记录

完成了仓库管理系统的制作,基本无bug,所展示功能都可以实现,基于 springboot 和 vue3 具体实现如下: 登录管理员页面 有仓库管理,物资管理,库存管理和统计功能仓库工作人员页面

网站测速——提升用户体验的关键

在互联网飞速发展的今天,网站已成为企业展示形象、提供服务以及用户获取信息的重要平台。而网站的速度,如同高速公路的路况,直接影响着用户的访问体验和满意度。因此,网站测速成为了网站运营和维护中不可或缺的关键环节。 网站速度对企业来说真的那么重要吗?​ 网站测速的…

表单和载荷的区别,以及python和js在处理json时的空格问题。

1、在 传载荷的时候,用json= 这样传底层会调用json.dumps来转换,如:response = requests.post(url,headers=headers,params=params,data=json.dumps(data.separators=(,":")) 2、在 传表单的时候,用data= 如:response = requests.post(url,headers=headers,…

荣誉| 触想TPC07-WIPC工控机荣获2025控制产品“新质”奖!

3月13日,第23届中国自动化与数字化“新质奖”评选颁奖典礼在无锡举办,触想自主研发的TPC07-WIPC工控机以卓越性能、灵活扩展和可靠性备受关注,获得2025控制产品“新质”奖。△ 触想代表朱涛(左四)上台领奖TPC07-WIPC系列工控机是触想面向边缘计算、机器视觉、AI智能等场景推…

汉诺塔游戏 | 数学益智游戏②

依托国产的 AI 豆包完成的网页端的汉诺塔益智游戏。前情概要 AI 辅助设计的一款数学益智游戏,汉诺塔游戏。 汉诺塔游戏body0 {font-family: Arial, sans-serif; text-align: left; background-color: #f4f4f9; display: flex; flex-direction: column; align-items: center; }…

20242801 2024-2025-2 《网络攻防实践》第3次作业

20242801 2024-2025-2 《网络攻防实践》第3次作业 一、实验内容使用tcpdump等网络流量分析工具对网络登陆过程进行嗅探,统计登陆通过中访问的ip地址信息。 使用wireshark或科来等网络分析工具,对以TELNET方式登陆BBS过程进行嗅探,查看BBS服务器的ip地址和访问端口,以及使用…

python 实验一(20241116魏铼)

课程:《Python程序设计》 班级: 2411 姓名: 魏铼 学号:20241116 实验教师:王志强 实验日期:2025年3月19日 必修/选修: 公选课 1.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能;(编写书中的程序,并进行调试分析,要有过程) 3.编写程序,练习变量和…