彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-登录与图形验证码(captcha)EP06

彩虹,女神,长空,go,语言,进阶,高性能,web,框架,iris,项目,实战,登录,图形,验证码,captcha,ep06 · 浏览次数 : 175

小编点评

**程序结构:** * `main` 函数: * 创建表单数据。 * 发送 POST 请求到 `captcha/` 路由。 * 读取响应并打印内容。 * `GetCaptchaImg` 函数: * 使用 `iris.Context` 获取图像大小。 * 启动 `captcha.Server` 服务,将图像渲染到页面上。 * `user.go` 函数: * 从表单中获取用户输入的 `cid` 和 `code`。 * 验证用户输入的 `cid` 和 `code` 是否正确。 * 如果正确,将验证结果返回给前端。 **路由配置:** `app.Get("/captcha/*/\", mytool.GetCaptchaImg)`: * 匹配任何以 `captcha/` 开头的路径。 * 调用 `GetCaptchaImg` 函数获取图片。 **前端逻辑:** * 用户登录时,生成 `cid` 并将其绑定到图片标签中。 * 用户在表单中输入密码后,将 `cid` 和 `code` 发送到服务器。 * 服务器验证用户输入的 `cid` 和 `code`,并返回验证结果。 **完整代码:** ```go package main import ( "bytes" "fmt" "io/ioutil" "net/http" "net/url" ) func main() { formValues := url.Values{} formValues.Set("username", "123") formValues.Set("password", "1243") formDataStr := formValues.Encode() formDataBytes := []byte(formDataStr) resp, err := http.Post("http://localhost:5000/captcha/\", "application/x-www-form-urlencoded;charset=utf-8", formBytesReader) if err != nil { fmt.Println(err) return } body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Println(string(body)) } ```

正文

书接上回,上一回我们按照“低耦合高内聚”的组织架构方针对项目的整体结构进行了优化,本回将会继续编写业务,那就是用户的登录逻辑,将之前用户管理模块中添加的用户账号进行账号和密码的校验,校验通过后留存当前登录用户的信息,过程中使用图形验证码强制进行人机交互,防止账号的密码被暴力破解。

登录逻辑

首先在逻辑层handler包中,创建用户模块文件user.go:

package handler  
  
import (  
  
  
	"github.com/kataras/iris/v12"  
)  
  
//用户登录模板  
func User_signin(ctx iris.Context) {  
  
	ctx.View("/signin.html")  
  
}

这里通过上下文管理器结构体ctx渲染用户登录模板。

随后,在views模板目录中,添加用户登录模板signin.html:

<div class="row justify-content-center">  
        
        <div class="col-md-10 col-lg-8 article">  
              
            <div class="article-body page-body mx-auto" style="max-width: 400px;">  
                  
                <h1 class="text-center mb-4">登 录</h1>  
                  
                <div class="socialaccount_ballot">  
                    <div class="text-center mb-3">  
                        <ul class="list-unstyled">  
                              
                            <li><a title="GitHub" class="socialaccount_provider github btn btn-secondary btn-lg w-100" href="JavaScript:void(0)">Connect With <strong>Meta Mask</strong></a></li>  
                          
                        </ul>  
                    </div>  
                      
                    <div class="text-center text-muted my-3">— or —</div>  
                </div>  
                      
                    <div class="form-group">  
                          
                        <div id="div_id_login" class="form-group">  
                              
                            <label for="id_login" class="requiredField"> Username<span class="asteriskField">*</span></label>  
                            <div class=""><input type="text" placeholder="请输入用户名" v-model="username" autofocus="" class="textinput textInput form-control"></div>  
                        </div>  
                    </div>  
                              
                            <div class="form-group">  
                                <div id="div_id_password" class="form-group">  
                                    <label for="id_password" class="requiredField"> Password<span class="asteriskField">*</span></label>  
                                    <div class="">  
                                        <input type="password" placeholder="请输入密码" v-model="password"  minlength="8" maxlength="99" class="textinput textInput form-control">  
                                    </div>  
                                </div>  
                            </div>  
                              
                              
                            <div class="text-center"><button class="btn btn-primary btn-lg text-wrap px-5 mt-2 w-100" name="jsSubmitButton">点击登录</button></div></div>  
                  
                </div>  
  
    </div>

这里的表单中有两个字段,分别是Username和Password。

随后通过Vue.js进行数据双向绑定逻辑:

const App = {  
            data() {  
                return {  
                    username: "",  
                    password: "",  
                };  
            },  
            created: function() {  
  
  
            },  
            methods: {  
            },  
        };  
const app = Vue.createApp(App);  
app.config.globalProperties.axios = axios;  
app.mount("#app");

随后添加登录模板路由逻辑:

app.Get("/signin/", handler.User_signin)

访问 http://localhost:5000/signin/ 如图所示:

随后编写登录后台业务user.go:

//登录动作  
func Signin(ctx iris.Context) {  
  
	db := database.Db()  
	defer func() {  
		_ = db.Close()  
	}()  
  
	Username := ctx.PostValue("username")  
	Password := ctx.PostValue("password")  
  
	user := &model.User{}  
  
	db.Where(&model.User{Username: Username, Password: mytool.Make_password((Password))}).First(&user)  
  
	ret := make(map[string]interface{}, 0)  
  
	if user.ID == 0 {  
  
		ret["errcode"] = 1  
		ret["msg"] = "登录失败,账号或者密码错误"  
		ctx.JSON(ret)  
		return  
  
	}  
	ret["errcode"] = 0  
	ret["msg"] = "登录成功"  
	ret["username"] = user.Username  
	ctx.JSON(ret)  
  
}

这里通过db.Where函数进行用户名和密码的检索,注意密码需要通过mytool.Make_password函数转换为密文。

随后通过判断主键ID的值来判定账号的合法性,这里注意返回值字典的值通过接口(interface)声明初始化,如此字典的value就可以兼容不同的数据类型。

在和前端联调之前,编写测试脚本tests.go:

package main  
  
import (  
	"bytes"  
	"fmt"  
	"io/ioutil"  
	"net/http"  
	"net/url"  
)  
  
func main() {  
  
	formValues := url.Values{}  
	formValues.Set("username", "123")  
	formValues.Set("password", "123")  
	formDataStr := formValues.Encode()  
	formDataBytes := []byte(formDataStr)  
	formBytesReader := bytes.NewReader(formDataBytes)  
  
	resp, err := http.Post("http://localhost:5000/signin/", "application/x-www-form-urlencoded;charset=utf-8", formBytesReader)  
	if err != nil {  
		fmt.Println(err)  
		return  
	}  
	body, err := ioutil.ReadAll(resp.Body)  
  
	fmt.Println(string(body))  
  
}

这里模拟表单数据,向后端Iris发起Post请求,程序返回:

{"errcode":0,"msg":"登录成功","username":"123"}

登录成功后,返回当前登录用户账号。

反之:

{"errcode":1,"msg":"登录失败,账号或者密码错误"}

返回错误码以及提示信息。

接着前端编写异步请求逻辑:

//登录请求  
                signin:function(){  
  
                    this.myaxios("http://localhost:5000/signin/","post",{"username":this.username,"password":this.password}).then(data => {  
                        console.log(data)  
                        alert(data.msg);  
                    });  
  
                }

如图所示:

至此,登录逻辑就完成了。

图像验证码

图形验证码的主要作用是强制当前用户进行人机交互,藉此来抵御人工智能的自动化攻击,可以防止恶意破解密码、刷票、论坛灌水,有效防止黑客对某一个特定注册用户用特定程序暴力破解进行不断的登录尝试。

首先在项目内安装三方的验证码校验包:

go get -u github.com/dchest/captcha

随后在工具类中添加验证码生成逻辑mytool.go:

package mytool  
  
import (  
	"crypto/md5"  
	"fmt"  
	"io"  
  
	"github.com/dchest/captcha"  
	"github.com/kataras/iris/v12"  
)  
  
const (  
	StdWidth  = 80  
	StdHeight = 40  
)  
  
func GetCaptchaId(ctx iris.Context) {  
	m := make(map[string]interface{}, 0)  
	m["errcode"] = 0  
	m["msg"] = "获取成功"  
	m["captchaId"] = captcha.NewLen(4)  
	ctx.JSON(m)  
	return  
}

这里定义常量StdWidth和StdHeight,意为图片宽和高,然后通过captcha.NewLen(4)函数获取验证码的标识,这里NewLen(4)标识生成四位验证码,如果不需要定制化操作,也可以使用captcha.New()返回默认长度的验证码。

接着添加路由:

app.Post("/captcha/", mytool.GetCaptchaId)

继续使用tests.go脚本进行测试:

package main  
  
import (  
	"bytes"  
	"fmt"  
	"io/ioutil"  
	"net/http"  
	"net/url"  
)  
  
func main() {  
  
	formValues := url.Values{}  
	formValues.Set("username", "123")  
	formValues.Set("password", "1243")  
	formDataStr := formValues.Encode()  
	formDataBytes := []byte(formDataStr)  
	formBytesReader := bytes.NewReader(formDataBytes)  
  
	resp, err := http.Post("http://localhost:5000/captcha/", "application/x-www-form-urlencoded;charset=utf-8", formBytesReader)  
	if err != nil {  
		fmt.Println(err)  
		return  
	}  
	body, err := ioutil.ReadAll(resp.Body)  
  
	fmt.Println(string(body))  
  
}

程序返回:

{"captchaId":"qGlf8P9RJtJJzc3Q1v4z","errcode":0,"msg":"获取成功"}

这里就获取到captchaId的值为qGlf8P9RJtJJzc3Q1v4z,随后编写逻辑,使用captchaId渲染具体的图片缓冲区:

func GetCaptchaImg(ctx iris.Context) {  
	captcha.Server(StdWidth, StdHeight).  
		ServeHTTP(ctx.ResponseWriter(), ctx.Request())  
}

这里通过captcha.Server将图片渲染出来,配置路由:

app.Get("/captcha/*/", mytool.GetCaptchaImg)

注意必须通过Get方式进行请求,因为需要浏览器对图片进行访问,如图所示:

随后,编写前端逻辑,首先登录页面初始化时,生成验证码id:

//获取验证码标识  
                get_cid:function(){  
  
                    this.myaxios("http://localhost:5000/captcha/","post").then(data => {  
                        console.log(data)  
                        this.cid = data.captchaId;  
                          
  
                    });  
  
                }

随后添加验证码字段,将验证码展示在表单中:

<div class="form-group">  
                                <div id="div_id_password" class="form-group">  
                                    <label for="id_password" class="requiredField"> 验证码<span class="asteriskField">  
  
                                        <img v-if="cid"  :src="'http://localhost:5000/captcha/'+cid+'.png'" />  
  
                                    </span></label>  
                                    <div class="">  
                                        <input type="text" placeholder="请输入验证码" v-model="code"  minlength="8" maxlength="99" class="textinput textInput form-control">  
                                    </div>  
                                </div>  
                            </div>

这里通过拼接将获取到的验证码id绑定在图片标签中,如图所示:

接着,改写用户登录逻辑user.go:

ret := make(map[string]interface{}, 0)  
  
	cid := ctx.PostValue("cid")  
	code := ctx.PostValue("code")  
  
	if captcha.VerifyString(cid, code) == false {  
  
		ret["errcode"] = 2  
		ret["msg"] = "登录失败,验证码错误"  
		ctx.JSON(ret)  
		return  
  
	}

这里增加两个参数,验证码标识以及用户输入的表单验证码,通过captcha.VerifyString函数进行比对,如果二者吻合那么证明用户输入正确,否则输入错误。

同样地,前端应对增加表单请求字段:

//登录请求  
                signin:function(){  
  
  
                    this.myaxios("http://localhost:5000/signin/","post",{"username":this.username,"password":this.password,"cid":this.cid,"code":this.code}).then(data => {  
                        console.log(data)  
                        alert(data.msg);  
                    });  
  
                }

如图所示:

至此,集成图像验证码的登录逻辑就完成了。

结语

每一次captcha.NewLen(4)返回的验证码标识都是唯一的,所以也就避免了多个账号并发登录所带来的覆盖问题,同时验证码本体和其生命周期都存储在Iris服务的内存中,既灵活又方便。登录成功以后,下一步就面临用户信息留存方案的选择。该项目已开源在Github:https://github.com/zcxey2911/IrisBlog ,与君共觞,和君共勉。

与彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-登录与图形验证码(captcha)EP06相似的内容:

彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-登录与图形验证码(captcha)EP06

书接上回,上一回我们按照“低耦合高内聚”的组织架构方针对项目的整体结构进行了优化,本回将会继续编写业务,那就是用户的登录逻辑,将之前用户管理模块中添加的用户账号进行账号和密码的校验,校验通过后留存当前登录用户的信息,过程中使用图形验证码强制进行人机交互,防止账号的密码被暴力破解。 登录逻辑 首先在逻

彩虹女神跃长空,Go语言进阶之Go语言高性能Web框架Iris项目实战-JWT和中间件(Middleware)的使用EP07

前文再续,上一回我们完成了用户的登录逻辑,将之前用户管理模块中添加的用户账号进行账号和密码的校验,过程中使用图形验证码强制进行人机交互,防止账号的密码被暴力破解。本回我们需要为登录成功的用户生成Token,并且通过Iris的中间件(Middleware)进行鉴权操作。 Iris模板复用 在生成Tok

r0tracer 源码分析

使用方法 修改r0tracer.js文件最底部处的代码,开启某一个Hook模式。 function main() { Java.perform(function () { console.Purple("r0tracer begin ... !") //0. 增加精简模式,就是以彩虹色只显示进出函数

[转帖]采用cat与EOF组合添加多行内容时防止变量解析的解决办法

https://blog.51cto.com/xoyabc/1718355 【问题描述】 当采用cat与EOF组合添加多行内容时,若含有变量,则追加后的文件中是变量对应的的值,并不是变量本身。 如$a对应的值为111,执行以下命令后 cat >> /etc/profile << EOF $a $a

推荐一款采用 .NET 编写的 反编译到源码工具 Reko

今天给大家介绍的是一款名叫Reko的开源反编译工具,该工具采用C#开发,广大研究人员可利用Reko来对机器码进行反编译处理。我们知道.NET 7 有了NativeAOT 的支持,采用NativeAOT 编译的.NET程序 无法通过ILSpy 之类的传统工具得到源码,这款Reko 可能是唯一一款可以把

Python ArcPy批量掩膜、重采样大量遥感影像

本文介绍基于Python中ArcPy模块,对大量栅格遥感影像文件进行批量掩膜与批量重采样的操作~

[转帖]Data studio普通用户采用非SSL的方式连接openGauss

https://cdn.modb.pro/db/43087 关闭SSL认证由于openGauss默认开启SSL认证,且配置认证较为麻烦,个人开发测试并不需要它。因此关闭openGauss的远程用户登录SSL认证模式。1.找到postgresql.conf cd /gaussdb/data/openG

[转帖]你怎么看Data studio普通用户采用非SSL的方式连接openGauss?

https://zhuanlan.zhihu.com/p/365144226 关闭SSL认证 由于openGauss默认开启SSL认证,个人开发测试并不需要它。因此关闭openGauss的远程用户登录SSL认证模式。 1.找到postgresql.conf。 cd /gaussdb/data/ope

[转帖]浅谈redis采用不同内存分配器tcmalloc和jemalloc

http://www.kaotop.com/it/173669.html 我们知道Redis并没有自己实现内存池,没有在标准的系统内存分配器上再加上自己的东西。所以系统内存分配器的性能及碎片率会对Redis造成一些性能上的影响。 在Redis的 zmalloc.c 源码中,我们可以看到如下代码: ?

【转帖】50.设置HotSpot采用解释器还是JIT编译器(-Xint、-Xcomp、Xmixed以及-Server、-Client)

目录 1.设置HotSpot 1.设置HotSpot 1.设置采用解释器还是JIT编译器 -Xint: 完全采用解释器模式执行程序。 -Xcomp: 完全采用即时编译器模式执行程序。如果即时编译出现问题,解释器会介入执行。 -Xmixed: 采用解释器和JIT编译器并存的方式共同执行程序。默认模式。