超详细的前后端实战项目(Spring系列加上vue3)前后端篇(四)(一步步实现+源码)
兄弟们,继昨天的代码之后,继续完成最后的用户模块开发,
昨天已经完成了关于用户的信息编辑页面这些,今天再完善一下,
从后端这边开始吧,做一个拦截器,对用户做身份校验,
拦截器
这边先解释一下吧:
那就再用面试题来细说:
拦截器相关面试题:过滤器与拦截器有什么区别?
答:
- 一:运行的顺序不同
- 过滤器是servlet容器接收到请求之后,在servlet被调用之前运行的
- 拦截器则是在servlet被调用之后,但是在响应被发送到客户端之前运行的
- 二:配置方式不同
- 过滤器是在web.xml配置
- 拦截器是在spring的配置文件中配置,或者基于注解进行配置
- 三:依赖关系
- Filter依赖于Servlet容器,而Interceptor不依赖于Servlet容器
- 四:能力方面
- Filter在过滤器中只能对request和response进行操作
- Interceptor可以对request,response,handler,modelAndView,exception,相当于Interceptor多了对SpringMvc生态下组件的一个操作能力
- 接口规范不同
- 过滤器需要实现Filter接口,拦截器需要实现HandlerInterceptor接口
- 拦截范围不同
- 过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源
OK,来做一个拦截器:
创建一个interceptors包,并创建LoginInterceptor类
拦截器的类要继承
HandlerInterceptor
注:
首先,创建拦截器类时,需要实现
HandlerInterceptor
接口。这个接口定义了三个方法:preHandle()
、postHandle()
和afterCompletion()
。preHandle()
方法是在控制器方法执行之前调用的,它返回一个布尔值,表示是否继续执行后续操作;postHandle()
方法是在控制器方法执行之后,视图解析之前调用的,可以用来对响应进行进一步的处理;afterCompletion()
方法是在整个请求处理完成之后调用的,通常用于资源的清理工作。其次,除了实现
HandlerInterceptor
接口,还可以选择继承HandlerInterceptorAdapter
类来简化拦截器的实现。HandlerInterceptorAdapter
提供了HandlerInterceptor
接口的空实现,使得开发者只需要重写自己关心的方法即可。最后,为了让拦截器生效,还需要在Spring配置文件中进行相应的配置。这通常涉及到定义一个配置类,实现
WebMvcConfigurer
接口,并重写addInterceptors()
方法。在这个方法中,可以使用addPathPatterns()
来指定拦截路径,使用excludePathPatterns()
来指定排除的路径。在实际开发中,根据需求选择合适的拦截器类型是非常重要的。例如,如果需要在Controller层进行权限验证,那么使用
HandlerInterceptor
接口是合适的;如果只是对请求进行简单的预处理和后处理,那么可以考虑使用WebRequestInterceptor
接口。选择合适的拦截器类型可以确保代码的整洁性和可维护性。
下面给出代码
import org.example.cetidenet.utils.JwtUtil;
import org.example.cetidenet.utils.ThreadLocalUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return false;
}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
这里继承HandlerInterceptor之后按快捷键Ctrl+o,然后继承三种方法就行了,这里常用的是第一种preHandle,在请求到Controller层之前会被preHandle拦截并处理,如果返回不为true,则会被拦截。
创建了拦截器之后还要注册拦截器。
这里就在之前创建的config包下进行操作
在之前加载Swagger资源的类下加上代码:
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//登录接口和注册接口不拦截registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login","/user/register","/doc.html#/home");}
这里有个小坑(之前单独创建了一个类继承的是WebMvcConfigurer,结果拦截器缺没有起作用,如果是这样,就继承WebMvcConfigueationSupport)
添加了拦截的的路径和放行的路径之后就可以进行检验了
OK,这里使用拦截器之后,登录接口可以正常使用,
而使用更新接口则返回401没有权限。那也就成功了。
JWT令牌:
那么使用了拦截器之后拦截了除了登录注册之外的其他的接口,那该如何才能让其使用呢,必然需要验证身份,这里可以使用JWT令牌,
也就是在用户登录完成之后发放JWT令牌,然后反馈给前端,前端携带其到后端,拦截器对其进行校验,如果带有JWT令牌就放行。
后端引入JWT令牌依赖
<!--java-jwt坐标--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency>
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
import java.util.Map;
public class JwtUtil {private static final String KEY = "CeTide";//接收业务数据,生成token并返回public static String genToken(Map<String, Object> claims) {return JWT.create().withClaim("claims", claims).withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 )).sign(Algorithm.HMAC256(KEY));}//接收token,验证token,并返回业务数据public static Map<String, Object> parseToken(String token) {return JWT.require(Algorithm.HMAC256(KEY)).build().verify(token).getClaim("claims").asMap();}}
然后在登录接口这里添加代码:
public Result<String> login(UserLoginDTO userLoginDTO) {//先获取DTO中的账号面膜String username = userLoginDTO.getUserName();String password = userLoginDTO.getPassword();//先查询数据库中是否有这号人物User user = userMapper.findByUsername(username);//判断是否存在if(user == null){return Result.error("该用户不存在");}String salt = password + "ceTide";String pwd = DigestUtils.md5Hex(salt.getBytes()).toUpperCase();//存在,判断密码是否正确if(!user.getPassword().equals(pwd)){return Result.error("用户密码错误");}
// boolean isLog = logService.addUserLogin(user);
// if(!isLog){
// return Result.error("用户登录日志记录失败");
// }//登录成功Map<String, Object> claims = new HashMap<>();claims.put("id", user.getId());claims.put("username", user.getUserName());String token = JwtUtil.genToken(claims);//存在且密码正确return Result.success(token);}
将用户的id和username放入到token中(虽然但是,尽量不要把密码放进去)
然后在拦截器这里进行校验:
@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//令牌验证System.out.println("开始验证");String token = request.getHeader("Authorization");//验证令牌try{Map<String,Object> claims = JwtUtil.parseToken(token);//放行return true;}catch (Exception e) {//反馈响应码401response.setStatus(401);return false;}}
这里如果能够解析就代表拥有JWT令牌,那也就可以放行了
现在回到前端这里:
先看一下修改后的前端代码
<template><div id="userCenter"><a-layout style="height: 100vh"><a-layout-header><a-page-header title="用户中心" subtitle="CeTide网" @click="returnPage"/><div class="header"><div class="user-introduce"><img:src="userNum.userImg"width="70px"height="70px"class="user-img"/><div><div class="personal-introduce"><div style="margin-left: 10px"><span class="name">{{ userNum.userName }}</span><span class="sex-icon"></span></div><a-space class="btn"><a-button type="dashed" shape="round" @click="handleClick"><icon-pen-fill />编辑资料</a-button><a-button type="dashed" shape="round"><icon-settings />设置</a-button><a-button type="dashed" shape="round"><icon-list />文章管理</a-button></a-space></div></div></div><div class="user-follow">{{ userNum.attention }}<icon-star /><span class="follow-num">关注</span>{{ userNum.fans }}<icon-heart /><span class="follow-num">粉丝</span>{{ userNum.article }}<icon-select-all /><span class="follow-num">文章</span></div><div class="user-follow">个人简介:{{ userSelfIntroduce }}</div></div></a-layout-header><a-layout style="margin: 24px 180px"><a-layout-sider :resize-directions="['right']"><a-layoutstyle="height: 100%;text-align: left;padding-left: 20px;background-color: #c4c4c4;"><a-layout-content style="height: 20%"><h3>CeTide等级</h3></a-layout-content><a-layout-content style="height: 20%"><h3>个人成就</h3></a-layout-content><a-layout-content style="height: 60%"><h3>个人动态</h3></a-layout-content></a-layout></a-layout-sider><a-layout-content class="content"><h3>用户中心</h3></a-layout-content></a-layout><a-layout-footer>Footer</a-layout-footer></a-layout><!-- 编辑个人信息的抽屉 --><a-drawer:visible="visible":width="500"@ok="handleOk"@cancel="handleCancel"unmountOnClose><template #title> 编辑个人信息 </template><div :style="{ marginBottom: '20px' }"><div ><img :src="userNum.userImg" width="70px" height="70px" class="user-img"/><a-button type="primary" @click="handleNestedClick" style="float: right;margin-top: 20px">更换头像</a-button></div><a-divider /><div> 用户名:<a-input :style="{width:'320px'}" allow-clear v-model="userNum.userName"/></div><a-divider /><div> 性别:<a-input :style="{width:'320px'}" v-model="numSex" /></div><a-divider /><div> 电话:<a-input :style="{width:'320px'}" v-model="userNum.phone"/></div><a-divider /><div> 生日:<a-input :style="{width:'320px'}" v-model="userNum.birthday" /></div><a-divider /><div> 城市:<a-input :style="{width:'320px'}" v-model="userNum.county" /></div><a-divider /><div> 住址:<a-input :style="{width:'320px'}" v-model="userNum.address" /></div><a-divider /><div> CeTide网ID:<a-input :style="{width:'320px'}" v-model="userNum.id" disabled/></div><a-divider /><div> 个人简介: <a-textarea v-model="userSelfIntroduce" allow-clear style="height: 100px"/></div></div></a-drawer><a-drawer:visible="nestedVisible"@ok="handleNestedOk"@cancel="handleNestedCancel"unmountOnClose><template #title> 文件操作 </template><a-space direction="vertical" :style="{ width: '100%' }" class="picture"><a-uploadaction="/":fileList="file ? [file] : []":show-file-list="false"@change="onChange"@progress="onProgress"><template #upload-button><div:class="`arco-upload-list-item${file && file.status === 'error' ? ' arco-upload-list-item-error' : ''}`"><divclass="arco-upload-list-picture custom-upload-avatar"v-if="file && file.url"><img :src="file.url" /><div class="arco-upload-list-picture-mask"><IconEdit /></div><a-progressv-if="file.status === 'uploading' && file.percent < 100":percent="file.percent"type="circle"size="mini":style="{position: 'absolute',left: '50%',top: '50%',transform: 'translateX(-50%) translateY(-50%)',}"/></div><div class="arco-upload-picture-card" v-else><div class="arco-upload-picture-card-text"><IconPlus /><div style="margin-top: 10px; font-weight: 600">Upload</div></div></div></div></template></a-upload></a-space></a-drawer></div>
</template><script setup>
import {userGetInfo} from '../api/user';
import { ref } from "vue";
import avatar from "../assets/userbg.png";
import { useRouter } from "vue-router";
const router = useRouter();
const userInfoList= async()=>{let result = await userGetInfo();userNum.value = result.data;
}
userInfoList();
const userSelfIntroduce = ref("这个人很懒,什么都没有留下");
const userNum = ref({id: "007",county: "四川",address: "成都",phone: "12345678910",birthday: "1999-09-09",gender: "女",email: "123@qq.com",userImg: avatar,userName: "我是小丑",attention: 0,fans: 0,article: 0,
});
const numSex = ref(userNum.value.gender === 'F' ? '男' : '女');
//抽屉显示隐藏
const visible = ref(false);
const nestedVisible = ref(false);const handleClick = () => {visible.value = true;
};
const handleOk = () => {visible.value = false;
};
const handleCancel = () => {visible.value = false;
};
const handleNestedClick = () => {nestedVisible.value = true;
};
const handleNestedOk = () => {nestedVisible.value = false;
};
const handleNestedCancel = () => {nestedVisible.value = false;
};//返回方法
const returnPage = () =>{router.push('/')
}//
</script><style lang="scss" scoped>
#userCenter {background: url("../assets/image.png") no-repeat bottom center / 100% 100%;
}
.header {font-family: "Satisfy", cursive;margin: 2% 100px;height: 20vh;background: url("../assets/back.png") no-repeat center / 100% 100%;position: relative;
}.personal-introduce {display: flex;justify-content: center;align-items: flex-end;margin-top: 10px;text-shadow: 0px 0px 4px rgba(0, 0, 0, 0.31);.name {line-height: 29px;font-size: 26px;}.sex-icon {display: inline-block;width: 16px;height: 16px;margin: 0px 8px;margin-bottom: 4px;background: url(../assets/user-images/sex-icon.png) no-repeat center;background-size: contain;border-radius: 50%;}.level-icon {display: inline-block;width: 16px;height: 16px;margin-bottom: 4px;background: url(../assets/user-images/leval-icon.png) no-repeat center;background-size: contain;border-radius: 50%;}
}.user-introduce {display: flex;justify-items: left;padding: 10px;
}
.user-img {border-radius: 50%;margin-left: 20px;
}
.user-follow {margin-left: 30px;font-size: 16px;display: flex;justify-items: left;
}
.follow-num {font-size: 16px;padding-right: 20px;
}
.content {margin-left: 70px;background-color: #c4c4c4;
}
.btn {position: absolute;right: 40px;
}</style>
在登录之后获取到了JWT值
“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjE1LCJ1c2VybmFtZSI6IueUqOaIt1VTRERBRCJ9LCJleHAiOjE3MTY4MDQ4ODB9._9GDxuux5wmoV5CsZCd0QI3wByESKWGGZCKmDaZVlbc”
这样一大串字,然后前端将其放置在请求头的Authorization属性中
举个例子
export const userGetInfo= () =>{return request.get('/user/getInfo',{headers : {'Authorization' : 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGFpbXMiOnsiaWQiOjE1LCJ1c2VybmFtZSI6IueUqOaIt1VTRERBRCJ9LCJleHAiOjE3MTY4MDQ4ODB9._9GDxuux5wmoV5CsZCd0QI3wByESKWGGZCKmDaZVlbc',}});}
此时访问页面,就能得到用户名等信息了
然后就算是成功实现了请求的拦截与用户身份的检验
相关文章:

超详细的前后端实战项目(Spring系列加上vue3)前后端篇(四)(一步步实现+源码)
兄弟们,继昨天的代码之后,继续完成最后的用户模块开发, 昨天已经完成了关于用户的信息编辑页面这些,今天再完善一下, 从后端这边开始吧,做一个拦截器,对用户做身份校验, 拦截器 这…...

决策树|随机森林 GBDT XGBoost|集成学习
文章目录 1 决策树模型1.1 决策树模型简介1.2 决策树模型核心问题1.2.1 分类划分标准1.2.1.1 信息增益1.2.1.2 增益率1.2.1.3 基尼系数 1.2.2 停止生长策略1.2.3 剪枝策略 1.3 决策树 - python代码1.3.1 结果解读1.3.2 决策树可视化1.3.3 CV - 留一法 2 集成学习2.1 Boosting2.…...
【C语言实现TCP通信】
要在C语言中实现TCP通信,您可以遵循以下步骤: 创建Socket:使用socket()函数创建套接字,指定协议族为AF_INET(IPv4)或AF_INET6(IPv6),类型为SOCK_STREAM表示使用TCP协议。…...
黑马点评-短信登录
Override public Result sendCode(String phone) { // 1.检验手机号 if (RegexUtils.isPhoneInvalid(phone)) { // 这里抛出异常和return fail有什么区别吗?———> 有区别,抛出异常会被全局异常处理器捕获,返回fail不会 throw ne…...

CentOS7 部署单机版 elasticsearch
一、环境准备 1、准备一台系统为CentOS7的服务器 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) 2、创建新用户,用于elasticsearch服务 # elastic不允许使用root账号启动服务 [rootlocalhost ~]# useradd elastic [rootlo…...

Mujoco仿真【xml文件的学习 4】
在学习Mujoco仿真的过程中,mujoco的版本要选择合适。先前我将mujoco的版本升级到了mujoco-3.1.4,在运行act的仿真代码时遇到了问题,撰写了博客: Aloha机械臂的mujoco仿真问题记录-CSDN博客 下面在进行mujoco仿真时,统…...

vue数据持久化仓库
本文章是一篇记录实用性vue数据持久化仓的使用! 首先在src中创建store文件夹,并创建一个根据本页面相关的名称, 在终端导入:npm i pinia 和 npm i pinia-plugin-persistedstate 接下来引入代码: import { defineSt…...

OrangePi AIpro评测 - 基础操作篇
0. 环境 ●OrangePi AIpro ●win10笔记本 ●路由器 准备下win10电脑、路由器,这些板卡通常是在网络正常的环境下才方便测试。 还要准备OrangePi AIpro的官方资料: http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-suppo…...
不含一阶导数项的线性二阶微分方程的通解
假设这里有一个线性二阶微分等式,形式如下: (1) 其中是连续的,是在实闭区间是连续的,如果有人倾向于推广,在相对假弱的假设下,这个结果能够被发现。如果是下列其次线性方程的任意两个线性无关的…...

Redis篇 String
String概念和set,get扩充 一. String类型的基本介绍二. String中set,get方法扩充 一. String类型的基本介绍 redis中所有的key都是字符串类型的,但是value的类型差异很大. redis中的字符串,直接就是二进制方式存储的,可以存储整数,二进制数据 文本数据,Json,xml还有音频等. 二.…...

【vue-2】v-on、v-show、v-if及按键修饰符
目录 1、v-on事件 2、按键修饰符 3、显示和隐藏v-show 4、条件渲染v-if 1、v-on事件 创建button按钮有以下两种方式: <button v-on:click"edit">修改</button> <button click"edit">修改</button> 完整示例代码…...

华为交换机基础实验----VLAN基础
交换机篇实验: 给交换机创建VLAN 1.单个VLAN的创建 [S]vlan 10 查看的方法:dis vlan 2.批量创建vlan的方法 Vlan b 20 30 40 连续创建三个vlan,分别为vlan20 vlan30和vlan40 [SONY-S1-vlan10]vlan b 20 30 40 3.批量创建连续的vlan…...

Vue3学习使用axios和qs进行POST请求和响应处理
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、前言1.准备工作2.发送POST请求3.处理响应数据4.总结 一、前言 在前端开发中,经常需要与后端进行数据交互,其中包括发送POST请求并处理响…...

张大哥笔记:赚钱高手养成计划---如何将一份时间产生N份收入?
我们常说的赚钱的四种境界有哪些? 1.靠体力挣钱 2.靠技能挣钱 3.靠知识挣钱 4.靠平台钱生钱 所以对应的收入的模式就会是下面4种模式: 1.一份时间卖1次 2.一份时间卖N次 3.一份时间溢价卖N次 4.购买他人时间为自己所用 时间对于每个人都是相同的…...

excel里如何将数据分组转置?
这个表格怎样转换为下表?按照国家来分组,把不同年份对应的不同序列值进行转置?? 这演示用数据透视表就完成这个数据转换。 1.创建数据透视表 选中数据中任意单元格,点击插入选项卡,数据透视表,…...
WHAT - 前端安全性测试和常见攻击手段
目录 一、安全性测试二、前端安全性测试三、跨站脚本(XSS)攻击1. 介绍2. 三大类型反射型 XSS(Reflected XSS)存储型 XSS(Stored XSS)DOM 型 XSS(DOM-based XSS) 3. xss 盲打4. xss 水…...

重量and体积,不要在傻傻的花冤枉钱寄快递了!
寄快递时有没有遇到过明明不重却被按体积收费的情况?别急,今天就来给大家揭秘快递收费的奥秘! 实际重量和体积重量! 首先,我们要明白两个概念:实际重量和体积重量。实际重量就是你看到的物品重量…...

docker ps显示的参数具体是什么意思
1,运行一个容器 docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"这段命令的作用是使用 docker run 命令运行一个基于 ubuntu:15.10 镜像的 Docker 容器,并在容器中执行一个无限循环的命令。 具体解…...

【C++】多态:编程中的“一人千面”艺术
目录 一、多态的概念二、多态的定义及实现1.多态的构成条件2.虚函数的重写2.1 什么是虚函数?2.2 虚函数的重写是什么?2.3 虚函数重写的两个例外2.4 C11 override 和 final2.5 重载、覆盖(重写)、隐藏(重定义)的对比 三、抽象类3.1 概念3.2 接口继承和实现…...

【必备工具】gitee上传-保姆级教程
目录 1.gitee是什么 2.gitee怎么注册 编辑 3.gitee怎么提交代码 4.gitee的三板斧 Clone仓库 Q&A 1. Gitee 只有三板斧吗? 2. Git 教了,Gitee 上没有绿点怎么办? 3. 用户名和密码输入错误怎么办? 4. 操作时不小心…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...