Go+Vue开发一个线上外卖应用的流程(用户名密码和图形验证码)
作者:you-men 发布时间:2024-05-09 14:52:17
图形化验证码生成和验证
功能介绍
在使用用户名和密码登录功能时,需要填写验证码,验证码是以图形化的方式进行获取和展示的。
验证码使用原理
验证码的使用流程和原理为:在服务器端负责生成图形化验证码,并以数据流的形式供前端访问获取,同时将生成的验证码存储到全局的缓存中,在本案例中,我们使用redis作为全局缓存,并设置缓存失效时间。当用户使用用户名和密码进行登录时,进行验证码验证。验证通过即可继续进行登录。
验证码库安装
借助开源的验证码工具库可以生成验证码。
首先,安装开源的验证码生成库:
go get -u github.com/mojocn/base64Captcha
go get github.com/mojocn/base64Captcha@v1.2.2
验证码代码示例
在下载后的base64Captcha库的目录中,可以看到有_example和_example_redis两个目录。第一个example是用于演示生成验证码和验证码的示例代码。
按照示例代码的说明,运行程序并在浏览器进行端口访问:
go run main.go
//浏览器中访问:http://localhost:8777
如下图所示:
通过自定义配置,可以选择不同的生成验证码的参数,并刷新验证码,同时还可以对验证码进行验证。
通过exmaple目录下的main.go程序可以看到生成验证码和验证验证码的逻辑,此处不再赘述。
项目集成验证码生成和Redis缓存
通常来说,验证码都是有一定的实效性的,过期验证码也就无效了。
因此,我们考虑在项目中引入Redis作为数据缓存。当验证码生成后,将验证码存放在Redis中,并根据配置文件对Redis进行设置。
安装go-redis库
在项目中使用redis,需要安装go-redis库,可以在https://github.com/go-redis/redis中查看如何下载go-redis和配置。
增加Redis配置
在配置文件app.json中新增redis配置:
"redis_config": {
"addr": "127.0.0.1",
"port": "6379",
"password": "",
"db": 0
}
同时,新增RedisConfig结构体定义,如下所示:
type RedisConfig struct {
Addr string `json:"addr"`
Port string `json:"port"`
Password string `json:"password"`
Db int `json:"db"`
}
Redis初始化操
进行了redis配置以后,需要对redis进行初始化。可以封装redis初始化操作函数如下所示:
type RedisStore struct {
redisClient *redis.Client
}
var Redis *redis.Client
func InitRediStore() *RedisStore {
config := GetConfig().RedistConfig
Redis = redis.NewClient(&redis.Options{
Addr: config.Addr + ":" + config.Port,
Password: config.Password,
DB: config.Db,
})
customeStore := &RedisStore{Redis}
base64Captcha.SetCustomStore(customeStore)
return customeStore
}
同时,为customeStore提供Set和Get两个方法,如下所示:
func (cs *RedisStore) Set(id string, value string) {
err := cs.redisClient.Set(id, value, time.Minute*2).Err()
if err != nil {
log.Println(err.Error())
}
}
func (cs *RedisStore) Get(id string, clear bool) string {
val, err := cs.redisClient.Get(id).Result()
if err != nil {
toolbox.Error(err.Error())
return ""
}
if clear {
err := cs.redisClient.Del(id).Err()
if err != nil {
toolbox.Error(err.Error())
return ""
}
}
return val
}
对Redis进行初始化和定义完成以后,需要在main中调用一下初始化操作InitRediStore:
func main(){
...
//Redis配置初始化
toolbox.InitRediStore()
...
}
验证码生成和验证
本项目中采用的验证码的生成库支持三种验证码,分别是:audio,character和digit。我们选择character类型。
定义Captcha.go文件,实现验证码的生成和验证码函数的定义。在进行验证码生成时,默认提供验证码的配置,并生成验证码后返回给客户端浏览器。如下是生成验证码的函数定义:
//生成验证码
func GenerateCaptchaHandler(ctx *gin.Context) {
//图形验证码的默认配置
parameters := base64Captcha.ConfigCharacter{
Height: 60,
Width: 240,
Mode: 3,
ComplexOfNoiseText: 0,
ComplexOfNoiseDot: 0,
IsUseSimpleFont: true,
IsShowHollowLine: false,
IsShowNoiseDot: false,
IsShowNoiseText: false,
IsShowSlimeLine: false,
IsShowSineLine: false,
CaptchaLen: 4,
BgColor: &color.RGBA{
R: 3,
G: 102,
B: 214,
A: 254,
},
}
captchaId, captcaInterfaceInstance := base64Captcha.GenerateCaptcha("", parameters)
base64blob := base64Captcha.CaptchaWriteToBase64Encoding(captcaInterfaceInstance)
captchaResult := CaptchaResult{Id: captchaId, Base64Blob: base64blob}
// 设置json响应
tool.Success(ctx, map[string]interface{}{
"captcha_result": captchaResult,
})
}
验证码接口解析
图形化验证码是用户名和密码登录功能的数据,属于Member模块。因此在MemberController中增加获取验证码的接口解析,如下:
func (mc *MemberController) Router(engine *gin.Engine){
//获取验证码
engine.GET("/api/captcha", mc.captcha)
}
测试结果如下,能够正常获取到数据:
验证码的验证
同理,可以对客户端提交的验证码进行验证,具体实现逻辑如下:
//验证验证码是否正确
func CaptchaVerify(r *http.Request) bool {
var captchaResult CaptchaResult
//接收客户端发送来的请求参数
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&captchaResult)
if err != nil {
log.Println(err)
}
defer r.Body.Close()
//比较图像验证码
verifyResult := base64Captcha.VerifyCaptcha(captchaResult.Id, captchaResult.VertifyValue)
return verifyResult
}
用户名密码登录功能开发
功能介绍
上节课已经完成了验证码的生成,本节课来开发用户名、密码和验证码登录功能。
接口和参数解析定义
用户名和密码的登录接口为:
/api/login_pwd
接口请求类型为POST,接口参数有三个:name,pwd,captcha。其中:captcha为验证码。
定义登录参数结构体LoginParam:
//用户名,密码和验证码登录
type LoginParam struct {
Name string `json:"name"` //用户名
Password string `json:"pwd"` //密码
Id string `json:"id"`// captchaId 验证码ID
Value string `json:"value"` //验证码
}
逻辑控制层实现登录流程控制
方法解析
在MemberController.go文件中,编写方法用于处理用户名密码登录的解析方法如下所示:
func (mc *MemberController) Router(engine *gin.Engine){
engine.POST("/api/login_pwd", mc.nameLogin)
}
登录流程编程实现
定义新的func并命名为nameLogin,实现登录流程逻辑控制:
//用户名、密码登录
func (mc *MemberController) nameLogin(context *gin.Context) {
//1、登录参数解析
var loginParam param.LoginParam
err := toolbox.Decode(context.Request.Body, &loginParam)
if err != nil {
toolbox.Failed(context, "参数解析失败")
return
}
//2、验证验证码
service := impl.NewMemberService()
validate := toolbox.CaptchaVerify(loginParam.Id, loginParam.Value)
fmt.Println(validate)
if !validate {
toolbox.ValidateFailed(context, "验证码不正确, 请重新验证 ")
return
}
//3、登录
member := service.Login(loginParam.Name, loginParam.Password)
if member.Id == 0 {
toolbox.Failed(context, "登录失败")
return
}
toolbox.Success(context, &member)
}
在控制层的nameLogin方法中,主要有3个逻辑处理:
1、通过*gin.Context解析请求登录请求携带的参数。
2、从携带的参数中得到提交的验证码数据,调用验证码判断验证码方法对验证码进行判断。验证码验证失败或者验证码失效,直接返回登录失败信息。
3、使用用户名、密码参数进行登录,判断登录结果。如果登录成功,返回用户登录信息,否则返回登录失败。
Service层实现
在功能服务层的MemberService文件中,定义和实现用户名密码登录的Login方法。详细实现如下:
//用户登录: 如果没有登录过,自动进行登录
func (msi *MemberServiceImpl) Login(name string, password string) *model.Member {
dao := impl.NewMemberDao()
//1、先查询是否已经存在该用户
member := dao.Query(name, password)
if member.Id != 0 {
return member
}
user := model.Member{}
user.UserName = name
user.Password = toolbox.EncoderSha256(password)
user.RegisterTime = time.Now().Unix()
result := dao.InsertMember(user)
user.Id = result
return &user
}
在service层的Login方法中,分为两步逻辑判断:
1、先查询是否已经存在该用户,如果该用于已经存在,则直接将查询到的用户信息返回。
2、如果用户不存在,将用户信息作为新记录保存到数据库中,新增一条记录。并返回用户信息。
最后,涉及到操作数据库的两个方法分别是:Query和InsertMember方法。InsertMember方法之前已经编写过,只需要重新编写一个Query方法即可,Query方法实现如下所示:
//根据用户名和密码查询用户记录
func (mdi *MemberDaoImpl) Query(name string, password string) *model.Member {
var member model.Member
password = toolbox.EncoderSha256(password)
_, err := mdi.Where(" user_name = ? and password = ? ", name, password).Get(&member)
if err != nil {
toolbox.Error(err.Error())
return nil
}
return &member
}
来源:https://www.cnblogs.com/you-men/p/13918316.html


猜你喜欢
- Js代码:vartempForm=document.createElement("form"); tempF
- 前言Python 的一大优点就是丰富的类库,所以我们经常会用 pip 来安装各种库,所以对于Python开发用户来讲,PIP安装软件包是家常
- 先解释一下这篇Blog延期的原因,本来已经准备好了全部内容,但是当我重新回顾实例三的时候,发现自己还是存在认知不足的地方,于是为了准确表述,
- 简介此样式基于bootstrap-3.3.0,样式文件里的源码行数都是指的这个版本.CSS源文件放到了Content文件夹下的bootstr
- 背景前段时间写了一个自动化安装 MySQL 的程序,其中有一个环节就是动态的渲染 my.cnf 文件;总的解决方案就是像 Django 渲染
- 情景一: var yx01 = new function() {return "圆心"}; alert(yx01);我们
- DVWA(Damn Vulnerable Web Application)是一个用来进行安全脆弱性鉴定的PHP/MySQL Web应用,旨在
- 功能点显示日期时间显示树莓派当前局域网IP显示当前UP主粉丝数显示B站未读消息显示B站视频总计播放数显示视频总计获赞数显示总计获得充电次数显
- 关于《回访确认》这件事情,在我blog和5Gme的回复里普遍出现这么几个疑问:1、下了订单却放弃支付的心理因素是什么?2、回访电话可以促进订
- Numpy中提供了concatenate,append, stack类(包括hsatck、vstack、dstack、row_stack、c
- 这篇文章讨论了Python的from <module> import *和from <package> import
- 上一篇 我们学习了简单的Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程
- 数据库连接池概念:其实就是一个容器(集合),存放数据库连接的容器。概念:其实就是一个容器(集合),存放数据库连接的容器。 &n
- 这两天项目里出了一个问题,LIMIT使用后报错。 需求是这样的,我有3张表,infor信息表,mconfig物料配置表,maaply物料申请
- 一、触发器概念触发器是SQL server提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,是由事件来触
- 相信各位phper在日常开发中,会经常遇到需要合并数组的场景。那么,在php中都有哪一些方法可以用来合并数组呢。第一种,使用&ldq
- 前言:文章利用Python pygame做一个贪吃蛇的小游戏而且讲清楚每一段代码是用来干嘛的。据说是贪吃蛇游戏是1976年,Gremlin公
- 字符串索引示意图字符串切片也就是截取字符串,取子串Python中字符串切片方法字符串[开始索引:结束索引:步长]切取字符串为开始索引到结束索
- 本文实例讲述了Python3.5集合及其常见运算。分享给大家供大家参考,具体如下:1、集合的定义:集合是一个无序的、无重复的数据的数据组合。
- Python可以使用 xml.etree.ElementTree 模块从简单的XML文档中提取数据。 为了演示,假设你想解析Planet P