IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> Go语言-使用Bcrypt实现加密或验证密码 -> 正文阅读

[PHP知识库]Go语言-使用Bcrypt实现加密或验证密码

Go语言使用Bcrypt实现加密或验证登录密码

Bcrypt 就是一款加密工具,它生成的密文是60位的,而且每次加密生成的值是不一样的。
MD5 加密后的值是32位的,且每次加密后的密文都是一样的。
保存密码,一般我们都推荐使用 Bcrypt 进行加密,而不使用 MD5.

Bcrypt 加密后的值举例:

# 比如加密 admin, 两次结果不一样,但都以 $2a 开头
$2a$10$cL3WHWi3/x96MII1pwm4NOMRESxbAHnImp.tV5AMIJCneIkp2IAF2
$2a$10$P1zZnMm8/KYVseSkkfh0T.i2cVwydZ5L/5rZEALWCo3f9TmVLmM9q

# 加密 123456:
$2a$10$wtJie2Wc93SqCCri5u/f4uZX7ATSSyMxlrCTEkPmNHLl9Oa0QdLim

MD5 加密后的值举例:

# 比如加密 admin, MD5两次结果都一样
21232f297a57a5a743894a0e4a801fc3
21232f297a57a5a743894a0e4a801fc3

# 加密 123456: 
e10adc3949ba59abbe56e057f20f883e

初始化项目

(1) 创建一个 pwd_demo 的目录。

$ mkdir pwd_demo

(2) 使用 go mod 命令来管理包,设置项目的包名为 pwd_demo:

$ mkdir pwd_demo
$ go mod init pwd_demo
go: creating new go.mod: module pwd_demo
go: to add module requirements and sums:
	go mod tidy

该命令会创建一个 go.mod 的文件,用于记录引入的包版本,类似于 node 的 package.json 或 php 的 composer.json 文件。

(3) 创建一个 main.go 文件:

package main

import (
	"fmt"
)

func main() {

	fmt.Println("=> hi password!")

}

(4) 运行代码

$ go run main.go 
 => hi password!

启动正常。

编写 utils 包

在 go 语言中,一个包就是一个目录,即同一个目录下的同级的所有go文件应该属于一个包。

这里我们创建一个 utils 目录,并将包名命名为 utils (即和目录名相同,不相同也可以)。

(1) 创建一个 utils 目录, 并创建一个 pwd.go 文件:

$ mkdir utils
$ cd utils
$ touch pwd.go

文件 pwd.go 代码如下:

package utils

import "golang.org/x/crypto/bcrypt"

// 密码加密: pwdHash  同PHP函数 password_hash()
func PasswordHash(pwd string) (string, error) {
	bytes, err := bcrypt.GenerateFrompwd([]byte(pwd), bcrypt.DefaultCost)
	if err != nil {
		return "", err
	}

	return string(bytes), err
}

// 密码验证: pwdVerify  同PHP函数 password_verify()
func PasswordVerify(pwd, hash string) bool {
	err := bcrypt.CompareHashAndpwd([]byte(hash), []byte(pwd))

	return err == nil
}

执行测试用例:

$ go test -v utils/pwd_test.go utils/pwd.go
utils/pwd.go:3:8: 
no required module provides package golang.org/x/crypto/bcrypt; 
to add it:
	go get golang.org/x/crypto/bcrypt

按照提示,安装 crypto/bcrypt 包:

$ go get golang.org/x/crypto/bcrypt
go: downloading golang.org/x/crypto 
   v0.0.0-20210711020723-a769d52b0f97
go get: added golang.org/x/crypto 
   v0.0.0-20210711020723-a769d52b0f97

像 md5 加密方法,在Go自带的包里都有,即 “crypto/md5” 。 但是自带的包里没有 bcrypt 机密库,在其 golang.org/x/crypto 加密扩展包里有,所以需要手动下载。

## 语言自带的
import "crypto/md5"

## 扩展子模块里,需要自己下载
import "golang.org/x/crypto/bcrypt"

如果不能下载,请使用 goproxy.io :

# 配置 GOPROXY 环境变量
export GOPROXY=https://goproxy.io,direct

执行测试代码

go test 命令用于执行测试文件,它会自动读取源码目录下面名为 _test.go 的文件,并执行测试用例。

测试文件中的测试用例要以 func Test 开始,比如:func TestXXXX(t *testing.T)

测试用的参数有且只有一个,即 t *testing.T

测试文件 pwd_test.go 代码如下:

// pwd_test.go
package utils

import (
	"testing"
)

// go test -v utils/pwd_test.go utils/pwd.go
func TestA(t *testing.T) {
	t.Log("TestA")
}

func TestPasswordHash(t *testing.T) {
	t.Log("--> TestHelloWorld ")

	pwd := "admin123"
	hash, _ := PasswordHash(pwd)

	t.Log("--> 输入密码:", pwd)
	t.Log("--> 生成hash:", hash)
	// $2a$10$lRewHvjtPrYZK4TQy.TWDemBMqwIEy/.IVoB7x/2KfqrjzZJNP2ia
	// $2a$10$KEl9ZHfD4gAPu/hgXAjAm.TLgWi5ce7EzBgTdhBfW5IOimtOSfin2

	match := PasswordVerify(pwd, hash)
	t.Log("--> 验证结果:", match)
}

func TestPasswordVerify(t *testing.T) {
	t.Log("--> TestpwdVerify ")

	pwd := "admin2"
	// PHP 生成的密码为 $2y$ 开头 (PHP实现 $2a$ 时有bug,修复时改成了 $2y$)
	hash := "$2y$10$f7ZKW1ZOR4UzGM37.GTmTuY6RmJHknfSwhBacki.Yro1I1kIddEiu"

	match := PasswordVerify(pwd, hash)
	t.Log("--> TestpwdVerify 验证结果:", match)
	if match == false {
		t.Errorf("TestpwdVerify failed. Got false, expected true.")
	}
}

执行测试代码(只返回结果):

$ go test utils/pwd_test.go utils/pwd.go 
ok  	command-line-arguments	0.813s

如果想查看执行过程和测试代码里的日志,使用 -v 参数即可。

$ go test -v utils/pwd_test.go utils/pwd.go
=== RUN   TestA
    pwd_test.go:9: TestA
--- PASS: TestA (0.00s)
=== RUN   Testpwd
    pwd_test.go:13: --> TestHelloWorld 
    pwd_test.go:18: --> 输入密码: admin123
    pwd_test.go:19: --> 生成hash: $2a$10$mqS/IPrw.
    pwd_test.go:24: --> 验证结果: true
--- PASS: Testpwd (0.17s)
=== RUN   TestpwdVerify
    pwd_test.go:28: --> TestpwdVerify 
    pwd_test.go:35: --> TestpwdVerify 验证结果: true
--- PASS: TestpwdVerify (0.08s)
PASS
ok  	command-line-arguments	0.843s

其他地方使用

若有在其他包中使用自定义的 PasswordHash 和 PasswordVerify 方法,直接使用 import 引入 utils 即可。

在 main.go 中使用示例如下:

package main

import (
	"fmt"
	"password_demo/utils"
)

func checkLogin(loginPwd string, hash string) bool {
	return utils.PasswordVerify(loginPwd, hash)
}

func main() {

	fmt.Println("=> hi password!")

	// loginPwd := "123456"
	loginPwd := "admin"
	dbPwd := "$2a$10$ND3xmjbf27oBfjh9AcHxxuLViQubQzVwR/DEYcfjB9RwwR.8h8j6W"

	if checkLogin(loginPwd, dbPwd) {
		fmt.Println("=> 密码输入正确!")
	} else {
		fmt.Println("=> failed 密码错误,请重新输入!")
	}
}
$ go run main.go
=> hi password!
=> 密码输入正确!

拓展内容

(1) PHP 语言中的相关函数:

md5() — 计算字符串的 MD5 散列值
crypt() — 单向字符串散列
password_hash() — 创建密码的散列(hash)
password_verify() — 验证密码是否和散列值匹配

password_hash() 使用了一个强的哈希算法,来产生足够强的盐值,并且会自动进行合适的轮次。
password_hash() 是 crypt() 的一个简单封装,并且完全与现有的密码哈希兼容。
推荐使用 password_hash()。

(2) go test 执行测试报错:

使用 go test 执行单个测试文件报错。

$ go test utils/pwd_test.go                  
# command-line-arguments [command-line-arguments.test]
utils/pwd_test.go:16:13: undefined: pwdHash
utils/pwd_test.go:23:11: undefined: pwdVerify
utils/pwd_test.go:34:11: undefined: pwdVerify
FAIL	command-line-arguments [build failed]
FAIL

这是因为在测试用例代码里并没有定义使用的方法,需要报 pwd.go 加上即可。

$ go test utils/pwd_test.go utils/pwd.go 
ok  	command-line-arguments	0.847s

参考内容

https://www.twle.cn/t/877
https://stackoverflow.com/questions/15733196/where-2x-prefix-are-used-in-bcrypt
https://blog.csdn.net/mrtwenty/article/details/91952707
https://zhuanlan.zhihu.com/p/92845975
https://www.jianshu.com/p/fc910a1f7c8d/
https://www.php.net/manual/zh/function.pwd-hash.php

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-08-08 11:01:33  更:2021-08-08 11:02:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/8 11:02:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码