项目1:登录功能设计

news2024/11/24 7:48:34

  • 需求
    • 后端接口设计
  • MySQL表
  • 常用功能模块
    • 后端总和
  • 前端实现方案
    • home页面

需求

实现一个登录功能

实现的功能

  1. 注册(邮箱注册)
  2. 登录(邮箱+密码)
  3. 重置密码
  4. 查看操作记录(登录, 注册, 重置密码, 登出. 都算操作)
  5. 登出

后端接口设计

1. 人机验证

只要下面出现 人机验证 的功能都需要使用到该接口

路径:http://localhost:8888/verifypic

方法get

功能:获取图片验证码

请求参数: 无

响应数据(示例)

{
    "code": 200,
    "msg""ok",
    "data": {
        "picid""52fa7b86-cf25-4822-a12c-d48e5b4d7f31",
        "png""iVBORw0KGgoAAAANSUhEUgAAAPAAAABQCAMAAAAQlwhOAAAAP1BMVEUAAABNKGM1EEu9mNOUb6pvSoVFIFtZNG96VZA2EUy5lM93Uo2ifbhEH1pFIFt/WpVkP3qrhsFkP3pKJWBnQn3MHHtHAAAAAXRSTlMAQObYZgAABDFJREFUeJzsWuuSsyAMTdZpd6b2Mjv7/u/6zacFchGINkXdevaPi1xyOCEELBw4cGCH+F7bgMb4/v44xmsb8Ll4PB6q7GsVS9rg8dCMv77+MOMnYYx/8AkKI4GsECbhbwABR75MYPIWxkmwce667k12uiHQmRI4FAxvLZS7bvOMkWooFA5l49sJX1fYMOFgfOKqawSWgXMdmyX8n8L4kI3LyJdubFHGNvlSveKj2nl1/NojYgAKAifdOF+rE28ZmKJuJJP31N3zDVtLoi0UlvX3TTg4sllhW6ZhTkjagrB9ShzMpNELZMahSmW3xs25NaKooFQNvp223NhoqIyYTyvRnH41hs4M5aaEOvsIkzQxFaTjLStM7GL/osilIZXTqD7Zra/Cfd+79DN9IiDPIpVmjTB7WBrlN6ZfFvS9L2NWQNawOizRRtwNZKe+W5cnYa4OglZ4fO66LlQWbiH6QNyywjL88kSaEBlOOnQ7yiwD8q9nyOr7/selI6VwLrKOhInCopHu0lNhAPj5cWIs/mVkTumFUli0Ar2knTclH74KjO+JM4aMwpzkAoWvXtYvALPxJN/lUkpKkmZqtjGv1/UYl1XJKQzIFX6W7UBhtS2rt4MaE0ELdd61xcRSoZz+DnyvVxDbMKUb82zvKP0mlK0MhNNGjBpxOvagMBatRAiEAymhLLuw34PC9SWMIaIOCnNVISaUiFjuajMoWTl1YoqeDfGEDJDk3T7jgi5CM/ovcWExI43MXo4CX3FiILsQkutsovsqCs8cUG6kgMlhhb4kXkmFeXEbKBczDsxFQfGllF7zCbLAvTvNQQvoTdE61bwiD7h8xbKTAhdfXpO8gyIzmm8WQD4i1MeWjLnnhtIo5/SdFv9q/F7G7DoRVSZQHVspPLGspaNmPJcM/yqrrLXyJjlukskRK6NPzYq8EZn8Oq6L9FrwBqbkZ/p1nXHdOvNdM1/IpiazYJrQuovVrLMbLxaVrZH19wFm37nVPoGUO5klFln81nb2X0QYGd9ut2hJyfGLoxhNCi3Cg6n+bL61ijfaIhdqyqMYTXq2AP5hztqu0qsp/k62Mo0VLnUWHOhR5m3mZudsf0uvFlJMqVQMlzoL8kTeaE7AO5/Pyjhk1w5zbYkmVB11ILxEXsXRvCT+KxwNo1kbvJqgp17znYyEl06pPIzMyOkRdc7ocE9WOV4gwsD3hf5Zb2avpmJwLDZFWRLW9NSVs8soQC+CDHVBHddcUzbiQuyOzvlUi3ODbFq8nmYA926Ml+liWn8dhpm9Et95wEQZJvhgv78OjGfmqA0Q3VrDhe/MxMsN9/s986aBLSswvt/zjFtgdvR6FUXCpwYG+G80ZZQIsx87vA0rMM6+a8HXP6/ZBfbwyc0ZW/yV8YEDBzaKy+WytglNcbl8GOOD8J/Hp/HdJP4FAAD//55dD54XIp4vAAAAAElFTkSuQmCC"
    }
}

png为base64编码数据,解码可以得到验证码图片,需要展示给用户

picid为图片的唯一标识符,验证时需要带上才能正常验证,否则将报错

2. 注册1:发送注册验证码:

路径: http://localhost:8888/loginemail1

方法post

功能:人机验证+邮箱验证码发送

请求数据(示例) :数据格式json

{
{
  "picid""c19c20d2-ebea-487a-9c7d-f9c98705728f",
  "email""...@gmail.com",
  "piccode": [
    2,
    3,
    9,
    9
  ]
}

picid为人机验证执行http://localhost:8888/verifypic时返回储存的,

piccode为图片中展示的验证码,需要用户输入

email是用户输入的注册邮箱

响应数据:数据格式json

{
code:状态码判断响应状态
msg:响应提示信息,一般为出错的时候展示给用户
data:nil(data为interface,根据需要进行返回,这里不需要返回值,所以为空nil)
}
alt

3. 注册2:验证+信息填写

路径: http://localhost:8888/signup2

方法post

请求参数示例:

{
    "name""小明",
    "password""abcd123",
    "emailverificationcode": {
        "email""...@gmail.com",
        "verification": 987639
    }
}

name,password,verification为用户输入,Email为注册1时填写的数据

返回:数据格式json

{
  "code": 200,
  "msg""ok",
  "data": nil
}

提示用户成败信息

4. 邮箱密码登录

路径: http://localhost:8888/loginpsw

方法:post

功能:人机验证+密码登录

请求参数:数据格式json

{
    "picid""3a00e077-0e2f-47c0-9123-04a827235a6e",
    "piccode": [
       7,1,9,3
    ],
    "email""...@gmail.com",
    "password""abcd123"
}

piccode,Email,password为用户输入,picid为人机验证功能执行方法

返回:数据格式json


{
  "code": 200,
  "msg""ok",
  "data": {
    idcode:身份码(string)
  }
}

这里如果成功data中会包含一条idcode信息,需要储存cookie,后面home下功能需要用到

alt

5. 邮箱验证码登录1:发送邮箱验证码

路径: http://localhost:8888/loginemail1

方法post

功能:人机验证+邮箱验证码发送

请求参数:数据格式json

{
    "picid""a3e6bcf5-e23b-4406-bd1a-14edf55d82b4",
    "piccode": [
        1,7,9,9
    ],
    "email""...@gmail.com"
}

响应数据:数据格式json

{
  "code": 200,
  "msg""ok",
  "data":nil
}

需要处理响应结果

6. 验证码登录2

路径:http://localhost:8888/loginemail2

方法post

功能:验证验证码

请求参数:数据格式json


{
    "email""...@gmail.com",
    "verification": 715562
}

Email为邮箱验证登录1时填写的数据

verification为用户输入

返回数据:数据格式json

{
    "code": 200,
    "msg""ok",
    "data": {
        "idcode""384a0457-7857-4da4-88b3-72c080634cf5"
    }
}

idcode存cookie,便于后期使用

alt

7. 密码改密

http://localhost:8888/home/resetpaswbypasw

方法post

功能:身份验证+密码验证+改密

参数:数据格式json

{
    "password""abcd123",
    "newpassword""abcd1234",
    "idcode""aed48dfc-868d-46c7-a7ed-23ec3fb09257"
}

idcode从cookie中获取,其他为用户填写,需要确保信息的合理性

返回:数据格式json

{
    "code": 200,
    "msg""ok",
    "data": nil
}

处理成败信息

alt

8. 查询操作信息

http://localhost:8888/getoperations

方法post

功能:身份验证+查询操作信息

请求参数:数据格式json

{
idcode:身份码
}

从cookie中获取

返回数据:数据格式json

{
code:200
msg:"ok"
{
  "operations": [
    {
      "id": 10004,
      "behavior""注册成功",
      "time": 1682166659
    },
    {
      "id": 10004,
      "behavior""signin成功",
      "time": 1682167322
    },
    {
      "id": 10004,
      "behavior""登出成功",
      "time": 1682167868
    },
    {
      "id": 10004,
      "behavior""signin成功",
      "time": 1682169145
    },
    {
      "id": 10004,
      "behavior""signin成功",
      "time": 1682170421
    },
    {
      "id": 10004,
      "behavior""signin成功",
      "time": 1682170825
    },
    {
      "id": 10004,
      "behavior""登出成功",
      "time": 1682171024
    },
    {
      "id": 10004,
      "behavior""signin成功",
      "time": 1682171528
    },
    {
      "id": 10004,
      "behavior""改密成功",
      "time": 1682171658
    }
  ],
  "msg""ok"
}

成功:展示"operations"中的 信息,失败返回失败原因

alt

9. 验证码改密1

路径http://localhost:8888/home/resetpaswbyemail1

方法post

功能:人机验证+身份验证+邮件验证码发送

参数:数据格式json

{
    "picid""52fa7b86-cf25-4822-a12c-d48e5b4d7f31",
    "piccode": [
        7,5,6,4
    ],
    "idcode""ebbeb952-27d2-42cc-979c-bd99e517f0cd"
}

piccode为人机验证功能时返回图片中的验证码,由用户输入

返回:数据格式json

{
    "code": 200,
    "msg""ok",
    "data": null
}

处理成败信息

10. 登出

http://localhost:8888/logout

方法post

功能:身份验证+查询操作信息

请求参数:数据格式json

{
idcode:身份码
}

从cookie中获取

返回数据:数据格式json

{
code:200
msg:"ok"
data:nil

处理成败信息

9. 验证码改密2

路径http://localhost:8888/home/resetpaswbyemail2

方法post

功能:身份验证+邮箱验证码验证+修改密码

参数:数据格式json

{
    "newpassword""abcdef1234ad",
    "idcode""ebbeb952-27d2-42cc-979c-bd99e517f0cd",
    "verification": 123455
}

返回(错误示例):数据格式json

{
    "code": 501,
    "msg""验证码错误验证码错误sql: no rows in result set",
    "data": null
}

上面示例为验证码错误产生的返回

alt
alt

MySQL表

  1. LoginList(注册列表)
名称数据类型
id(主键)int
name(唯一)string
passwordstring
Email(唯一)string
GitHubstring(GitHub账号)
  1. SignInList(登录列表)
名称数据类型
id (主键)int
idcodestring
ExpirationTimeint

解释:身份码为随机字符串,用于对用户操作是进行身份验证,ExpirationTime储存过期时间,如果用户发送请求的时间超过过期时间,则视为用户掉线,需要重新登陆.

  1. OperationRecord(操作列表)
名称数据类型
idint
操作string
timetime
  1. VerificationCode(邮箱验证码储存库)
名称数据类型
Emailstring
Verificationint
ExpirationTimeint
timeint

ExpirationTime储存验证码过期时间,如果用户验证时间超过ExpirationTime则验证无效

每次只查询最后一条匹配的数据(最新生成的验证码数据)

  1. verifition_codes(图片验证表)
名称数据类型
picidstring
code[4]int
create_attime

常用功能模块

1. 邮箱验证码功能

  1. 传入邮箱信息
  2. 生成验证码(随机6位数),与Email一起存入VerificationCode(验证码储存库),同时储存验证码过期时间,次数为默认值0
  3. 向用户邮箱发送验证码
  4. 服务端使用用户的提交验证码时的Email去VerificationCode(验证码储存库)获取最后一条匹配的数据(最新生成的验证码数据)的过期时间,和验证码值,如果现在的时间大于过期时间,返回验证码过期,如果小于过期时间,对比传入的数据与数据库中验证码的值Verification(验证码值)是否相同,.
  5. 处理用户请求
  6. 每30分钟扫描一次数据库,删除超过30的数据

sendemail.go

发送邮件功能

我们这里使用的QQ邮箱的SMTP功能进行发送,你需要有一个QQ邮箱开通该功能

// 使用gomail库创建SMTP客户端
func CreatDialer() *gomail.Dialer {
 return gomail.NewDialer(".qq.com"123"@qq.com""*****"//AuthCode为邮箱的授权码
}

// 发送邮件函数,+邮箱可用性验证
func SendEmail(dialer *gomail.Dialer, to string, verification int) error {
 // 发送邮件的QQ邮箱地址
 qqEmail := "xxxx@qq.com"

 // 检查电子邮件地址是否可用
 err := checkmail.ValidateFormat(to)
 if err != nil {
  return fmt.Errorf("email address %s is not available: %s", to, err.Error())
 }
 // 创建邮件消息
 message := gomail.NewMessage()
 message.SetHeader("From", qqEmail)
 message.SetHeader("To", to)
 message.SetHeader("Subject""验证码")
 message.SetBody("text/plain", fmt.Sprintf("验证码:%d", verification))

 // 发送邮件消息,开携程发生邮件
 go dialer.DialAndSend(message)

 return nil
}

储存验证码到数据库

// 增:验证码列表插入信息
func InsertVerification(db *sql.DB, data *EmailVerificationCode) error {

 // 准备SQL语句

 stmt, err := db.Prepare("REPLACE INTO VerificationCode (Email, Verification, ExpirationTime, time) VALUES (?, ?, ?, ?)")
 if err != nil {
  return err
 }
 defer stmt.Close()

 // 执行插入操作
 _, err = stmt.Exec(data.Email, data.Verification, time.Now().Unix()+5*600)
 if err != nil {
  return err
 }
 return nil
}

储存验证码到数据库与发送逻辑

//获取验证码
verification := GetuVerification(db, emial)
  //储存验证码到数据库
  err = InsertVerification(db, &EmailVerificationCode{
   Email:        emial,
   Verification: verification})
  if err != nil {
   ctx.Returnfunc(401"发送失败,稍后再试"+err.Error(), nil)
   return
  }
  //发送验证码
  err = SendEmail(dialer, emial, verification)
  if err != nil {
   ctx.Returnfunc(401"邮箱错误"+err.Error(), nil)
   return
  }
  ctx.Returnfunc(200"ok"nil)

getverification获取验证码函数

// 获取验证码
// 检查是否进行验证过,用于重新发送验证码时调用,检查用户是否验证过
// 如果没有就发送与原来相同的验证码,否则就发送新验证码
func GetuVerification(db *sql.DB, email string) int {

 // 查询验证码是否正确
 var expirationTime int64 //储存过期时间

 verification, t := 0-1

 err := db.QueryRow("SELECT time, Verification, ExpirationTime FROM VerificationCode WHERE Email = ?", email).Scan(&t, &verification, &expirationTime)
 //当没有验证码发送记录||已经验证过||超出时间限制  则需要重新生成验证码
 if err != nil || t == 1 || expirationTime+2500*60 < time.Now().Unix() {
  fmt.Printf("重新生成验证码%#v\n")
  rand.Seed(time.Now().UnixNano())
  // 生成100000到999999之间的随机数,作为验证码
  return rand.Intn(899999) + 100000
 } else {
  fmt.Printf("旧验证码=%#v\n", verification)
  return verification
 }
}

特殊情况:
9. 重新提交:如果验证码生成后,该用户未进行任何验证(time=0),则生成相同的验证码,并告知用户,如果之前有验证失败(time>0),就生成不同的验证码.避免某些用户在5分钟内没法收到验证码的情况.

邮箱验证码验证:

// 查: 验证验证码
func Verify(db *sql.DB, email string, verification int) error {

 // 查询验证码是否正确
 var expirationTime int64 //储存过期时间
 //代表已经验证过
 _, err := db.Exec("UPDATE VerificationCode SET time = ? WHERE Email = ?", 1, email)
 if err != nil {
  return fmt.Errorf("出错了" + err.Error())
 }
 err = db.QueryRow("SELECT ExpirationTime FROM VerificationCode WHERE Email = ? AND Verification = ?", email, verification).Scan(&expirationTime)
 if err != nil {
  return fmt.Errorf("验证码错误" + err.Error())
 }

 // 验证身份认证码是否过期
 if time.Now().Unix() > expirationTime {
  return fmt.Errorf("过期")
 }
 return nil
}

图片验证功能

  1. 当调用获取图片验证功能,生成一组4位的验证码与验证码唯一标识符picid,储存进数据库,再 根据验证码生成一个base64的图片编码,将图片编码与唯一标识符发送给前端
  2. 用户填写验证码提交给服务端,将携带刚刚的图片唯一标识符
  3. 后端查找MySQL库中对应的code(验证码)与用户传入的验证码是否相同
  4. 相同则继续用户的操作
  5. 不同返回验证码错误

生成储存验证码,并响应验证图片

// 发送图片验证码图片中间件(用户提交请求时)
func Picverfi(db *sql.DB) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {

  // 生成验证码
  id := uuid.New().String() //生成唯一标识符
  code := captcha.RandomDigits(4)
  img := captcha.NewImage(id, code, captcha.StdWidth, captcha.StdHeight)
  // 生成验证码
  var buf bytes.Buffer
  _, err := img.WriteTo(&buf)
  if err != nil {
   ctx.Returnfunc(401"出错了,点击重试"nil)
  }

  // 将验证码和图片存储到数据库中
  encodedImage := base64.StdEncoding.EncodeToString(buf.Bytes())
  verificationCode := VerificationCode{PicCode: code, CreatedAt: time.Now()}
  _, err = db.Exec("INSERT INTO verification_codes (id, code, created_at) VALUES (?, ?, ?)", id, verificationCode.PicCode, verificationCode.CreatedAt)
  if err != nil {
   ctx.Returnfunc(http.StatusInternalServerError, "Internal Server Error:"+err.Error(), nil)
   return
  }

  // 返回图片与唯一标识符
  ctx.Returnfunc(200"ok", gee7.H{
   "picid": id,
   "png":   encodedImage,
  })

  fmt.Printf("id=%#v\ncode=%d", id, code)
 }
}

验证码验证:

// 图片验证码验证操作:用户传入图片id(唯一标识符发送图片信息时给的),+验证码
func Verifypiccode(db *sql.DB, id string, code []byte) error {
 var createdAt []uint8
 err := db.QueryRow("SELECT created_at FROM verification_codes WHERE id = ? AND code = ?", id, code).Scan(&createdAt)
 if err != nil {
  return err
 }
 created_ats := string(createdAt)
 created_at, err := time.Parse("2006-01-02 15:04:05", created_ats)
 if err != nil {
  return err
 }
 // 删除验证码
 db.Exec("DELETE FROM verification_codes WHERE id = ?", id)
 if time.Now().Unix() > created_at.Unix()+5*60 { //时效5分钟
  return fmt.Errorf("验证码过期")
 }
 return nil
}

身份验证功能:

为确保用户的身份与登录状态,我们使用身份验证功能来实现

  1. 用户请求(自带idcode身份码(cookie中获取))
  2. 使用idcode查找SignInList(登录列表)的对应信息(如果没有,返回未登录并退回登录页面)
  3. 检查身份码与过期时间(如果身份码不正确或者已过期,返回登录过期并退回登录页面)
  4. 核验成功重置过期时间(从核验成功开始30分钟后)
  5. 处理用户的请求
 //储存登录用户信息
 signinlest := funcmod.SignInList{}
  
  //登录成功
  //向登录列表中加入元素
  signinlest[idcode] = &signindata

我们使用的是map进行储存

验证

  //身份验证
  if signinlest[data.IDcode] == nil {
   ctx.Returnfunc(420, "非法访问", nil)
   return
  }

  //时间验证

  if signinlest[data.IDcode].ExpirationTime <time.Now().Unix(){
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }

密码验证功能

  1. 用户上传邮箱/账号(id)+密码
  2. 真人验证:图片验证
  3. 核验密码是否规范
  4. 密码加密
  5. 查找LoginList(注册列表)中对应的邮箱/账号(如果没有返回账号/邮箱未注册)
  6. 核验密码是否正确
  7. 处理用户请求

密码验证功能

// 查:验证user(注册列表)密码
func Verifypassword(db *sql.DB, id int, password string) error {

 count := 0
 //SELECT COUNT(*)这个函数一般不会报错,如果找不到值,那么count=0
 err := db.QueryRow("SELECT COUNT(*) FROM user WHERE id = ? AND password = ? ", id, encryptPassword(password)).Scan(&count)
 fmt.Printf("count=%#v\n", count)
 if err != nil || count == 0 {
  return fmt.Errorf("出错了:密码错误:%#v", err)
 }
 return nil
}


储存操作信息功能

  1. 通过请求数据的id与方法确定请求操作的种类,将post操作的内容与id和请求时间一起储存在OperationRecord(操作列表)中

后端总和

main.go

package main

import (
 "fmt"
 "goweb/day7/gee7"
 "goweb/logindata/funcmod"
 "goweb/logindata/signup"
 "log"
 "time"
 // "github.com/rs/cors"
)

func main() {
 r := gee7.New()
 // r.Use(cors.Default())
 //设置统一日志输出(日期+时间+输出文件+行+信息)
 r.Use(funcmod.Dfot())
 log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
 db, err := funcmod.Opensql()
 if err != nil {
  fmt.Printf("数据库出错=%#v\n", err)
  return
 }
 defer db.Close()

 //储存登录用户信息
 signinlest := funcmod.SignInList{}
 signinlest["ebbeb952-27d2-42cc-979c-bd99e517f0cd"] = &funcmod.SignInData{
  ID:             10007,
  ExpirationTime: time.Now().Unix() + 30*60,
 }
 r.Use(funcmod.DBMid(db))
 // funcmod.Changepassword(db, 10007, "abcd1234")
 //连接邮箱
 dailer := funcmod.CreatDialer()
 //获取图片验证码
 r.GET("/verifypic", funcmod.Picverfi(db))
 //注册1,1. 图片验证,;2. 发送邮件验证码
 r.POST("/signup1"func(ctx *gee7.Context) {
  data := funcmod.Login1data{}
  //读取用户传入的json数据
  err = ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(410"解析数据错误"+err.Error(), nil)
   funcmod.Logwarning("解析数据错误" + err.Error())
   return
  }
  if funcmod.Validatedata(data.Email) {
   ctx.Returnfunc(402"email不合理,请规范填写数据"nil)
   funcmod.Logwarning("email不合理")
   return
  }
  //验证图片验证码是否正确
  err = funcmod.Verifypiccode(db, data.PicID, data.PicCode)
  if err != nil {
   ctx.Returnfunc(401"验证码错误"+err.Error(), nil)
   funcmod.Logwarning("验证码错误")
   return
  }
  _, err = funcmod.GetuserId(db, data.Email)
  if err == nil {
   ctx.Returnfunc(410"账号已经注册了"nil)

   return
  }

  verification := funcmod.GetuVerification(db, data.Email)
  //储存验证码到数据库
  err = funcmod.InsertVerification(db, &funcmod.EmailVerificationCode{
   Email:        data.Email,
   Verification: verification})
  if err != nil {
   ctx.Returnfunc(401"发送失败,稍后再试"+err.Error(), nil)
   return
  }
  //发送验证码
  err = funcmod.SendEmail(dailer, data.Email, verification)
  if err != nil {
   ctx.Returnfunc(401"发送失败"+err.Error(), nil)
   return
  }

  ctx.Returnfunc(200"ok"nil)
 })
 //login2:1.验证验证码,;2. 向用户列表插入数据
 r.POST("/signup2"func(ctx *gee7.Context) {
  data := funcmod.Logindata{}
  //读取用户传入的json数据
  err = ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(410"解析数据错误"+err.Error(), nil)
   return
  }
  if funcmod.Validatedata(data.Name) {
   ctx.Returnfunc(402"name不合理,请规范填写数据"nil)
   return
  }
  if funcmod.Validatedata(data.Password) {
   ctx.Returnfunc(402"password不合理,请规范填写数据"nil)
   return
  }
  //验证用户验证码是否正确
  err = funcmod.Verify(db, data.Email, data.Verification)
  if err != nil {
   ctx.Returnfunc(501"验证码错误"+err.Error(), nil)
   return
  }
  //插入用户数据
  id, err := funcmod.AddLogindata(db, &data)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
  } else {
   // 添加操作信息
   err = funcmod.Addoperation(db, id, "注册成功")
   if err != nil {
    ctx.Returnfunc(401, err.Error(), nil)
   } else {
    ctx.Returnfunc(200"ok"nil)
   }
  }
 })
 r.POST("/loginpsw", signup.SigninBypassword(db, signinlest))
 r.POST("/loginemail1", signup.SigninByEmail1(db, dailer))
 r.POST("/loginemail2", signup.SigninByEmail2(db, signinlest))

 home := r.Group("/home")
 home.Use(funcmod.Addoperationmid(db))
 //修改密码
 //密码改密
 home.POST("/resetpaswbypasw", funcmod.ResetpaswBypassword(db, signinlest))
 //验证码改密
 home.POST("/resetpaswbyemail1", funcmod.ResetpaswByEmail1(db, dailer, signinlest))
 home.POST("/resetpaswbyemail2", funcmod.ResetpaswByEmail2(db, signinlest))
 // 登出
 r.POST("/signout"func(ctx *gee7.Context) {
  data := funcmod.IDCode{}
  //读取用户传入的json数据
  err = ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(410"解析数据错误"+err.Error(), nil)
   return
  }

  //身份验证
  if signinlest[data.IDcode] == nil {
   ctx.Returnfunc(420"非法访问"nil)
   return
  }
  ctx.Userid = signinlest[data.IDcode].ID

  //时间验证

  if signinlest[data.IDcode].ExpirationTime < time.Now().Unix() {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  //删除登录列表信息
  delete(signinlest, data.IDcode)

  ctx.Returnfunc(200"ok"nil)

 })
 // 获取操作信息
 r.POST("/getoperations"func(ctx *gee7.Context) {
  data := funcmod.IDCode{}
  //读取用户传入的json数据
  err = ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(410"解析数据错误"+err.Error(), nil)
   return
  }
  if signinlest[data.IDcode] == nil {
   ctx.Returnfunc(420"非法访问"nil)
   return
  }
  r, err := funcmod.Getoperations(db, signinlest[data.IDcode].ID)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  ctx.Returnfunc(200"ok", gee7.H{
   "operations": r,
  })
 })

 r.Run(":8888")
}

各种响应函数与中间件函数



// 允许跨域访问中间件
func Dfot() gee7.HandlerFunc {
 return func(ctx *gee7.Context) {
  // 处理预检请求
  if ctx.Req.Method == "OPTIONS" {
   // 验证预检请求的来源、头部字段和请求方法是否符合预期
   // ...

   // 设置响应头部字段
   ctx.Writer.Header().Set("Access-Control-Allow-Origin""*")
   ctx.Writer.Header().Set("Access-Control-Allow-Methods""POST, GET, OPTIONS")
   ctx.Writer.Header().Set("Access-Control-Allow-Headers""Content-Type")

   // 返回状态码 200 和空响应体
   ctx.Writer.WriteHeader(http.StatusOK)
   return
  }
 }
}



// 统一输出日志
// 程序正常运行
func Lognormal(msg string) {
 log.Println("normal:", msg)
}

// 警告
func Logwarning(msg string) {
 log.Println("warning:", msg)
}

// 错误
func Logerror(msg string) {
 log.Panicln("error:", msg)
}

// 添加用户操作中间件
func Addoperationmid(db *sql.DB) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {
  ctx.Next()
  //没有id信息,直接退出无需插入操作信息
  if ctx.Userid == 0 {
   return
  }
  defer func() {
   if ctx.StatusCode >= 400 {
    Addoperation(db, ctx.Userid, "操作"+ctx.Path+"失败")
   } else {
    Addoperation(db, ctx.Userid, "操作"+ctx.Path+"成功")
   }
  }()
 }
}

// 发送图片验证码图片中间件(用户提交请求时)
func Picverfi(db *sql.DB) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {

  // 生成验证码
  id := uuid.New().String() //生成唯一标识符
  code := captcha.RandomDigits(4)
  img := captcha.NewImage(id, code, captcha.StdWidth, captcha.StdHeight)
  // 生成验证码
  var buf bytes.Buffer
  _, err := img.WriteTo(&buf)
  if err != nil {
   ctx.Returnfunc(401"出错了,点击重试"nil)
  }

  // 将验证码和图片存储到数据库中
  encodedImage := base64.StdEncoding.EncodeToString(buf.Bytes())
  verificationCode := VerificationCode{PicCode: code, CreatedAt: time.Now()}
  _, err = db.Exec("INSERT INTO verification_codes (id, code, created_at) VALUES (?, ?, ?)", id, verificationCode.PicCode, verificationCode.CreatedAt)
  if err != nil {
   ctx.Returnfunc(http.StatusInternalServerError, "Internal Server Error:"+err.Error(), nil)
   return
  }

  // 返回图片与唯一标识符
  ctx.Returnfunc(200"ok", gee7.H{
   "picid": id,
   "png":   encodedImage,
  })

  fmt.Printf("id=%#v\ncode=%d", id, code)
 }
}

// 用于检验用户传入的数据是否合理
// 正则表达式,匹配长度为 3-40 的 ASCII 字符串
// 合理为false不合理为true
func Validatedata(password string) bool {
 regex := regexp.MustCompile(`^[[:ascii:]]{3,40}$`)
 return !regex.MatchString(password)
}

// 密码改密
func ResetpaswBypassword(db *sql.DB, signinlest SignInList) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {
  data := ResetpaswData{}
  //读取用户传入的Returnfunc数据
  err := ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(410"解析数据错误"+err.Error(), nil)
   return
  }
  if signinlest[data.IDcode] == nil {
   ctx.Returnfunc(420"非法访问"nil)
   return
  }
  ctx.Userid = signinlest[data.IDcode].ID
  if Validatedata(data.Password) {
   ctx.Returnfunc(401"password不合理,请规范填写数据"nil)
   return
  }
  if Validatedata(data.Newpassword) {
   ctx.Returnfunc(401"newpassword不合理,请规范填写数据"nil)
   return
  }
  //身份验证
  err = SignInVerification(signinlest, data.IDcode)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  //验证密码
  err = Verifypassword(db, signinlest[data.IDcode].ID, data.Password)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  //验证成功:修改密码
  err = Changepassword(db, signinlest[data.IDcode].ID, data.Newpassword)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  ctx.Returnfunc(200"ok"nil)
 }
}

// 验证改密第一步:1. 图片验证,;2. 发送邮件验证码
func ResetpaswByEmail1(db *sql.DB, dialer *gomail.Dialer, signinlest SignInList) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {
  data := ResetpaswbyemailData1{}
  //读取用户传入的Returnfunc数据
  err := ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(410, err.Error(), nil)
   return
  }
  if signinlest[data.IDcode] == nil {
   ctx.Returnfunc(420"非法访问"nil)
   return
  }
  ctx.Userid = signinlest[data.IDcode].ID
  //验证图片验证码是否正确
  err = Verifypiccode(db, data.PicID, data.PicCode)
  if err != nil {
   ctx.Returnfunc(401"图片验证码错误"+err.Error(), nil)
   return
  }
  // 验证成功:获取用户邮箱
  emial, err := GetuserEmail(db, signinlest[data.IDcode].ID)
  if err != nil {
   ctx.Returnfunc(410"未找到邮箱信息,请联系管理员"+err.Error(), nil)
   return
  }
  verification := GetuVerification(db, emial)
  //储存验证码到数据库
  err = InsertVerification(db, &EmailVerificationCode{
   Email:        emial,
   Verification: verification})
  if err != nil {
   ctx.Returnfunc(401"发送失败,稍后再试"+err.Error(), nil)
   return
  }
  //发送验证码
  err = SendEmail(dialer, emial, verification)
  if err != nil {
   ctx.Returnfunc(401"邮箱错误"+err.Error(), nil)
   return
  }
  ctx.Returnfunc(200"ok"nil)
 }
}

// 完成验证码改密功能:验证验证码是否正确,插入数据
func ResetpaswByEmail2(db *sql.DB, signinlest SignInList) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {
  var data ResetpaswbyemailData2
  err := ctx.Getjson(&data)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  if signinlest[data.IDcode] == nil {
   ctx.Returnfunc(420"非法访问"nil)
   return
  }
  if Validatedata(data.Newpassword) {
   ctx.Returnfunc(402"password不合理,请规范填写数据"nil)
   return
  }
  //获取邮箱
  email, err := GetuserEmail(db, signinlest[data.IDcode].ID)
  if err != nil {
   ctx.Returnfunc(401"验证码错误"+err.Error(), nil)
   return
  }
  // 验证用户邮箱验证码是否正确
  err = Verify(db, email, data.Verification)
  if err != nil {
   ctx.Returnfunc(501"验证码错误"+err.Error(), nil)
   return
  }
  //验证成功,获取id信息
  id, err := GetuserId(db, email)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  //修改密码
  err = Changepassword(db, id, data.Newpassword)
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  // 添加操作信息
  err = Addoperation(db, id, "改密成功")
  if err != nil {
   ctx.Returnfunc(401, err.Error(), nil)
   return
  }
  ctx.Returnfunc(200"ok"nil)
 }
}

底层数据库操作:

//对数据库操作的所有函数
import (
 "database/sql"
 "fmt"
 "math/rand"
 "time"
)

// 添加操作信息,向表OperationRecord(操作列表)添加
func Addoperation(db *sql.DB, id int, operation string) error {

 _, err := db.Exec("insert into OperationRecord (id,behavior,time) VALUES (?, ?, ?)", id, operation, time.Now().Unix())
 if err != nil {
  return err
 }
 return nil
}

// 获取:表OperationRecord(操作列表)的数据
func Getoperations(db *sql.DB, id int) ([]OperationRecord, error) {

 rows, err := db.Query("SELECT behavior, time FROM OperationRecord WHERE id = ?", id)
 if err != nil {
  return nil, err
 }
 defer rows.Close()
 result := []OperationRecord{}
 for rows.Next() {
  p := &OperationRecord{ID: id}
  err = rows.Scan(&p.Behavior, &p.Time)
  if err != nil {
   return result, err
  }
  result = append(result, *p)
 }
 if err := rows.Err(); err != nil {
  return result, err
 }
 return result, nil
}

// user表------------------------------------------------------------------

// add:向user(注册列表)添加元素
func AddLogindata(db *sql.DB, logindata *Logindata) (int, error) {

 r, err := db.Exec("insert into user (name,password,Email) VALUES (?, ?, ?)", logindata.Name, encryptPassword(logindata.Password), logindata.Email)
 if err != nil {
  return 0, err
 }
 id, err := r.LastInsertId()
 return int(id), err
}

// ((id是操作用的,不是给用户登录用的))
// get:获取:向user(注册列表)获取id,登陆成功,获取id
func GetuserId(db *sql.DB, email string) (int, error) {

 id := -1
 err := db.QueryRow("SELECT id FROM user WHERE Email = ?", email).Scan(&id)
 if err != nil {
  return -1, err
 }
 return id, nil
}

// get:获取:向user(注册列表)获取email(改密时查Email,不能由用户传入Email,)
func GetuserEmail(db *sql.DB, id int) (string, error) {

 Email := ""
 err := db.QueryRow("SELECT Email FROM user WHERE id = ?", id).Scan(&Email)
 if err != nil {
  return "", err
 }
 return Email, nil
}

// 查:验证user(注册列表)密码
func Verifypassword(db *sql.DB, id int, password string) error {

 count := 0
 //SELECT COUNT(*)这个函数一般不会报错,如果找不到值,那么count=0
 err := db.QueryRow("SELECT COUNT(*) FROM user WHERE id = ? AND password = ? ", id, encryptPassword(password)).Scan(&count)
 fmt.Printf("count=%#v\n", count)
 if err != nil || count == 0 {
  return fmt.Errorf("出错了:密码错误:%#v", err)
 }
 return nil
}

// 改:修改:user(注册列表)密码
func Changepassword(db *sql.DB, id int, newpassword string) error {

 _, err := db.Exec("UPDATE user SET password = ? WHERE id = ?", encryptPassword(newpassword), id)
 if err != nil {
  return err
 }
 return nil
}

// -SignInlist-----------删除其他操作,使用内存管理登录列表---------------------------------------------------------

// 查:SignIn查询身份验证功能:传入SignInlist
func SignInVerification(signinlest SignInList, idcode string) error {

 // 查询身份认证码是否正确

 if signinlest[idcode] == nil {
  return fmt.Errorf("非法访问")
 }

 // 验证身份认证码是否过期
 if time.Now().Unix() > signinlest[idcode].ExpirationTime {
  return fmt.Errorf("身份验证过期,请重新登陆")
 }

 // 验证成功.更新身份认证码的过期时间
 signinlest[idcode].ExpirationTime = time.Now().Unix() + 30*60
 return nil
}

// // 增:向登录列表插入信息
// func InsertSignInData(db *sql.DB, data *SignInData) error {

//  // 准备SQL语句
//  stmt, err := db.Prepare("REPLACE INTO SignInList (id, IDcode, ExpirationTime) VALUES (?, ?, ?)")
//  if err != nil {
//   return err
//  }
//  defer stmt.Close()

//  // 执行插入操作
//  result, err := stmt.Exec(data.ID, data.IDCode, time.Now().Unix()+30*60)
//  if err != nil {
//   return err
//  }

//  // 输出插入结果
//  _, err = result.RowsAffected()
//  if err != nil {
//   return err
//  }
//  return nil
// }

// // delete:删除登录信息; 登出删除登录列表元素
// func Deletesignindata(db *sql.DB, id int) error {
//  _, err := db.Exec("DELETE FROM SignInList WHERE id = ?", id)
//  if err != nil {
//   return err
//  }
//  return nil
// }

// VerificationCode(验证码储存库)----------------------------------------------------------

// 获取验证码
// 检查是否进行验证过,用于重新发送验证码时调用,检查用户是否验证过
// 如果没有就发送与原来相同的验证码,否则就发送新验证码
func GetuVerification(db *sql.DB, email string) int {

 // 查询验证码是否正确
 var expirationTime int64 //储存过期时间

 verification, t := 0-1

 err := db.QueryRow("SELECT time, Verification, ExpirationTime FROM VerificationCode WHERE Email = ?", email).Scan(&t, &verification, &expirationTime)
 //当没有验证码发送记录||已经验证过||超出时间限制  则需要重新生成验证码
 if err != nil || t == 1 || expirationTime+2500*60 < time.Now().Unix() {
  fmt.Printf("重新生成验证码%#v\n")
  rand.Seed(time.Now().UnixNano())
  // 生成100000到999999之间的随机数,作为验证码
  return rand.Intn(899999) + 100000
 } else {
  fmt.Printf("旧验证码=%#v\n", verification)
  return verification
 }
}

// 查: 验证验证码
func Verify(db *sql.DB, email string, verification int) error {

 // 查询验证码是否正确
 var expirationTime int64 //储存过期时间
 //代表已经验证过
 _, err := db.Exec("UPDATE VerificationCode SET time = ? WHERE Email = ?"1, email)
 if err != nil {
  return fmt.Errorf("出错了" + err.Error())
 }
 err = db.QueryRow("SELECT ExpirationTime FROM VerificationCode WHERE Email = ? AND Verification = ?", email, verification).Scan(&expirationTime)
 if err != nil {
  return fmt.Errorf("验证码错误" + err.Error())
 }

 // 验证身份认证码是否过期
 if time.Now().Unix() > expirationTime {
  return fmt.Errorf("过期")
 }
 return nil
}

// 增:验证码列表插入信息
func InsertVerification(db *sql.DB, data *EmailVerificationCode) error {

 // 准备SQL语句

 stmt, err := db.Prepare("REPLACE INTO VerificationCode (Email, Verification, ExpirationTime, time) VALUES (?, ?, ?, ?)")
 if err != nil {
  return err
 }
 defer stmt.Close()

 // 执行插入操作
 _, err = stmt.Exec(data.Email, data.Verification, time.Now().Unix()+5*600)
 if err != nil {
  return err
 }
 return nil
}

// get:获取:验证次数,重发验证码0无,1有
func GetuVerificationtime(db *sql.DB, email string) (int, error) {
 t := -1
 err := db.QueryRow("SELECT time FROM user WHERE Email = ?", email).Scan(&t)
 if err != nil {
  return -1, err
 }
 return t, nil
}

// // ----------------------------------------------------------
// func Insertuserdata(db *sql.DB, data Logindata) error {

//  // 准备SQL语句

//  stmt, err := db.Prepare("INSERT INTO user (Email, name, password) VALUES (?, ?, ?)")
//  if err != nil {
//   return err
//  }
//  defer stmt.Close()

//  // 执行插入操作
//  _, err = stmt.Exec(data.Email, data.Name, encryptPassword(data.Password))
//  if err != nil {
//   return err
//  }
//  return nil
// }
//
// 图片验证码验证操作:用户传入图片id(唯一标识符发送图片信息时给的),+验证码
func Verifypiccode(db *sql.DB, id string, code []byte) error {
 var createdAt []uint8
 err := db.QueryRow("SELECT created_at FROM verification_codes WHERE id = ? AND code = ?", id, code).Scan(&createdAt)
 if err != nil {
  return err
 }
 created_ats := string(createdAt)
 created_at, err := time.Parse("2006-01-02 15:04:05", created_ats)
 if err != nil {
  return err
 }
 // 删除验证码
 db.Exec("DELETE FROM verification_codes WHERE id = ?", id)
 if time.Now().Unix() > created_at.Unix()+5*60 { //时效5分钟
  return fmt.Errorf("验证码过期")
 }
 return nil
}

事务处理

import (
 "database/sql"
 "goweb/day7/gee7"
)

// 事务中间件,如果用户响应出错,则数据库回滚到响应之前的状态
func DBMid(db *sql.DB) gee7.HandlerFunc {
 return func(ctx *gee7.Context) {

  // 开始事务
  tx, err := db.Begin()
  if err != nil {
   ctx.FailJson(401, err.Error())
   return
  }

  // 调用下一个处理器
  ctx.Next()

  defer func() {
   // 如果响应出现错误,回滚事务,500以内的错误回滚
   if ctx.StatusCode >= 400 && ctx.StatusCode <= 500 {
    tx.Rollback()
    return
   }
   // 提交事务
   tx.Commit()
  }()
 }
}

数据体:


import "time"

// 登录列表方便查找,避免查找sql 提升性能.key为身份码
type SignInList map[string]*SignInData

// 用于 储存登录信息
type SignInData struct {
 ID             int   `json:"id"`
 ExpirationTime int64 `json:"ExpirationTime"`
}

// 身份验证信息(用于接收用户传入的身份码)
type IDCode struct {
 IDcode string `json:"idcode"`
}

// 邮箱验证码信息(验证码需要真人验证之后再发送)(登录时可以直接使用)
type EmailVerificationCode struct {
 Email        string `json:"email"`
 Verification int    `json:"verification"`
}

// 注册传入信息2,邮箱验证码信息+用户基本信息
type Logindata struct {
 EmailVerificationCode `json:"emailverificationcode"`
 Name                  string `json:"name"`
 Password              string `json:"password"`
}

// 注册传入信息1,id为图片的唯一标识符,code为用户传入 的图片验证码, Email为需要发送验证邮件的邮箱
type Login1data struct {
 PicID   string `json:"picid"`
 PicCode []byte `json:"piccode"`
 Email   string `json:"email"`
}

// 操作信息列表
type OperationRecord struct {
 ID       int    `json:"id"`
 Behavior string `json:"behavior"`
 Time     int    `json:"time"`
}

// 密码:改密接受信息
type ResetpaswData struct {
 IDCode
 Password    string `json:"password"`
 Newpassword string `json:"newpassword"`
}

// 改密接受信息1:图片验证+身份验证
type ResetpaswbyemailData1 struct {
 IDCode
 PicID   string `json:"picid"`
 PicCode []byte `json:"piccode"`
}

// 验证码验证+新密码
type ResetpaswbyemailData2 struct {
 IDCode
 Verification int    `json:"verification"`
 Newpassword  string `json:"newpassword"`
}

// 验证码数据模型
type VerificationCode struct {
 PicID     string    `json:"picid"`
 PicCode   []byte    `json:"piccode"`
 Image     string    `json:"image"`
 CreatedAt time.Time `json:"created_at"`
}

// 密码登录信息(图片验证信息+邮箱+密码)
type Signupbypasw struct {
 PicID    string `json:"picid"`
 PicCode  []byte `json:"piccode"`
 Email    string `json:"email"`
 Password string `json:"password"`
}

// 发送验证码时上传的信息(图片验证信息+身份验证信息)
type Signupbyemail1 struct {
 PicID   string `json:"picid"`
 PicCode []byte `json:"piccode"`
 Email   string `json:"email"`
}

// 使用邮箱验证码登录时上传的信息(图片验证信息+身份验证信息)
type Signupbyemail2 struct {
 EmailVerificationCode
}

哈希加密:(密码)


//密码哈希加密算法

func encryptPassword(password string) string {
 // 将字符串类型的密码转换为字节类型
 passwordBytes := []byte(password)
 // 创建SHA-256哈希对象
 sha256Hash := sha256.New()
 // 将密码字节数据传入哈希对象
 sha256Hash.Write(passwordBytes)
 // 获取哈希值的字节数据
 hashBytes := sha256Hash.Sum(nil)
 // 将字节数据转换为十六进制字符串
 hashString := hex.EncodeToString(hashBytes)
 // 返回十六进制字符串类型的哈希值
 return hashString
}

前端实现方案

注册1 signup1(注册)

3个按钮:

1 获取图片验证:访问图片验证码接口,展示base64验证图片并储存picid

2 提交:访问注册1接口

表单:

名称数据类型
Emailstring
验证码[4]int

向验证码发送接口提交请求,携带json数据,并处理响应结果

如果成功跳转页面到signup2页面,失败则展示失败信息

3 登录:跳转页面到LoginPsw(登录页面)

alt

代码示例:

<template>
    <div>
      <h3>注册1</h3>
    </div>
    <div>
      <button @click="getVerificationImage">获取验证图片</button>
      <div v-if="verificationImage">
      <img :src="decodeBase64(verificationImage.png)" alt="验证码图片">
    </div>
      <form @submit="submitForm">
        <label for="email">邮箱:</label>
        <input type="email" id="email" v-model="email" required>
        <br>
        <label for="piccode">验证码:</label>
        <input type="text" id="piccode" v-model="piccode" pattern="[0-9]{4}" required>
        <br>
        <button type="submit">提交</button>
        <button @click="goToLogin">登录</button>
      </form>
    </div>
  </template>
  
  <script>
  import axios from 'axios';
  
  export default {
    data() {
      return {
        verificationImagenull,
        picid'',
        email'',
        piccode''
      };
    },
    methods: {
      goToLogin() {
        this.$router.push('/LogInPsw');
      },
      async getVerificationImage() {
        try {
          const response = await axios.get('http://localhost:8888/verifypic');
          if (response.data.code === 200) {
            this.verificationImage = response.data.data;
            this.picid = response.data.data.picid;
            
          }
        } catch (error) {
          console.error('获取验证图片失败', error);
        }
      },
      decodeBase64(base64Data) {
      return `data:image/png;base64, ${base64Data}`;
    },
      async submitForm(event) {
        event.preventDefault();
        if (!this.validateEmail(this.email)) {
          console.error('邮箱格式不正确');
          return;
        }
        if (!this.validatePiccode(this.piccode)) {
          console.error('验证码必须为4位数字');
          return;
        }
        try {
          const requestData = {
            picidthis.picid,
            emailthis.email,
            piccodethis.piccode.split('').map(Number)
          };
          const response = await axios.post('http://localhost:8888/signup1', requestData);
          if (response.data.code === 200) {
            // 成功后进行页面跳转到
            this.$router.push({
        path'/signup-tow',
        query: { emailthis.email },
      });
          }
        } catch (error) {
          console.error('提交失败', error);
        }
      },
      validateEmail(email) {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
      },
      validatePiccode(piccode) {
        return piccode.length === 4 && /^\d+$/.test(piccode);
      }
    }
  };
  
</script>
  

注册2 signup2

3个按钮:

1 重新发送验证码: 退回到signup1页面

2 提交:访问注册2接口

表单:

名称数据类型
namestring
passwordstring
验证码int

向验证码发送接口提交请求,携带json数据,并处理响应结果

如果成功展示注册成功;失败展示失败信息

3 登录:跳转页面到LoginPsw(登录页面)

alt

代码示例:

<template>
  <div>
    <h2>注册2</h2>
    <p>hallo:{{ email }}</p>
  </div>
  <div>
  <form @submit="submitForm">
      <label for="name">Name:</label>
      <input type="text" id="name" v-model="name" required>
      <br>
      <label for="password">Password:</label>
      <input type="password" id="password" v-model="password" required>
      <br>
      <label for="verification">Verification Code:</label>
      <input type="text" id="verification" v-model="verification" pattern="[0-9]{6}" required>
      <button @click="ReVerify">重发验证码</button>
      <br>
      <button type="submit">Submit</button>
    </form>
  </div>
  <div>
    <button @click="goToLogin">登录</button>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      name'',
      password'',
      email'',
      verification'',
    };
  },
  created() {
    this.email = this.$route.query.email || '';
  },
  methods: {
    ReVerify() {
        this.$router.push('/signup1');
      },
    goToLogin() {
        this.$router.push('/LogInPsw');
      },
    async submitForm(event) {
      event.preventDefault();
      if (!this.validateName(this.name)) {
        console.error('Name must be less than 20 characters');
        return;
      }
      if (!this.validatePassword(this.password)) {
        console.error('Password must be less than 20 characters');
        return;
      }
      if (!this.validateVerification(this.verification)) {
        console.error('Verification code must be 6 digits');
        return;
      }
      try {
        const requestData = {
          namethis.name,
          passwordthis.password,
          emailverificationcode: {
            emailthis.email,
            verificationparseInt(this.verification),
          },
        };
        const response = await axios.post('http://localhost:8888/signup2', requestData);
        if (response.data.code === 200) {
          console.log('Signup successful');
        } else {
          console.error(response.data.msg);
        }
      } catch (error) {
        console.error('Signup failed', error);
      }
    },
    validateName(name) {
      return name.length > 0 && name.length <= 20;
    },
    validatePassword(password) {
      return password.length > 0 && password.length <= 20;
    },
    validateVerification(verification) {
      return verification.length === 6 && /^\d+$/.test(verification);
    },
  },
};
</script>

密码登录loginpsw

4个按钮:

1 获取验证码图片:访问获取验证码图片接口,展示返回的图片并储存picid

2 忘记密码: 跳转到到loginemail1页面

3 提交:访问密码登录接口接口

表单:

名称数据类型
Emailstring
passwordstring
验证码int

向密码登录接口发送提交请求,携带json数据,并处理响应结果

如果成功储存返回的idcode(cookie)并跳转页面到home页面;失败展示失败信息

4 登录:跳转页面到signup1

alt

<template>
  <div>
      <h3>密码登录</h3>
    </div>
    <div>
      <button @click="getVerificationImage">获取验证图片</button>
      <div v-if="verificationImage">
        <img :src="decodeBase64(verificationImage.png)" alt="验证码图片">
      </div>
      <form @submit="submitForm">
        <label for="email">邮箱:</label>
        <input type="email" id="email" v-model="email" required>
        <br>
        <label for="password">密码:</label>
        <input type="password" id="password" v-model="password" required>
        <br>
        <label for="piccode">验证码:</label>
        <input type="text" id="piccode" v-model="piccode" pattern="[0-9]{4}" required>
        <br>
        <button type="submit">提交</button>
      </form>
    </div>
    <div>
    <button @click="goToLoginE">忘记密码</button>
  </div>
    <div>
    <button @click="goToSignup">注册</button>
   </div>
   
  </template>
  
  <script>
  import axios from 'axios';
  
  export default {
    data() {
      return {
        verificationImagenull,
        picid'',
        email'',
        password'',
        piccode''
      };
    },
    methods: {
      goToLoginE() {
        this.$router.push('/LogInEmail');
      },
      goToSignup() {
        this.$router.push('/signup1');
      },
      async getVerificationImage() {
        try {
          const response = await axios.get('http://localhost:8888/verifypic');
          if (response.data.code === 200) {
            this.verificationImage = response.data.data;
            this.picid = response.data.data.picid;
          }
        } catch (error) {
          console.error('获取验证图片失败', error);
        }
      },
      decodeBase64(base64Data) {
        return `data:image/png;base64, ${base64Data}`;
      },
      async submitForm(event) {
        event.preventDefault();
        if (!this.validateEmail(this.email)) {
          console.error('邮箱格式不正确');
          return;
        }
        if (!this.validatePassword(this.password)) {
          console.error('密码长度不能超过20个字符');
          return;
        }
        if (!this.validatePiccode(this.piccode)) {
          console.error('验证码必须为4位数字');
          return;
        }
        try {
          const requestData = {
            picidthis.picid,
            piccodethis.piccode.split('').map(Number),
            emailthis.email,
            passwordthis.password
          };
          const response = await axios.post('http://localhost:8888/loginpsw', requestData);
          if (response.data.code === 200) {
            // 登录成功后进行页面跳转到 HomeV.vue
            this.$cookies.set('idcode', response.data.data.idcode);
            this.$router.push('/HomeV');
          } else {
            console.error(response.data.msg);
          }
        } catch (error) {
          console.error('提交失败', error);
        }
      },
      validateEmail(email) {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
      },
      validatePassword(password) {
        return password.length <= 20;
      },
      validatePiccode(piccode) {
        return piccode.length === 4 && /^\d+$/.test(piccode);
      }
    }
  };
  
</script>
  

验证码登录

  1. loginemail1

4个按钮:

1 获取图片验证:访问图片验证码接口,展示base64验证图片并储存picid

2 提交:访问验证码登录1接口

用户填写表单:

名称数据类型
Emailstring
验证码[4]int

向验证码发送接口提交请求,携带json数据,并处理响应结果

如果成功跳转页面到loginemail2页面,失败则展示失败信息

3 注册:跳转页面到sianup1(注册页面)

4 使用密码登录:跳转到loginpsw(密码登录页面)

alt

<template>
    <div>
      <h3>验证码登录1</h3>
    </div>
    <div>
        <VerifyPic @verificationImage="handleVerificationImage"></VerifyPic>
      <form @submit="submitForm">
        <label for="piccode">验证码:</label>
        <input type="text" id="piccode" v-model="piccode" pattern="[0-9]{4}" required>
        <br>
        <label for="email">邮箱:</label>
        <input type="email" id="email" v-model="email" required>
        <br>
        <button type="submit">提交</button>
      </form>
    </div>
    <div>
    <button @click="goToSignup">注册</button>
  </div>
  <div>
    <button @click="goToLogin">使用密码登录</button>
  </div>
  </template>
  
  <script>
  import VerifyPic from './VerifyPic.vue';
  import axios from 'axios';
  
  export default {
    components: {
      VerifyPic
    },
    data() {
      return {
        picid'',
        piccode'',
        email''
      };
    },
    methods: {
      goToLogin() {
        this.$router.push('/LogInPsw');
      },
      goToSignup() {
        this.$router.push('/signup1');
      },
    handleVerificationImage(picid) {
      this.picid = picid;
    },
      async submitForm(event) {
        event.preventDefault();
        if (!this.validatePiccode(this.piccode)) {
          alert('验证码必须为4位数字');
          return;
        }
        if (!this.validateEmail(this.email)) {
          alert('邮箱格式不正确');
          return;
        }
        try {
          const requestData = {
            picidthis.picid,
            piccodethis.piccode.split('').map(Number),
            emailthis.email
          };
          const response = await axios.post('http://localhost:8888/loginemail1', requestData);
          if (response.data.code === 200) {
            // 登录成功后进行页面跳转到 LoginEmail2.vue
            this.$router.push({
        path'/LogInEmail2',
        query: { emailthis.email },
      });
          } else {
            alert(response.data.msg);
          }
        } catch (error) {
          alert('提交失败', error);
        }
      },
      validatePiccode(piccode) {
        return piccode.length === 4 && /^\d+$/.test(piccode);
      },
      validateEmail(email) {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
      }
    }
  };
  
</script>
  1. loginemail2

4个按钮:

1 重新发送验证码: 退回到loginemail1页面

2 提交:访问验证码登录2接口

表单:

名称数据类型
验证码int

向验证码发送接口提交请求,携带json数据,并处理响应结果

如果成功展示登录成功,储存idcode到cookie中并跳转页面到home;失败展示失败信息

3 注册:跳转页面到signup1(注册页面)

4 使用密码登录:跳转到loginpsw(密码登录页面)

alt
<template>
  <div>
      <h3>验证码登录2</h3>
    </div>
    <div>
      <form @submit="submitForm">
        <label for="verification">验证码:</label>
        <input type="number" id="verification" v-model.number="verification" max="999999" required>
        <button @click="goToLogin1">重发验证码</button>
        <br>
        <button type="submit">登录</button>
      </form>
    </div>
    <div>
    <button @click="goToSignup">注册</button>
  </div>
  <div>
    <button @click="goToLogin">使用密码登录</button>
  </div>
  </template>
  
  <script>
  import axios from 'axios';
  
  export default {
    data() {
      return {
        email'',
        verificationnull
      };
    },
    created() {
      this.email = this.$route.query.email || '';
    },
    methods: {
      goToLogin1() {
        this.$router.push('/LogInEmail');
      },
      goToLogin() {
        this.$router.push('/LogInPsw');
      },
      goToSignup() {
        this.$router.push('/signup1');
      },
      async submitForm(event) {
        event.preventDefault();
        if (!this.validateVerification(this.verification)) {
          alert('验证码必须为小于6位的数字');
          return;
        }
        try {
          const requestData = {
            emailthis.email,
            verificationthis.verification
          };
          const response = await axios.post('http://localhost:8888/loginemail2', requestData);
          if (response.data.code === 200) {
            // 登录成功后进行页面跳转到 HomeV.vue
            this.$cookies.set('idcode', response.data.data.idcode);
            this.$router.push('/HomeV');
          } else {
            alert(response.data.msg);
          }
        } catch (error) {
          alert('提交失败', error);
        }
      },
      validateVerification(verification) {
        return verification !== null && verification.toString().length <= 6;
      }
    }
  };
  
</script>

home页面

/home

  • 定义三个按钮 sign out(登出),reset password(重置密码),view(查看操作记录)

sign out(登出):访问登出接口,提交json数据,退回到loginpsw(登录页面)

查询:访问查询功能接口,并处理响应数据,如果成功展示操作信息,如果失败,展示失败信息

alt

<template>
 <div>
   <h1>Welcome to HomeV</h1>
   <button @click="confirmLogout">登出</button>
   <button @click="changePassword">改密</button>
   <button @click="getOperations">查询</button>
 </div>
</template>

<script>
import axios from 'axios';

export default {
 methods: {
   async confirmLogout() {
     const confirmLogout = confirm('是否确认登出?');
     if (confirmLogout) {
       try {
         const idcode = this.$cookies.get('idcode');
         const requestData = { idcode };
         const response = await axios.post('http://localhost:8888/signout', requestData);
         if (response.data.code === 200) {
           this.$router.push('/');
         }else if (response.data.code === 420) {
           alert(response.data.msg);
           this.$router.push('/');
         } else {
           alert(response.data.msg);
         }
       } catch (error) {
         console.error('登出失败', error);
         alert('操作失败');
         this.$router.push('/');
       }
     }
   },
   changePassword() {
     this.$router.push('/RePasswordPsw');
   },
   async getOperations() {
     try {
       const idcode = this.$cookies.get('idcode');
       const requestData = { idcode };
       const response = await axios.post('http://localhost:8888/getoperations', requestData);
       if (response.data.code === 200) {
         const operations = response.data.data.operations;
         // 在此处理返回的操作数据,可以存储到组件的数据中并进行展示
         console.log(operations);
       }else if (response.data.code === 420) {
           alert(response.data.msg);
           this.$router.push('/');
         } else {
         alert(response.data.msg);
       }
     } catch (error) {
       console.error('查询操作失败', error);
       if (error.response && error.response.status === 420) {
         alert('非法访问');
         this.$router.push('/');
       }
     }
   }
 }
};
</script>

repasswordpsw 旧密码密码改密

三个按钮:

1 忘记密码:跳转到repasswordemail页面(验证码改密)

2 返回:退回到home页面

3 改密:

旧密码改密表单:

名称数据类型
passwordstring
new passwordstring
new password againstring
验证码[4]int

(2). 用户填写表单信息,点击提交.

(3). 调用密码验证功接口 提交json数据,处理响应数据

如果成功展示"改密成功",否则展示错误信息

alt

<template>
    <div>
      <h1>密码改密</h1>
      <form @submit="changePassword">
        <label for="password">当前密码:</label>
        <input type="password" id="password" v-model="password" required>
        <br>
        <label for="newpassword">新密码:</label>
        <input type="password" id="newpassword" v-model="newPassword" required>
        <br>
        <button type="submit">改密</button>
      </form>
      <button @click="goBack">返回</button>
      <button @click="goToForgotPassword">忘记密码</button>
    </div>
  </template>
  
  <script>
  import axios from 'axios';
  
  export default {
    data() {
      return {
        password'',
        newPassword''
      };
    },
    methods: {
      goBack() {
        this.$router.push('/HomeV');
      },
      goToForgotPassword() {
        this.$router.push('/RePasswordEmail1');
      },
      async changePassword(event) {
        event.preventDefault(); // 阻止表单提交的默认行为
  
        try {
          const idcode = this.$cookies.get('idcode');
          const requestData = {
            passwordthis.password,
            newpasswordthis.newPassword,
            idcode: idcode
          };
          const response = await axios.post('http://localhost:8888/home/resetpaswbypasw', requestData);
          if (response.data.code === 200) {
            alert('改密成功');
          }else if (response.data.code===420) {
            alert(response.data.msg);
            this.$router.push('/');
          } else {
            alert(response.data.msg);
          }
        } catch (error) {
          console.error('改密失败', error);
          if (error.response && error.response.status === 420) {
          alert('非法访问');
          this.$router.push('/');
        }
        }
      }
    }
  };
  
</script>

repasswordemail 验证码改密1

3个按钮:

1 获取图片验证:访问图片验证码接口,展示base64验证图片并储存picid

2 提交:访问验证码改密1接口

表单:

名称数据类型
验证码[4]int

向验证码发送接口提交请求,携带json数据,并处理响应结果

如果成功跳转页面到repasswordemai2页面,失败则展示失败信息

3 使用密码改密:跳转页面到repasswordPsw(密码改密页面)

4 返回:退回到home页面

alt
<template>
    <div>
      <h1>验证码改密1</h1>
      <VerifyPic @verificationImage="handleVerificationImage"></VerifyPic>
      <form @submit="submitForm">
        <label for="piccode">验证码:</label>
        <input type="text" id="piccode" v-model="piccode" pattern="[0-9]{4}" required>
        <br>
        <button type="submit">提交</button>
      </form>
      <button @click="goBack">返回</button>
      <button @click="goToRePasswordPsw">使用密码改密</button>
    </div>
  </template>
  
  <script>
  import axios from 'axios';
  import VerifyPic from './VerifyPic.vue';

  
  export default {
    components: {
      VerifyPic
    },
    data() {
      return {
        piccode'',
        picid''
      };
    },
    methods: {
      goBack() {
        this.$router.push('/HomeV');
      },
      goToRePasswordPsw() {
        this.$router.push('/RePasswordPsw');
      },
      handleVerificationImage(picid) {
      this.picid = picid;
    },
    validatePiccode(piccode) {
        return piccode.length === 4 && /^\d+$/.test(piccode);
      },
      async submitForm(event) {
        event.preventDefault(); // 阻止表单提交的默认行为
        if (!this.validatePiccode(this.piccode)) {
          alert('验证码必须为4位数字');
          return;
        }
        try {
          const idcode = this.$cookies.get('idcode');
          const requestData = {
            picidthis.picid, // 请替换为您自己的数据
            piccodethis.piccode.split('').map(Number),
            idcode: idcode
          };
          const response = await axios.post('http://localhost:8888/home/resetpaswbyemail1', requestData);
          if (response.data.code === 200) {
            this.$router.push('/RePasswordEmail2');
          }else if (response.data.code===420) {
            alert(response.data.msg);
            this.$router.push('/');
          } else {
            alert(response.data.msg);
          }
        } catch (error) {
          console.error('提交失败', error);
          if (error.response && error.response.status === 420) {
          alert('非法访问');
          this.$router.push('/');
        }
        }
      }
    }
  };
  
</script>
  1. 验证码改密2 repasswordemail2

4个按钮:

1 重新发送验证码: 退回到repasswordemail1页面

2 提交:访问验证码验证码改密2接口

表单:

名称数据类型
newpasswordstring
验证码int

向验证码发送接口提交请求,携带json数据,并处理响应结果

如果成功提示改密成功;失败展示失败信息

3 返回home:跳转页面到home(注册页面)

4 使用密码改密:跳转到repasswordpsw(密码改密页面)

alt
<template>
    <div>
      <h1>验证码改密2</h1>
      <form @submit="submitForm">
        <label for="newpassword">新密码:</label>
        <input type="password" id="newpassword" v-model="newPassword" required>
        <br>
        <label for="verification">验证码:</label>
        <input type="text" id="verification" v-model="verification" required>
      <button @click="ReVerify">重发验证码</button>
        <br>
        <button type="submit">提交</button>
      </form>
      <button @click="goBack">返回</button>
      <button @click="goToRePasswordPsw">使用密码改密</button>
    </div>
  </template>
  
  <script>
  import axios from 'axios';
  
  export default {
    data() {
      return {
        newPassword'',
        verification''
      };
    },
    methods: {
      ReVerify() {
        this.$router.push('/RePasswordEmail1');
      },
      goBack() {
        this.$router.push('/HomeV');
      },
      goToRePasswordPsw() {
        this.$router.push('/RePasswordPsw');
      },
      async submitForm(event) {
        event.preventDefault(); // 阻止表单提交的默认行为
  
        try {
          const idcode = this.$cookies.get('idcode');
          const requestData = {
            newpasswordthis.newPassword,
            idcode: idcode,
            verificationparseInt(this.verification)
          };
          const response = await axios.post('http://localhost:8888/home/resetpaswbyemail2', requestData);
          if (response.data.code === 200) {
            alert('改密成功');
          }else if (response.data.code===420) {
            alert(response.data.msg);
            this.$router.push('/');
          } else {
            alert(response.data.msg);
          }
        } catch (error) {
          console.error('提交失败', error);
          if (error.response && error.response.status === 420) {
          alert('非法访问');
          this.$router.push('/');
        }
        }
      }
    }
  };
  
</script>

本文由 mdnice 多平台发布

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

容器化:MySQL

1 缘起 开启容器化之路。 2 容器化MySQL 2.1 查看MySQL镜像 docker search mysql2.2 指定版本&#xff1a;5.7.30 通过官网查看&#xff1a;https://hub.docker.com/ docker pull mysql:5.7.302.3 路径挂载 容器路径挂载到宿主机。 新建宿主机路径 mkdir -p /home/xind…

ElasticSearch-索引和文档的创建修改删除

目录 一、创建索引 二、查看索引 三、索引是否存在 四、删除索引 五、创建文档 六、查看文档 七、更新文档 八、文档是否存在 九、删除文档 一、创建索引 # 创建一个默认的索引&#xff0c;默认是标准分词器的索引 PUT /es_db2# 创建一个默认为ik分词器的索引 PUT /e…

十万条数据,后端不分页咋办!(如何优化长列表渲染)

十万条数据&#xff0c;后端不分页咋办&#xff01;&#xff08;如何优化长列表渲染&#xff09; 长列表是什么&#xff1f; 我们通常把一组数量级很大的数据叫做长列表&#xff0c;比如渲染一组上千条的数据&#xff0c;我们以数组的形式拿到这些信息&#xff0c;然后遍历渲…

Rust快速安装

Rust依赖C编译&#xff0c;Rust官方推荐的安装方式是利用VisualSudio安装C环境&#xff0c;VisualStuidio用过的都懂&#xff0c;庞大无比、卡顿、下载还贼慢(我当时装了一上午好像)&#xff0c;因此我们通过其它方式配置C 安装C环境 下载MinGW 64 win32 seh Mingw官网&…

Java的并发集合框架

文章目录 一、并发集合框架1. 简介2. 接口Iterable2. 接口Collection3. 接口List4. 接口Set5. 接口Queue6. Deque 二、非阻塞队列1. 概念2. ConcurrentHashMap使用&#xff08;1&#xff09;简介&#xff08;2&#xff09;验证HashMap不是线程安全的&#xff08;3&#xff09;验…

ld文件中指定变量在flash中的地址定义

本文说的是在gcc环境中&#xff0c;Keil或IAR可能有自己的使用方法。 我们在定义变量时&#xff0c;有时候需要把变量定义放到flash中的固定位置或区域&#xff0c;此时需要修改工程中的链接文件&#xff08;link file&#xff0c;ld文件&#xff09;。 方法一 修改ld文件中的…

软件IIC通信以及源码解析(如何使用)

以对读取MPU6050为例&#xff0c;解析如何采用IIC通信源码。 IIC的的通信&#xff0c;通常三种用途读写。分为&#xff1a; 1&#xff1a; 2&#xff1a; 3&#xff1a; 注&#xff1a;其中最常用的就是1和3了。 对1进行讲解&#xff1a; 指定地址写&#xff0c;通常用作对状…

【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - IP地址

目录 一、概述 二、IP地址的定义 三、IP地址由网络和主机两部分标识组成 一、概述 上篇文章简单介绍了网际协议IP。网际协议 IP 大致分为三大作用模块&#xff0c; ①IP寻址、 ②路由&#xff08;最终节点为止的转发&#xff09; 、③IP分包与组包。 这篇文章主要详细介绍IP地址…

【HTTP协议详解】

目录 1.什么是http2.抓包工具2.1 抓包工具2.2 抓包原理 3.Http协议格式3.1Http请求报文3.2Http响应报文的格式 4.请求报文格式4.1 报文首行4.2 请求报文header 5. 响应报文格式6.构造Http请求7.Https协议7.1 对称密钥7.2 非对称密钥7.3 证书 1.什么是http HTTP全称为“超文本协…

《MySQL是怎么运行的》阅读分享

mysql运行的整体架构简介 Mysql是由两部分构成&#xff0c;一部分是服务器程序&#xff0c;一部分是客户端程序。 服务器程序又包括两部分&#xff1a; 第一部分server层包括连接器、查询缓存、分析器、优化器、执行器等。涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有…

LeetCode:738.单调递增的数字 714.买卖股票的最佳时机含手续费 968.监控二叉树

738.单调递增的数字 题目 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 贪心 class Solution {public int monotoneIncrea…

数据结构——单链表(C语言)

在这⼀条⼗分漫长的路上&#xff0c;我⾛过阳关⼤道&#xff0c;也⾛过独⽊⼩桥。路旁有深⼭⼤泽&#xff0c;也有平坡宜⼈&#xff1b;有杏花春⾬&#xff0c;也有塞北秋风&#xff1b;有⼭重⽔复&#xff0c;也有柳暗花明&#xff1b;有迷途知返&#xff0c;也有绝处逢⽣。—…

[论文分享] Function Representations for Binary Similarity

Function Representations for Binary Similarity [TDSC 2022] Luca Massarelli , Giuseppe Antonio Di Luna, Fabio Petroni, Leonardo Querzoni, and Roberto Baldoni 二进制相似度问题在于仅考虑两个函数的编译形式来判断它们是否相似。近年来&#xff0c;计算二进制相似度…

【2023 · CANN训练营第一季】进阶班 Atlas 200I DK 智能小车

1 智能小车三维结构设计 1.1 基本模块 坚固酷炫结构模块运动控制模块超声波传感器模块摄像头视觉模块其他传感器模块 1.2 结构设计基本原则 从零开始设计并搭建智能小车&#xff0c;在满足外观要求的基础上&#xff0c;要满足小车运转过程中的运动干涉率为O&#xff0c;并且…

玩转ChatGPT:Show Me插件尝鲜

一、写在前面 之前&#xff0c;不少人问我GPT能否画技术路线图&#xff0c;然后看到了这个插件&#xff1a;Show Me&#xff1a; 简单问GPT这个插件的使用方法&#xff1a; 二、尝鲜过程 &#xff08;1&#xff09;用TA提供的例子试一试&#xff1a; 咒语&#xff1a;请用图…

009、实例连接访问控制

实例连接访问控制 1、实例连接访问控制概述2、pg_hba.conf文件3、名单格式4、pg_hba.conf 示例:5、当有重复或者冲突的时候1、实例连接访问控制概述 • 实例访问控制就像是一道防火墙,用它来控制来自于不同主机、不同用户是否 允许访问指定的数据库、以及验证方式。 2、pg…

【Tcp通信服务器流程】

TCP通信流程 1、服务器端&#xff08;被动接收连接的角色&#xff09; &#xff08;1&#xff09;创建一个用于监听的套接字 - 监听&#xff1a;监听有客户端的连接 - 套接字&#xff1a;这个套接字其实就是一个文件描述符 &#xff08;2&#xff09;将这个监听文件描述符和…

TextCNN文本分类

本文主要介绍TextCNN文本分类&#xff0c;主要从TextCNN的原理的Pytorch实现来逐步讲解。主要思想来自论文《Convolutional Neural Networks for Sentence Classification&#xff08;EMNLP2014&#xff09; 论文连接&#xff1a;[1408.5882] Convolutional Neural Networks f…

设计模式系列/ 职责链模式

必读 本篇主要分析设计模式之 职责链模式。 什么人适合学习本章节呢。 从未接触过设计模式的同学有n年工作经验 && 对职责链模式不了解的同学 1. 职责链模式意识形态 设计模式充斥着我们开发过程中的方方面面&#xff0c;责任链模式也是如此。也许你用到了设计模式&…

ElasticSearch-文档查询

目录 一、查询某个索引下的全部文档 二、根据查询条件查询文档 三、分页查询 四、查询指定返回的字段 五、排序 六、多条件查询 七、范围查询 八、全文检索与完全匹 一、查询某个索引下的全部文档 语法&#xff1a; GET /索引名称/_search {"query": {&quo…