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

如何在go项目中实现发送邮箱验证码、邮箱+验证码登录

前期准备

  • GoLand :2024.1.1
    下载官网:https://www.jetbrains.com/zh-cn/go/download/other.html
    在这里插入图片描述
  • Postman:
    下载官网:https://www.postman.com/downloads/
    在这里插入图片描述

效果图(使用Postman)

  • Google
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • QQ
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

And so on…
Just you can try!


项目结构

本项目基于nunu基础上实现(github地址:https://github.com/go-nunu/nunu),Nunu是一个基于Golang的应用脚手架,它的名字来自于英雄联盟中的游戏角色,一个骑在雪怪肩膀上的小男孩。和努努一样,该项目也是站在巨人的肩膀上,它是由Golang生态中各种非常流行的库整合而成的,它们的组合可以帮助你快速构建一个高效、可靠的应用程序。拥有以下功能:
在这里插入图片描述

请添加图片描述

从nunu官方按照规范安装好之后:
在这里插入图片描述

基本操作流程

  1. 用户提交邮箱(email) 以请求 验证码(code)
  2. 服务器生成验证码并发送到用户邮箱。
  3. 用户输入收到的验证码和邮箱进行登录(login)
  4. 服务器验证验证码和邮箱。
  5. 如果验证成功,用户登录成功(sucess);否则,返回错误信息(error)

代码实现

1.internal/model/user.go和config/local.yml

注意:config和internal在同一级目录下

在这里插入图片描述

咱们先定义一个表结构,然后去连接数据库,创建对应映射的表,存储咱们的useridemail,验证码(code)是临时的,保存在cache里就好,不需要落库。

package modelimport ("time""gorm.io/gorm"
)type User struct {Id        string `gorm:"primarykey"`Email     string `gorm:"not null"`CreatedAt time.TimeUpdatedAt time.TimeDeletedAt gorm.DeletedAt `gorm:"index"`
}func (u *User) TableName() string {return "users"
}

建议直接从右边状态栏里直接连接mysql数据库:
在这里插入图片描述

对应的SQL建表语句:

create table users
(id         varchar(255) not nullprimary key,email      varchar(255) not null,created_at timestamp    not null,updated_at timestamp    not null,deleted_at timestamp    null,constraint emailunique (email),constraint idunique (id)
);

另外还需要在config包下修改local.yml数据库连接配置信息:

在这里插入图片描述在这里插入图片描述
库名为刚才所添加表的所在库名哦!

2.api/v1/user.go

package v1type LoginResponseData struct {AccessToken string `json:"accessToken"`
}type SendVerificationCodeRequest struct {Email string `json:"email"`
}type LoginByVerificationCodeRequest struct {Email string `json:"email"`Code  string `json:"code"`
}

这段Go代码定义了三个结构体:

  1. LoginResponseData:表示登录成功后的响应数据,包含一个AccessToken字段,用于标识用户的访问令牌。
  2. SendVerificationCodeRequest:表示发送验证代码请求的数据结构,包含一个Email字段,用于指定要发送验证代码的邮箱地址。
  3. LoginByVerificationCodeRequest:表示通过验证代码登录的请求数据结构,包含一个Email字段和一个Code字段,分别用于指定邮箱地址和收到的验证代码。

3.internal/repository/user.go

  • GetByEmail函数通过邮箱地址从数据库中获取用户信息。
  1. 参数:ctx context.Context表示上下文信息,email string表示要查询的邮箱地址。
  2. 返回值:*model.User表示查询到的用户信息,error表示错误信息。
  3. 该函数首先根据邮箱地址查询数据库中是否存在该用户,如果查询成功,则返回用户信息;如果查询失败,则返回错误信息。
  • CreateUserByEmail函数通过邮箱地址创建一个新的用户。
  1. 参数:ctx context.Context表示上下文信息,email string表示要创建的用户的邮箱地址。
  2. 返回值:*model.User表示创建的用户信息,error表示错误信息。
  3. 该函数首先生成一个唯一的用户ID,然后使用邮箱地址创建一个新的用户实例,并设置创建时间和更新时间为当前时间。
  4. 接着,将新用户实例插入到数据库中,如果插入成功,则返回新创建的用户信息;如果插入失败,则返回错误信息。
package repositoryimport ("context""errors""fmt""time""emerge-ai-core/common/utils""emerge-ai-core/internal/model""gorm.io/gorm"
)type UserRepository interface {GetByEmail(ctx context.Context, email string) (*model.User, error)CreateUserByEmail(ctx context.Context, email string) (*model.User, error)
}func NewUserRepository(r *Repository,
) UserRepository {return &userRepository{Repository: r,}
}type userRepository struct {*Repository
}func (r *userRepository) GetByEmail(ctx context.Context, email string) (*model.User, error) {var user model.Userif err := r.DB(ctx).Where("email = ?", email).First(&user).Error; err != nil {return nil, err}return &user, nil
}// CreateUserByEmail creates a user by email
func (r *userRepository) CreateUserByEmail(ctx context.Context, email string) (*model.User, error) {now := time.Now()user := &model.User{Id:        utils.GenerateUUID(),Email:     email,CreatedAt: now,UpdatedAt: now,}if err := r.DB(ctx).Create(user).Error; err != nil {return nil, fmt.Errorf("failed to create user by email: %v", err)}return user, nil
}

4.internal/service/email.go和internal/service/user.go

在这里插入图片描述

user.go

  • 定义了一个名为UserService的接口,其中包含一个GenerateTokenByUserEmail方法,用于生成用户的令牌。实现该接口的是userService结构体,它通过NewUserService函数进行实例化。GenerateTokenByUserEmail方法首先通过userRepo获取用户信息,如果用户不存在,则创建新用户,并使用jwt.GenToken方法生成令牌。
package serviceimport ("context""errors""time"v1 "emerge-ai-core/api/v1""emerge-ai-core/internal/model""emerge-ai-core/internal/repository""github.com/patrickmn/go-cache""golang.org/x/crypto/bcrypt""gorm.io/gorm"
)type UserService interface {GenerateTokenByUserEmail(ctx context.Context, email string) (string, error)
}func NewUserService(service *Service,userRepo repository.UserRepository,
) UserService {return &userService{userRepo: userRepo,Service:  service,}
}type userService struct {userRepo     repository.UserRepositoryemailService EmailService*Service
}// GenerateTokenByUserEmail generates a token for a user
func (s *userService) GenerateTokenByUserEmail(ctx context.Context, email string) (string, error) {// get user by emailuser, err := s.userRepo.GetByEmail(ctx, email)if err != nil {if errors.Is(err, gorm.ErrRecordNotFound) {// is new user create useruser, err = s.userRepo.CreateUserByEmail(ctx, email)if err != nil {return "", err}} else {return "", err}}// generate tokentoken, err := s.jwt.GenToken(user.Id, time.Now().Add(time.Hour*24*1))if err != nil {return "", err}return token, nil
}

email.go

  • 提供了一个电子邮件服务,用于发送和验证用户邮箱中的验证代码。
package serviceimport ("context""fmt""math/rand""net/smtp""time""github.com/jordan-wright/email""github.com/patrickmn/go-cache"
)var (// cache for storing verification codes// 缓存中的验证代码将在创建后5分钟内有效,且每隔10分钟进行一次清理。verificationCodeCache = cache.New(5*time.Minute, 10*time.Minute)
)type EmailService interface {SendVerificationCode(ctx context.Context, to string) errorVerifyVerificationCode(email string, code string) bool
}type emailService struct {
}func NewEmailService() EmailService {return &emailService{}
}// SendVerificationCode sends a verification code to the user's email
func (e *emailService) SendVerificationCode(ctx context.Context, to string) error {code := generateVerificationCode()err := e.sendVerificationCode(to, code)if err != nil {return err}// store the verification code in the cache for later verificationverificationCodeCache.Set(to, code, cache.DefaultExpiration)return nil
}// sendVerificationCode 发送验证代码到指定的邮箱。
// 参数 to: 邮件接收人的邮箱地址。
// 参数 code: 需要发送的验证代码。
// 返回值 error: 发送过程中遇到的任何错误。
func (e *emailService) sendVerificationCode(to string, code string) error {// 创建一个新的邮件实例em := email.NewEmail()em.From = "Xxxxxxx <xxxxxxxxxx@qq.com>"em.To = []string{to}em.Subject = "Verification Code"// 设置邮件的HTML内容em.HTML = []byte(`<h1>Verification Code</h1><p>Your verification code is: <strong>` + code + `</strong></p>`)// 发送邮件(这里使用QQ进行发送邮件验证码)err := em.Send("smtp.qq.com:587", smtp.PlainAuth("", "xxxxxxxxxx@qq.com", "这里填写的是授权码", "smtp.qq.com"))if err != nil {return err // 如果发送过程中有错误,返回错误信息}return nil // 邮件发送成功,返回nil
}// 随机生成一个6位数的验证码。
func generateVerificationCode() string {rand.Seed(time.Now().UnixNano())code := fmt.Sprintf("%06d", rand.Intn(1000000))return code
}// VerifyVerificationCode verifies the verification code sent to the user
func (e *emailService) VerifyVerificationCode(email string, code string) bool {// debug codeif code == "123456" {return true}// retrieve the verification code from the cachecachedCode, found := verificationCodeCache.Get(email)// 如果没有找到验证码或者验证码过期,返回falseif !found {return false}// compare the cached code with the provided codeif cachedCode != code {return false}return true
}

注意:这里需要SMTP协议知识,并且要想获取到授权码,一般要去所在邮箱官方进行申请,这里以QQ为例:

  1. 电脑端打开QQ邮箱,点击设置
    在这里插入图片描述

  2. 点击账号在这里插入图片描述

  3. 往下滑,找到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务,我这里已经开启了服务。
    在这里插入图片描述
    在这里插入图片描述

  4. 即可获取到授权码!

5.internal/handler/user.go

  • 处理用户通过验证代码登录的HTTP请求
package handlerimport ("net/http""emerge-ai-core/api/v1""emerge-ai-core/internal/model""emerge-ai-core/internal/service""github.com/gin-gonic/gin""go.uber.org/zap"
)type UserHandler struct {*HandleruserService  service.UserServiceemailService service.EmailService
}func NewUserHandler(handler *Handler, userService service.UserService, emailService service.EmailService) *UserHandler {return &UserHandler{Handler:      handler,userService:  userService,emailService: emailService,}
}// SendVerificationCode send verification code
func (h *UserHandler) SendVerificationCode(ctx *gin.Context) {var req v1.SendVerificationCodeRequestif err := ctx.ShouldBindJSON(&req); err != nil {v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, err.Error())return}if err := h.emailService.SendVerificationCode(ctx, req.Email); err != nil {v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, err.Error())return}v1.HandleSuccess(ctx, nil)
}// LoginByVerificationCode by verification code
func (h *UserHandler) LoginByVerificationCode(ctx *gin.Context) {var req v1.LoginByVerificationCodeRequestif err := ctx.ShouldBindJSON(&req); err != nil {v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, err.Error())return}// check verification codeif !h.emailService.VerifyVerificationCode(req.Email, req.Code) {v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)return}token, err := h.userService.GenerateTokenByUserEmail(ctx, req.Email)if err != nil {v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, err.Error())return}v1.HandleSuccess(ctx, v1.LoginResponseData{AccessToken: token,})
}

6.internal/server/http.go

  • 创建一个以/v1为前缀的路由分组v1,然后在该分组下创建子分组/public。在/public子分组下定义了两个POST请求的路由,分别对应/send-verification-code/login,并绑定相应的处理函数。
package serverimport (apiV1 "emerge-ai-core/api/v1""emerge-ai-core/docs""emerge-ai-core/internal/handler""emerge-ai-core/internal/middleware""emerge-ai-core/pkg/jwt""emerge-ai-core/pkg/log""emerge-ai-core/pkg/server/http""github.com/gin-gonic/gin""github.com/spf13/viper"swaggerfiles "github.com/swaggo/files"ginSwagger "github.com/swaggo/gin-swagger"
)func NewHTTPServer(logger *log.Logger,conf *viper.Viper,jwt *jwt.JWT,userHandler *handler.UserHandler,chatHandler *handler.ChatHandler,
) *http.Server {gin.SetMode(gin.DebugMode)s := http.NewServer(gin.Default(),logger,http.WithServerHost(conf.GetString("http.host")),http.WithServerPort(conf.GetInt("http.port")),)...v1 := s.Group("/v1"){publicRouter := v1.Group("/public"){// POST /v1/public/send-verification-codepublicRouter.POST("/send-verification-code", userHandler.SendVerificationCode)// POST /v1/public/loginpublicRouter.POST("/login", userHandler.LoginByVerificationCode)}}return s
}

Postman测试

同效果图

  • Google
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • QQ
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

And so on…
Just you can try!

相关文章:

如何在go项目中实现发送邮箱验证码、邮箱+验证码登录

前期准备 GoLand &#xff1a;2024.1.1 下载官网&#xff1a;https://www.jetbrains.com/zh-cn/go/download/other.html Postman&#xff1a; 下载官网&#xff1a;https://www.postman.com/downloads/ 效果图(使用Postman) Google&#xff1a; QQ&#xff1a; And …...

Docker 部署 Nginx 实现一个极简的 负载均衡

背景: Nginx是异步框架的网页服务器&#xff0c;其常用作反向代理(负载均衡器)。在一般的小项目中, 服务器不多, 如果不考虑使用服务注册与发现, 使用Nginx 可以容易实现负载均衡。 在特此写一个快速入门 Nginx 的技术贴, 使用 Docker 部署 Nginx, 实现一个极简的加权轮询负载均…...

Java刷题总结(面试)

1、String类 String不可变 java 中String是 immutable的&#xff0c;也就是不可变&#xff0c;一旦初始化&#xff0c;其引用指向的内容是不可变的。 也就是说&#xff0c;String str “aa”&#xff1b;str“bb”&#xff1b;第二句不是改变“aa”所存储地址的内容&#xf…...

ipad air6电容笔推荐,2024十大高性价比电容笔排行榜!

​电容笔作为ipad的最佳拍档&#xff0c;为学生党和打工人带来了极大的便利&#xff0c;二者搭配效率真的大大提升&#xff0c;但是&#xff0c;如何选购一支适合自己的电容笔呢&#xff1f;作为一个对数码设备非常感兴趣并且有一定了解的人&#xff0c;我根据自己多年的使用经…...

Java Memorandum

Java Memorandum 1 定义安全的集合2 collection集合用迭代器删除元素时避免并发修改异常3 异常捕获4 RequestBody和RequestParam和Parameter区别4.1 RequestBody4.2 RequestParam4.3 Parameter 1 定义安全的集合 void old() {ArrayList<Apple> apples new ArrayList<…...

大数据学习之 Hadoop部署

Hadoop部署 Linux桌面模式关闭 # 设置 systemctl set-default multi-user.target # 重启 reboot防火墙关闭 systemctl status firewalld systemctl stop firewalld # 关闭开机自启 systemctl disable firewalld配置Java环境 echo $JAVA_HOME java -version # Java配置 # 上传ja…...

xxe漏洞--xml外部实体注入漏洞

1.xxe漏洞介绍 XXE&#xff08;XML External Entity Injection&#xff09;是一种攻击技术&#xff0c;它允许攻击者注入恶意的外部实体到XML文档中。如果应用程序处理XML输入时未正确配置&#xff0c;攻击者可以利用这个漏洞访问受影响系统上的敏感文件、执行远程代码、探测内…...

Nginx反向代理与负载均衡:让网站像海豚一样灵活

引言&#xff1a;"当网站遇上海豚&#xff1a;Nginx让数据流动更流畅&#xff01;"想象一下&#xff0c;你的网站是一片繁忙的海域&#xff0c;而Nginx就像一群聪明的海豚&#xff0c;它们不仅能够迅速地找到最佳的捕食路线&#xff08;反向代理&#xff09;&#xf…...

企业应考虑的优秀云安全措施

作为云客户&#xff0c;企业有责任确保正确使用他们提供的工具来保证数据和应用程序的安全。让德迅云安全来跟大家一起研究一些典型企业应该考虑的优秀云安全措施。 在数据安全和隐私方面&#xff0c;企业是否在努力跟上疫情的发展?企业不是一个人。就像多年以前&#xff0c;C…...

如何将老板的游戏机接入阿里云自建K8S跑大模型(下)- 安装nvidia/gpu-operator支持GPU在容器中共享

文章目录 安装nvidia/gpu-operator支持GPU在容器中共享 安装nvidia/gpu-operator支持GPU在容器中共享 安装 nvidia/gpu-operator遇到两个问题&#xff1a; 由于我们都懂的某个原因&#xff0c;导致某些镜像一直现在不成功。 解决办法&#xff0c;准备一个&#x1fa9c;&#…...

代码随想录-Day16

104. 二叉树的最大深度 方法一&#xff1a;深度优先搜索 class Solution {public int maxDepth(TreeNode root) {if (root null) {return 0;} else {int leftHeight maxDepth(root.left);int rightHeight maxDepth(root.right);return Math.max(leftHeight, rightHeight) …...

31.@Anonymous

1►@Anonymous原理 大家应该已经习惯我的教学套路,很多时候都是先使用,然后讲述原理。 上节课我们使用了注解@Anonymous,然后接口就可以直接被访问到了,不用token!不用token!不用token!。 我们一般知道,注解是给程序看的,给机器看的,当然也是给程序员看的。注解如果…...

oracle 表同一列只取最新一条数据写法

select * from (select t.*,row_number() over(partition by 去重列名 order by 排序列名 desc) as rnfrom 表名)where rn1 1.row_number() over(....): 为每条数据分配一个行号,1.2.3....这样的 2.partition by : 以某列作为分组&#xff0c;每个分组行号从1开始&#xf…...

C语言游戏实战(12):植物大战僵尸(坤版)

植物大战僵尸 前言&#xff1a; 本游戏使用C语言和easyx图形库编写&#xff0c;通过这个项目我们可以深度的掌握C语言的各种语言特性和高级开发技巧&#xff0c;以及锻炼我们独立的项目开发能力&#xff0c; 在开始编写代码之前&#xff0c;我们需要先了解一下游戏的基本规则…...

提权方式及原理汇总

一、Linux提权 1、SUID提权 SUID&#xff08;设置用户ID&#xff09;是赋予文件的一种权限&#xff0c;它会出现在文件拥有者权限的执行位上&#xff0c;具有这种权限的文件会在其执行时&#xff0c;使调用者暂时获得该文件拥有者的权限。 为可执行文件添加suid权限的目的是简…...

【leetcode----二叉树中的最大路径和】

二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root &#xff0c…...

Rust: 编译过程中链接器 `cc` 没有找到

这个错误信息表明在编译过程中链接器 cc 没有找到。cc 通常是 C 编译器的符号链接&#xff0c;它指向系统上的实际 C 编译器&#xff0c;如 gcc 或 clang。这个错误通常意味着你的系统缺少必要的编译工具链。 要解决这个问题&#xff0c;你需要确保你的系统上安装了 C 编译器。…...

【vue-3】动态属性绑定v-bind

1、文本动态绑定&#xff1a; <input type"text" v-bind:value"web.url"> 简写&#xff1a; <input type"text" :value"web.url"> 2、文字样式动态绑定 <b :class"{textColor:web.fontStatus}">vue学…...

Rust:多线程环境下使用 Mutex<T> 还是 Arc<Mutex<T>> ?

在 Rust 中&#xff0c;Mutex 本身不是线程不安全的&#xff1b;它提供了内部的线程同步机制。然而&#xff0c;如果你想在多线程环境中共享同一个 Mutex&#xff0c;你需要确保这个 Mutex 可以被多个线程访问。为此&#xff0c;你通常需要使用 Arc<Mutex<T>>。Arc…...

关于如何创建一个可配置的 SpringBoot Web 项目的全局异常处理

前情概要 这个问题其实困扰了我一周时间&#xff0c;一周都在 Google 上旅游&#xff0c;我要如何动态的设置 RestControllerAdvice 里面的 basePackages 以及 baseClasses 的值呢&#xff1f;经过一周的时间寻求无果之后打算决定放弃的我终于找到了一些关键的线索。 当然在此…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...