GolangNote

Golang笔记

golang 实现Authenticator 二次验证,可用到web 登录

Permalink

Google 的Authenticator app 可计算动态码,用于app 安全登录,利用其算法,可以使用在web 用户登录的二次验证。

下面是用 golang 实现的 Authenticator 二次验证,验证码主要取决于secretKey 的值。

Go: Authenticator 二次验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base32"
	"fmt"
	"github.com/skip2/go-qrcode"
	"os"
	"strings"
	"time"
)

func toBytes(value int64) []byte {
	var result []byte
	mask := int64(0xFF)
	shifts := [8]uint16{56, 48, 40, 32, 24, 16, 8, 0}
	for _, shift := range shifts {
		result = append(result, byte((value>>shift)&mask))
	}
	return result
}

func toUint32(bytes []byte) uint32 {
	return (uint32(bytes[0]) << 24) + (uint32(bytes[1]) << 16) +
		(uint32(bytes[2]) << 8) + uint32(bytes[3])
}

func oneTimePassword(key []byte, value []byte) uint32 {
	// sign the value using HMAC-SHA1
	hmacSha1 := hmac.New(sha1.New, key)
	hmacSha1.Write(value)
	hash := hmacSha1.Sum(nil)

	// We're going to use a subset of the generated hash.
	// Using the last nibble (half-byte) to choose the index to start from.
	// This number is always appropriate as it's maximum decimal 15, the hash will
	// have the maximum index 19 (20 bytes of SHA1) and we need 4 bytes.
	offset := hash[len(hash)-1] & 0x0F

	// get a 32-bit (4-byte) chunk from the hash starting at offset
	hashParts := hash[offset : offset+4]

	// ignore the most significant bit as per RFC 4226
	hashParts[0] = hashParts[0] & 0x7F

	number := toUint32(hashParts)

	// size to 6 digits
	// one million is the first number with 7 digits so the remainder
	// of the division will always return < 7 digits
	pwd := number % 1000000

	return pwd
}

func main() {
	secretKey := "qwertyuiopasdfghqwertyuiopasdfgh" // 16*N 个字符
	secretKeyUpper := strings.ToUpper(secretKey)
	key, err := base32.StdEncoding.DecodeString(secretKeyUpper)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	// generate a one-time password using the time at 30-second intervals
	epochSeconds := time.Now().Unix()
	pwd := oneTimePassword(key, toBytes(epochSeconds/30))

	secondsRemaining := 30 - (epochSeconds % 30)
	fmt.Printf("%06d (%d second(s) remaining)\n", pwd, secondsRemaining)

	// 生成二维码供 Authenticatior (身份验证器) 扫描
	authLink := "otpauth://totp/网站名?secret=" + secretKey + "&issuer=用户名"
	fmt.Println(authLink)

	err = qrcode.WriteFile(authLink, qrcode.Medium, 256, "qr.png")
	if err != nil {
		fmt.Println("write error")
	}
}

运行上面的代码后就会生成一个二维码图片,使用Authenticatior 扫描,即可同步显示验证码,每30秒刷新次。

参考:

本文网址: https://golangnote.com/topic/227.html 转摘请注明来源

Related articles

Golang Web 程序生产环境独立部署示例

一个 web 应用通常是跑在一个前端代理,如 Nginx 后,这样可以方便的在同一个服务器部署多个应用。这里说的独立部署是指让 go web 程序直接暴露在外面,独占 443、80 端口(俗称裸跑)。这样做除了性能有些提高外,更重要的是部署方便。...

Golang 把cookie 字符串解析为cookie 结构

在做爬虫时有时候会遇到需要带已登录的 cookie 请求,这个时候最简单的方法是在浏览器登录后,在开发者面板找到cookie 字符串,然后拷贝粘贴。这就面临一个问题需要把cookie 字符串解析成Go 语言 cookie 结构体。...

Golang quicktemplate 模版快速入门

Golang 有很多的模版引擎,自带的 `html/template` 也很好,大多数情况都能满足需求,只是有些逻辑、条件判断不好在模版里实现, `quicktemplate` 是个很好的选择。...

Write a Comment to "golang 实现Authenticator 二次验证,可用到web 登录"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.18 Processed in 0ms