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

苍穹外卖项目结构

苍穹外卖项目总结-CSDN博客

【苍穹外卖|项目】万字总结-CSDN博客

苍穹外卖项目总结_苍芎外卖如何实现的跨域-CSDN博客

【苍穹外卖 | 项目日记】第九天 万字总结-CSDN博客

【苍穹外卖|项目】万字总结-CSDN博客

苍穹外卖面试题准备(持续更新-CSDN博客

业务模块

一、技术点:

讲讲什么是 Httpclient(微信登录模块)
Httpclient是一个服务器端进行 HTTP 通信的库,他使得后端可以发送各种 HTTP 请求和接收 HTTP 响应,使用 HTTPClient,可以轻松的发送 GET, POST, PUT, DELETE 等各种类型的的请求。
在我们的项目中,在进行微信登录开发时,后端在使用登录凭证校验接口的时候就需要发送指定请求到给定的 URL 中。因此我们使用 Httpclient 去完成该任务。

Nginx代理

负载均衡:通过调度算法将客户端的请求分配到不同的服务器上

正向代理:通过代理服务器代理浏览器/客户端去重定向请求访问到目标服务器

反向代理(跨域):代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的客户端

反向代理隐藏服务器正向代理隐藏客户端

特性正向代理(Forward Proxy)反向代理(Reverse Proxy)
代理对象代理客户端(用户)访问外部服务器代理服务器(网站)接受客户端请求
适用场景访问被限制的网站、加速网络请求、隐藏用户 IP负载均衡、缓存静态资源、安全防护
请求流向客户端 → 正向代理 → 目标服务器客户端 → 反向代理 → 真实服务器
是否对外暴露用户需要配置代理才能使用用户无需感知,透明访问
示例VPN、Shadowsocks、SquidNginx、HAProxy、Cloudflare

MD5密码加密

特点:固定长度,不可逆

验证用户密码是否正确的时候,思路是:把用户输入的密码与正确密码加密后得到的MD5字符串进行比较,如果相同则说明得到的是正确的密码。

Swagger – 框架

3.1作用:接口文档的在线生成—doc.html

3.2集成:1.导入Knife4j依赖 2.加入配置,设置资源映射 3.使用注解在对应位置

3.3注解:

@Api(tags = “”)—用在类上

@ApiModel—类上

@ApiModelProperty–用在属性上

@ApiOperation(方法上)

JWT

4.1 概述:将原始JSON进行安全封装,由Header(令牌类型、签名算法等),载荷(携带信息),签名(防伪)组成

4.2 应用场景 :身份验证、授权、信息交换

过滤器Filter 

应用场景:防止未登录就进入界面 验证用户身份 缓存控制

拦截器Interceptor – IOC容器

6.1 概念:由Spring框架提供,动态拦截请求,本质是面向切面编程(AOP)的

6.2 应用场景:登录验证,权限验证,日志记录,性能监控……

AOP 切面

在我们项目前期,会发现一个问题:我们的项目开发会涉及大量的数据表,而这些数据表中有一些重叠的字段,例如创建人,创建时间,修改人,修改时间。对这一部分字段的填充代码相同。这些填充部分的代码分布在整个项目的四处,不涉及核心功能,却影响了多个模块

新增员工或者新增菜品分类时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工或者编辑菜品分类时需要设置修改时间、修改人等字段。这些字段属于公共字段,也就是也就是在我们的系统中很多表中都会有这些字段

聚焦到项目:我们的整体思路为:通过注解的方式标记方法,利用AOP思想创建一个切面,在切面中实现对标记方式中字段的填充,然后再运行原方法。这样就实现了在不改动原方法的前提下,实现了对代码的优化升级。

微信小程序

Spring Cache

概念:基于注解的缓存功能,简化开发

应用场景:缓存数据,防止重复请求,实现分布式系统中的数据共享……

Spring Task – 任务调度工具

应用场景:定时推送,系统未支付订单

WebSocket

概念:基于TCP连接的全双工通信网络协议

应用场景:用于实时通信,实时双向传输数据 – 弹幕、网页聊天…

阿里云OSS

我们通过这项技术来存储菜品,套餐,员工的图片。之所以不存到本地,这是因为前端无法回调服务器的本地图片,这也就造成我们只能存图片,无法回显图片的BUG,而我们如果调用阿里云的云存储服务,照片存储到阿里巴巴的云之后,会返送一个URL,我们通过这段URL就可以回调图片

而在云存储服务中,还有两个小知识点:

1.使用UUID生成文件名

我们需要使用UUID来为上传到阿里云OSS的图片命名。而阿里云图片命名不允许重复,否则就会覆盖。因此我们使用UUID来生成一串随机数,这样就确保了文件不被新文件覆盖。

2.不要把配置类写死

在阿里云配置中我们可以最直观的看到,我们并不是直接把配置写到application.yml 而是在application-dev.yml 写具体配置,在application.yml中应用application-dev.yml的配置。这是因为一个大型项目落地的时候,需要经过很多的环境:开发环节-测试环境-生产环境。而这三个环境可能并不会通用一套数据库,oss等配置类,如果我们直接把配置写到application.yml中,那在切换环境的时候,就要在代码中逐个修改,这对于大型项目的体量而言,无疑是灾难性的。而我们在开发环境采用application-dev.yml 的配置,在测试环境采用application-tex.yml的配置,以此类比。这样是一种很好的开发习惯。我们在自己的练手项目中也应该这样写。

验证码

利用Hutool工具生成验证码,并保存验证码至数据库,方便验证

基于消息转换器对时间进行格式化:

消息转换器在Spring MVC中负责处理请求和响应的数据格式转换,例如将Java对象转换为JSON格式或者把JSON格式转换为Java。

对方法开启事务:

随着业务操作的增加,对任何一张表的修改可能对会影响到其他的表,例如我们在插入dish中的菜品的时候,也应该一并插入dish_flavor中该菜品的对应的口味。

而这两张表的数据的插入,应该是确保都完成的,不可以出现只插入了菜品或者只插入了口味的情况。因此我们要把对这两张表的操作设置为一个事务。

1.在启动类上方添加@EnableTransactionManagement

2.开启事务注解之后,我们只需要在需要捆绑成为一个事务的方法上添加@Transactional 

传播行为可以用于定义事务在多个方法间的传递方式,以确保事务的一致性和完整性。

传播行为(Propagation)指的是事务在不同方法间进行传播的行为。常见的传播行为包括:

  • REQUIRED:如果当前存在事务,则加入到当前事务中,如果没有事务,则创建一个新的事务。
  • REQUIRES_NEW:无论当前是否存在事务,都创建一个新的事务,并挂起当前事务。
  • SUPPORTS:如果当前存在事务,则加入到当前事务中,如果没有事务,则以非事务的方式执行。
  • NOT_SUPPORTED:以非事务的方式执行,如果当前存在事务,则挂起该事务。
  • NEVER:以非事务的方式执行,如果当前存在事务,则抛出异常。
  • MANDATORY:要求当前必须存在事务,否则抛出异常。
  1. REQUIRED:假设有两个方法A和B,A方法被标记为 @Transactional,而B方法没有。当在A方法中调用B方法时,如果当前已存在一个事务,B方法会加入到该事务中,如果没有事务,则会创建一个新的事务并将B方法放在这个新的事务中。
  2. REQUIRES_NEW:同样假设有两个方法A和B,A方法和B方法都被标记为 @Transactional。当在A方法中调用B方法时,无论A方法所处的上下文是否已经存在一个事务,B方法都会创建一个新的事务,并将A方法原有的事务挂起,B方法在创建的新事务中独立执行。
  3. SUPPORTS:,假设有两个方法A和B,其中A方法被标记为 @Transactional,而B方法没有。当在A方法中调用B方法时,如果当前已存在一个事务,B方法会加入到该事务中,以保证事务的一致性;如果没有事务,则B方法以非事务的方式执行,即没有事务的保护和控制。
  4. NOT_SUPPORTED:假设有两个方法A和B,其中A方法被标记为 @Transactional,而B方法没有。当在A方法中调用B方法时,无论当前是否存在事务,B方法都会以非事务的方式执行,即使A方法原本处于一个事务中,也会被挂起。
  5. NEVER:假设有一个方法A被标记为 @Transactional,而在A方法内部调用了一个被标记为 @Transactional(propagation = Propagation.NEVER) 的方法B。在这种情况下,如果在调用B方法时存在一个事务,那么会抛出异常,因为B方法不允许在事务

 redis

查询店铺营业状态 :像这种店铺营业状态,本项目无非就两个状态:营业中/打样。而且它属于高频查询。只要用户浏览到这个店铺,前端就要自动发送请求到后端查询店铺状态。

缓存请求相应内容:如果小程序又发送相同请求,那么我们就从缓存中直接返回相应内容。这样就减少了直接对后端的数据库的查询。(缓存菜品)

微信小程序登录--前端

后端

1.用户登录验证(网页)

注意:为了防止数据库泄露带来的用户账号密码安全性问题,我们即使是在数据库中也不会进行明文存储密码,而是存储MD5加密方法加密后的一串字符串。防止用户密码以明文的形式进行传递

1.用户登录验证(微信)

1.我们的小程序会调用wx.login()来获得一个code。该 code 的作用是用于后续的用户身份验证和获取用户信息。

2.小程序的wx.request会把code发送给后端,后端再打包自己的小程序ID(appid)小程序密钥(appsecert) 最后加上小程序发送给自己的code,利用Httpclient从后端发送给微信接口服务。而微信接口服务会在校验之后返回session_keyopenid

微信接口服务返回的session_key和openid具有以下用途:

  1. 用户身份识别:通过openid,可以唯一标识用户的身份。开发者可以将openid与用户在自己的系统中的账号进行关联,实现用户的登录、注册等功能。
  2. 数据加密解密:session_key是用于对用户敏感数据进行加密和解密的密钥。开发者可以使用session_key对用户的敏感数据进行加密,确保数据在传输过程中的安全性;同时,也可以使用session_key对加密后的数据进行解密,获取原始数据。
  3. 用户信息获取:通过openid和session_key,开发者可以向微信接口服务发送请求,获取用户的详细信息,如用户昵称、头像等。这些信息可以用于个性化展示、社交分享等功能

3.在后端获取到微信接口服务发送给自己的session_key和openid,自定义用户登录态,并且发送给小程序

4.小程序把后端发送过来的自定义登录态存入到storge中。

wx.request()发送业务请求,携带自定义登录态(用户名和密码),方便后端识别当前用户身份。后端根据前端发送过来的自定义登录态来查询openid和session_key。此时后端就可以识别当前用户身份。返回当前用户的个性化业务数据。

2.TreadLocal线程存储

配合Spring拦截器进行token验证

ThreadLocal 允许在同一线程中存储数据,我们可以把从JWT中解析到的用户数据存入到线程当中,在业务层代码里需要获得用户数据的时候,即可从线程变量中获取。避免频繁解析 token,提高性能,并保证数据的线程安全性。

登录过后,每次访问携带token(一般有效期是1天),判断是否用户还在登录状态,可以继续访问。

解决HTTP无状态

  • 采用 JWT(JSON Web Token) 机制,将用户信息编码后存储在 token 中,并通过请求头(Authorization)传递给服务器。
  • 服务器在每次请求时 解析 token,提取用户信息,并将其与当前线程绑定,确保在整个请求链路中可用。

3.用户查看菜品

4.添加菜品购物车

购物车的菜品先存放在redis里面,后期更新到mysql,这样避免用户频繁的更新购物车的数据

5.用户支付

6.下单流程

注意:订单号也可以用雪花算法来实现,主要用来支持分布式系统应用的(美团、饿了么)

方案适用场景并发性能可靠性额外依赖
Redis 自增高并发外卖系统Redis
雪花算法(Snowflake)分布式系统机器 ID

Apache POI技术实现导出文件:


Apache POI(Poor Obfuscation Implementation)是一个用于处理Microsoft Office格式文档的开源Java库。POI提供了一组可以读取、写入和操作各种Office文件的API,包括Word文档(.doc和.docx)、Excel电子表格(.xls和.xlsx)以及PowerPoint演示文稿(.ppt和.pptx)。

通过POI,开发者可以在Java应用程序中读取和编辑Office文档,实现对文档内容、样式、格式和元数据的操作。它提供了向现有文档添加新内容、修改现有内容、删除内容以及进行格式设置和样式调整等功能。

而在本项目中,我们并不使用ApachePOI建表,这样无疑是在拷打自己。我们的想法是直接就提供一张创建好的模板表,这样我们只需要使用ApachePOI来实现填充数据就好了。

项目总结

(1)第一个感觉是思维的解放。

在这么多业务的练习中,我的对于业务的抽象能力大大提升,简单的讲:我认为代码的编写,实际上就是对业务需求的不断解构,拆分,细化。

例如:实现购物车接口。最初我还在想:如何让用户端购物车可以自动显示添加的菜品这些内容。因为没有办法把该业务抽象拆分为具体的代码思路而感到厌烦。之后就明白了,其实就是建立一张表,买了啥都记到表里面,所谓的添加商品可以实时看到,只不过是加了一个数据库查询之后回显给前端而已

这就是我想要说的,再复杂的业务也可以不断的进行抽离,拆分,最终变为一个个简单的逻辑代码,而谁的抽象拆分能力越强,谁就越可能成为一位合格的程序员。

(2)第二个感觉是思维的提升。

回头望去,原来自己学习到了这么多的知识点,并且也没有自己最开始认为的那么难。回顾整个项目,我认为作为初写项目的学生来讲,我面临的最大的问题是:缺乏宏观思想。我在写业务代码的时候,通常只能局限于仅仅实现当前业务,并没有思考代码复用性,业务通用性,逻辑顺畅性这些问题。导致写了很多的功能相同的代码。四个字总结:站位不高。

而这也是我尝试写项目总结的原因,项目总结让我脱离具体的业务板块,不再把思维聚焦在某一个功能的实现上,而是尝试聚焦整个业务整体。在我的眼里,实现项目是从小到大,我用一个一个业务去组成了这个大的项目。而写项目日记是从大到小,当我从一整个项目整体开始拆分业务的时候,我是切身实地的觉得我的站位变高了,因为我在真真切切的思考不同业务代码之间的逻辑关系。由于实现过整个业务,我可以让思维在不同的业务之间穿梭,不断的解构这些业务。尝试探寻更好的业务解决方案。

我认为:如果我可以在业务逻辑代码搭建阶段就有这种宏观思考的能力,那么整个项目的业务逻辑实现就会变的轻松很多。

项目提问

redis做为缓存,mysql的数据如何与redis进行同步呢?(双写一致性)

  • 介绍自己简历上的业务,我们当时是把文章的热点数据存入到了缓存中,虽然是热点数据,但是实时要求性并没有那么高,所以,我们当时采用的是异步的方案同步的数据
  • 我们当时是把抢券的库存存入到了缓存中,这个需要实时的进行数据同步,为了保证数据的强一致,我们当时采用的是redisson提供的读写锁来保证数据的同步

那你来介绍一下异步的方案(你来介绍一下redisson读写锁的这种方案)
允许延时一致的业务,采用异步通知

  • 延时双删策略:使用MQ中间中间件,更新数据之后,通知缓存删除
  • 订阅binlog日志:利用canal中间件,伪装为mysq1的一个从节点,通过读取binlog数据更新缓存

强一致性的,采用Redisson提供的读写锁

  • 共享锁:读锁readLock,加锁之后,其他线程可以共享读操作
  • 排他锁:独占锁writeLock也叫,加锁之后,阻塞其他线程读写操作
     

Redisson

分布式读写锁:分别对缓存和数据库中的分布式加锁,持有写锁线程先更新数据库,再更新缓存,最后释放锁。

锁续期机制:​Redisson 内部提供了“看门狗”机制,默认锁的有效期为 30 秒。如果在锁持有期间,持锁的 Redisson 实例未关闭,锁的有效期会自动延长,避免因 Redis 节点宕机导致的锁死问题。

可能出现问题

分布式解决问题

❌ 问题 1:超卖
多个用户同时下单,库存 = 1,但两个人都成功购买,导致库存变负数

❌ 问题 2:订单重复提交==重复消费
同一个用户点击多次提交订单,导致创建多个相同订单。

消息幂等性避免消息重复消费导致数据库数据错误

  • 使用 唯一 ID(如订单号)来标记已消费的消息,防止重复写入数据库。

  • 可以在 数据库增加唯一约束,避免重复插入相同数据。

❌ 问题 3:并发写入数据覆盖
多个用户同时修改购物车,可能导致数据丢失或错误

解决方案

  • 悲观锁:确保同一时间只有一个用户对库存进行操作,避免多线程冲突
  • 乐观锁:根据商品的版本号进行更新库存操作,只有开始的版本号与提交的版本号一致才行
  • 使用 Redis 分布式锁 确保扣库存操作是原子的,防止并发问题

Redis分布式锁?

如何实现

在redis中提供了一个命令setnx(SET if not exists),由于redis的单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候其他客户端是不能设置这个key的

那你如何控制Redis实现分布式锁有效时长呢?

redis的setnx指令不好控制这个问题,我们当时采用的redis的一个框架redisson实现的。
在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时问就检查当前业务是否还持有锁,如果持有就增加加锁的持有时问,当业务执行完成之后需要使用释放锁就可以了
还有一个好处就是,在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会白选不断尝试获取锁,如果客户1释放之后,客户2就可以马上持有锁,性能也得到了提升。==自旋锁

redisson实现的分布式锁是叫重入的吗?

嗯,是可以重入的。这样做是为了避免死锁的产生:这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如术释放锁就会在计算上减。在储数据的时似采用的hash结构,大kcy可以按照自己的业务进行定制,其中小kcy是当前线程的唯一标识,value是当前线程重入的次数

redisson实现的分布式锁能解决主从一致性的问题吗

这个是不能的,比如,当线程 1 加锁成功后,主节点数据会异步复制到从节点,此时当前持有 Redis 锁的主节点宕机,从节点被提升为新的主节点,假如锁的同步尚未完成,新主节点上没有该锁的记录,此时其他线程可能会重新获取锁并修改数据,导致多个线程对同一资源进行并发修改,从而造成数据不一致的问题

redis分布式锁的粒度--按业务粒度分类

锁类型锁定范围适用场景优缺点
全局锁整个系统或应用重大业务操作,如定时任务、数据库表迁移适用于保证全局数据一致性,但会降低系统并发性能
业务级锁业务操作(如订单、支付、库存)如支付事务、订单状态变更控制特定业务的并发,避免多个线程同时修改关键业务数据
资源级锁具体的资源(商品 ID、用户 ID)如商品库存扣减、用户账户操作降低锁冲突,提高并发性能

RabbitMQ

高并发场景下,写操作通过消息队列异步执行,减少数据库瞬时压力。

基于AMPQ协议,主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。

消息可靠性:RabbitMQ-如何保证消息不丢失

   当时MYSQL和Redis的数据双写一致性就是采用RabbitMQ实现同步的,这里面就要求了消息的高可用性,我们要保证消息的不丢失。主要从三个层面考虑

  • 第一个是开启生产者确认机制,确保生产者的消息能到达队列,如果报错可以先记录到日志中,再去修复数据
  • 第二个是开启持久化功能,确保消息未消费前在队列中不会丢失,其中的交换机、队列、和消息都要做持久化
  • 第三个是开启消费者确认机制为auto,由spring确认消息处理成功后完成ack,当然也需要设置一定的重试次数,我们当时设置了3次,如果重试3次还没有收到消息,就将失败后的消息投递到异常交换机,交由人工处理

消息幂等性:避免消息重复消费导致数据库数据错误

  • 使用 唯一 ID(如订单号)来标记已消费的消息,防止重复写入数据库。

  • 可以在 数据库增加唯一约束,避免重复插入相同数据。

应用场景

(1) 订单系统:秒杀 / 外卖 / 电商高并发下的数据库优化

秒杀、外卖、双 11 活动等场景下,大量用户并发下单,数据库瞬间写入压力过大,可能导致死锁、事务冲突、数据库崩溃

(2)订单状态更新

外卖平台的订单状态(已下单、已支付、已完成)需要频繁更新,直接写数据库会导致大量 update 操作,影响数据库性能。


为什么不用kafka

1订单支付、状态更新必须严格按顺序执行。

  • RabbitMQ 支持事务和 ACK 确认机制
  • Kafka 没有内置事务,无法保证严格的订单消息顺序!

2.适合短生命周期的消息队列

  • RabbitMQ 是短生命周期消息的最佳选择:消息被消费后立即删除,毫秒级延迟
  • Kafka 适用于日志、监控等长生命周期的数据流,不适合短时事务!

3.适合处理订单超时

  • RabbitMQ 支持 TTL(Time-To-Live)+ 死信队列(DLX):订单消息进入 延迟队列;超时后,消息被路由到 死信队列;消费者监听死信队列,执行订单取消操作
  • Kafka 没有 TTL 机制,只能依赖应用程序手动轮询,性能较差。

kafaka

Kafka 更适合大数据分析,不适合事务处理

Kafka 适用于

  • 日志存储(存储时间长,用户行为分析)

  • 实时数据流(如用户个性化推荐、ETL 数据处理)

  • 分布式事件驱动架构(微服务解耦)

Threadlocal

为什么使用ThreadLocal?

如果每个请求都手动解析 token 并传递用户信息,可能会导致:

  • 代码侵入性高,需要在每个方法中传递 token 解析结果,影响代码结构。
  • 并发问题,如果多个线程共享同一个 token 解析结果,可能导致数据错乱。

ThreadLocal 方式的优势

  • 避免参数层层传递:解析 token 后,将用户信息存入 ThreadLocal,整个请求链中的 拦截器、服务层、控制器 都能直接获取用户信息。
  • 线程安全ThreadLocal 变量 仅对当前线程有效,不会被其他线程访问,避免并发数据混乱问题。

如何使用ThreadLocal来存储和管理用户认证信息

在项目中,如何使用ThreadLocal来存储和管理用户的认证信息?
(1)初始化:在拦截器的 preHandle方法中,从请求中提取JWTToken,并进行校验。一旦校验通过,就把用户的信息从Token中解析出来,并存储到ThreadLocal中。

(2)使用:由于 ThreadLocal 变量在整个线程内有效,控制层或服务层可以直接获取用户信息,而不需要显式传递 userId 参数。这样做的好处是,它消除了通过方法参数传递信息的要,使得方法签名更简洁、逻辑更清晰。

(3)清理:在请求的生命周期即将结束时,例如在返回响应之前,需要显式清除ThreadLocal中的数据。在拦截器的afterCompletion方法中完成清理,确保每个请求结束后清理掉所有关联的数据,防止内存泄漏。

内存泄漏

如果在业务处理完成后未及时清理ThreadLocal存储的数据,这些数据会一直存在于对应线程的ThreadLocalMap中,导致垃圾回收器无法释放这些对象,从而引发内存泄漏。

因为ThreadLocalMap是由一个个Entry构成的数组,并且每个Entry的key是弱引用,这就意味着当触发GC时,Entry的key也就是ThreadLocal就会被回收。如果此时value外部也没有强引用指向的话,那么这个value就永远无法访问了,按道理也该被回收,但是由于entry还在强引用value。那么此时value 就无法被回收。

  • 每次使用完毕之后记得调用一下remove()方法清除数据
  • ThreadLocal变量尽量定义成static final类型,避免频繁创建ThreadLocal实例。

数据污染

由于线程池中线程会被复用,未清除的ThreadLocal数据可能在下次任务中被错误地使用,进而引发数据污染问题。

在合适的时机手动调用ThreadLocal.remove()方法来清除ThreadLocal中的数据。这样可以确保每个请求或任务开始时ThreadLocal中不会包含上一次请求或任务的数据,从而保证了数据的独立性和正确性。

JWT

jwt服务端如何保证用户是对应用户?

用户登录成功后,服务器生成一个JWT并返回给客户端,客户端在后续请求中将JWT放在请求头中,服务器通过验证 JWT来识别用户。浏览器发起请求,请求登录接口时,如果登录成功,生成一个令牌token,即用户的合法身份凭证。接下来响应数据时,直接将令牌响应给前端。前端接收到令牌之后,可以将令牌存储在cookie。后续的每一次请求都需要将令牌携带到服务端(请求头header中携带,名称为token,值为登录时下发的JWT令牌)。服务端统一拦截请求,判断是否有令牌,如果无,直接拒绝访问,如果有,校验令牌是否有效。在同一次会话的多次请求之间想共享数据,可以将共享数据存储在令牌当中。

为什么用到统一拦截?

程序中所开发的增删改查都需要使用以上套路进行登录校验。此时就会出现:相同代码逻辑,每个功能都需要编写,就会造成代码非常繁琐。为了简化这块操作,使用统一拦截技术。拦截浏览器发送过来的所有的请求,拦截到这个请求之后,通过请求来获取之前所存入的登录标记在获取到登录标记且标记为登录成功,说明员工已经登录。如果已经登录,直接放行(访问正常的业务接口)。


http://www.mrgr.cn/news/96655.html

相关文章:

  • 网络架构搭建中的 QinQ 与端口安全策略
  • DAY 32 leetcode 242--哈希表.有效的字母异位词
  • Oracle数据库数据编程SQL<3.5 PL/SQL 存储过程(Procedure)>
  • 魔改chromium——基础环境搭建
  • Open GL ES ->GLSurfaceView在正交投影下的图片旋转、缩放、位移
  • OpenCV图像输入输出模块imgcodecs
  • 什么是 CSSD?
  • OCCT(2)Windows平台编译OCCT
  • OpenCV图像输入输出模块imgcodecs(imwrite函数的用法)
  • Oracle数据库数据编程SQL<3.4 PL/SQL 自定义函数(Function)>
  • 初始ARM
  • 同步SVPWM调制策略的初步学习记录
  • 3-栈、队列、数组
  • 《大模型部署》——ollama下载及deepseek本地部署(详细快速部署)
  • 【VM虚拟机ip问题】
  • 类的默认成员函数
  • Vue React
  • Qt基础:信号槽
  • PHP 开发API接口签名验证
  • npm webpack打包缓存 导致css引用地址未更新