【REST2SQL】12 REST2SQL增加Token生成和验证

【REST2SQL】01RDB关系型数据库REST初设计
【REST2SQL】02 GO连接Oracle数据库
【REST2SQL】03 GO读取JSON文件
【REST2SQL】04 REST2SQL第一版Oracle版实现
【REST2SQL】05 GO 操作 达梦 数据库
【REST2SQL】06 GO 跨包接口重构代码
【REST2SQL】07 GO 操作 Mysql 数据库
【REST2SQL】08 日志重构增加输出到文件log.txt
【REST2SQL】09 给Go的可执行文件exe加图标和版本信息等
【REST2SQL】10 REST2SQL操作指南
【REST2SQL】11 基于jwt-go生成token与验证


【REST2SQL】11 基于jwt-go生成token与验证的Token生成和验证合并到【REST2SQL】

1 Rest2sql目录下新建token子目录

2 token.go的代码重构

  • 包名改为token
  • 屏蔽或删除 main()入口函数
  • 重构全局变量,都改为外部不可见,即变量名改为首字母改为小写即可
  • 只暴露生成token函数GenerateTokenHandler()和验证token函数ValidateTokenHandler()
  • 暴露函数第二个参数重构

重构完成的代码如下:

go 复制代码
package token

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
)

// 初始化函数
func init() {
	// 实例化计数计时器
	counter = NewCounter()

	// 初始化随机 Key
	key, err := generateRandomString(16)
	if err != nil {
		log.Fatal(err)
	}

	// 打印时间戳
	fmt.Println("Start At timeStamp :", timeStamp, time.Unix(timeStamp, 0), key)
}

// Counter 定义一个简单的计数器
type Counter struct {
	value      int
	timeStampc int64 // 时间戳,用于定期更新密钥key
}

// 计时器变量实例
var counter *Counter

// NewCounter 创建一个新的计数器
func NewCounter() *Counter {
	return &Counter{value: 0, timeStampc: time.Now().Unix()}
}

// Increment 增加计数器的值
func (c *Counter) Increment() {
	c.value++
	c.timeStampc = time.Now().Unix()
}

// // Decrement 减少计数器的值
// func (c *Counter) Decrement() {
// 	c.value--
// }

// // Reset 重置计数器的值为0
// func (c *Counter) Reset() {
// 	c.value = 0
// }

// GetValue 返回计数器的当前值
func (c *Counter) GetCounter() *Counter {
	return c
}

// 
// 定义Token的Claims
type customClaims struct {
	Userid string `json:"userid"`
	Passwd string `json:"passwd"`
	jwt.StandardClaims
}

// 定义Token相关变量
var (
	uid string = "BLMa"  //用户名
	pwd string = "5217"  //密码
	key string = "token" //默认密钥,服务启动时会修改
	iss string = "guwuy" //签发者

	timeStamp   int64         = time.Now().Unix() // 时间戳,用于定期更新密钥key
	timeSecond  int64         = 20                //60 * 60 * 24 * 7  //一周时间的秒数,用于7天修改一次Key
	timeExpires time.Duration = 60 * 60 * 8       // Token 过期时间 秒数,8小时
)

// generateRandomString 生成一个指定长度的随机字符串
func generateRandomString(length int) (string, error) {
	const letters = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := make([]byte, length)
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}
	for i, b := range bytes {
		bytes[i] = letters[b%byte(len(letters))]
	}
	return string(bytes), nil
}

// 定期生成随机Key
func generateRandomKey() {
	//当前时间戳
	timestamp := counter.GetCounter().timeStampc
	//fmt.Println("Now timeStamp,timestamp:", timeStamp, timestamp)

	if timestamp-timeStamp > timeSecond {
		counter.Increment()
		// 修改Key
		key, err := generateRandomString(16)
		if err != nil {
			log.Fatal(err)
		}
		timeStamp = timestamp // 更新Key修改的时间戳

		// 打印时间戳
		fmt.Println("Now timeStamp :", timeStamp, time.Unix(timeStamp, 0), key)
	}
}

// 生成新的Token
func generateToken(userId string) (string, error) {
	// 计数器,计时器
	counter.Increment()

	// 设置Claims
	claims := customClaims{
		Userid: userId,
		Passwd: pwd,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(time.Second * timeExpires).Unix(), // 设置过期时间
			Issuer:    iss,                                              // 设置签发者
		},
	}

	// 创建Token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 定期生成随机Key
	generateRandomKey()
	// 签名Token,这里使用硬编码的密钥,实际生产环境中应使用更安全的密钥管理方式
	signedToken, err := token.SignedString([]byte(key))
	if err != nil {
		return "", err
	}

	return signedToken, nil
}

// 验证Token
func validateToken(tokenString string) (*customClaims, error) {
	// 解析Token
	token, err := jwt.ParseWithClaims(tokenString, &customClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证Token的签名,这里使用硬编码的密钥
		return []byte(key), nil
	})

	if claims, ok := token.Claims.(*customClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, err
}

// HTTP处理函数:生成Token
func GenerateTokenHandler(w http.ResponseWriter, uid_pwd map[string]string) {
	//请求参数,实际情况下,这里可能从请求参数或身份验证过程中获取
	uid = uid_pwd["Userid"]
	pwd = uid_pwd["Passwd"]
	// 这里加uid,pwd的数据库校验
	//fmt.Println(uid, pwd)
	token, err := generateToken(uid)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{
		"token": token,
	})
}

// HTTP处理函数:验证Token
func ValidateTokenHandler(w http.ResponseWriter, tokenString string) {

	claims, err := validateToken(tokenString)
	if err != nil {
		http.Error(w, err.Error(), http.StatusUnauthorized)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]interface{}{
		"userid":  claims.Userid,
		"expires": claims.ExpiresAt,
	})
}

// // main入口
// func main() {
// 	// 检查并生成Key
// 	GenerateRandomKey()

// 	// Token 路由
// 	http.HandleFunc("/generate-token", generateTokenHandler)
// 	// Http://localhost:8080/generate-token?userid=blma&passwd=5217
// 	// curl Http://localhost:8080/generate-token?userid=blma%26passwd=5217
// 	http.HandleFunc("/validate-token", validateTokenHandler)
// 	//curl http://localhost:8080/validate-token -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOiI5OTk4IiwicGFzc3dkIjoiODk5OSIsImV4cCI6MTcwOTcyMDU0MSwiaXNzIjoiZ3V3dXkifQ.UXiW-cgnDZfGUmLtv_yme6gzFZ9XDiKaNATIdFzJ2fY"

// 	fmt.Println("Starting Token server ...")
// 	fmt.Println("Http://localhost:8080/generate-token?userid=&passwd=")
// 	fmt.Println("curl http://localhost:8080/validate-token -H \"Authorization:token\"")
// 	log.Fatal(http.ListenAndServe(":8080", nil))
// }

3 rest2sql.go的 handler() 增加token访问路由

重构的核心代码如下:

  • 请求路径错误提示代码:
go 复制代码
// 2请求路径Path
	req["Path"] = r.URL.Path
	path := strings.Split(r.URL.Path, "/")
	if len(path) < 3 {
		w.Write([]byte("400 Bad Request错误请求。请尝试/rest/xxx or /sql/xxx or /TOKEN/xxx"))
		return
	}
  • 允许的请求路径代码:
go 复制代码
// 3 请求类型REST or SQL or Token
	rors := strings.ToUpper(fmt.Sprint(path[1]))
	// 支持的请求类型
	if !(rors == "REST" || rors == "SQL" || rors == "TOKEN") {
		w.Write([]byte("400 Bad Request错误请求。请尝试/REST/xxx or /SQL/xxx or /TOKEN/xxx"))
		return
	}
  • 请求头token结构代码:
go 复制代码
// 8 请求头 Authorization
	req["Authorization"] = r.Header.Get("Authorization") // 假设Token在Authorization头中
  • 请求参数增加userid和passw代码:
go 复制代码
// 9 请求参数
	query := r.URL.Query()
	req["Userid"] = query.Get("userid") // 登录用户
	req["Passwd"] = query.Get("passwd") // 登录密码

3 dothing.go代码重构

go 复制代码
分支代码:
go 复制代码
case "TOKEN":
		// Token 生成与校验
		doTOKEN(w, req)
  • doTiken函数代码:
go 复制代码
// 根据请求参数执行不同的TOKEN操作 ///
func doTOKEN(w http.ResponseWriter, req map[string]interface{}) {
	// token操作, generate or validate
	resToken := strings.ToLower(req["ResName"].(string))
	switch resToken {
	case "generate-token":
		// w.Write([]byte("generate-token"))
		var uid_pwd map[string]string = make(map[string]string)
		uid_pwd["Userid"] = req["Userid"].(string)
		uid_pwd["Passwd"] = req["Passwd"].(string)
		
		token.GenerateTokenHandler(w, uid_pwd)

		// http://127.0.0.1:5217/TOKEN/generate-token?userid=9998&passwd=8999
	case "validate-token":
		//w.Write([]byte("validate-token"))
		var tokenString string = req["Authorization"].(string)
		token.ValidateTokenHandler(w, tokenString)

		// curl http://localhost:5217/token/validate-token -H "Authorization:token"
	}
}

4 实操演练

Setp 1 启动服务

Step 2 生成Token

html 复制代码
http://127.0.0.1:5217/TOKEN/generate-token?userid=9998&passwd=8999

Step 3 验证Token

html 复制代码
curl http://localhost:5217/token/validate-token -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDk4Njk0OTEsImlzcyI6Imd1d3V5In0.7CLaQKNXZOhnirLfOb_1meYYnc6KVDkXUhrxbfvYgKw"

Step 4 服务日志


本文完。

相关推荐
lzhlizihang几秒前
【Hive sql 面试题】求出各类型专利top 10申请人,以及对应的专利申请数(难)
大数据·hive·sql·面试题
威哥爱编程1 小时前
SQL Server 数据太多如何优化
数据库·sql·sqlserver
Mephisto.java2 小时前
【大数据学习 | kafka高级部分】kafka的kraft集群
大数据·sql·oracle·kafka·json·hbase
Mephisto.java2 小时前
【大数据学习 | kafka高级部分】kafka的文件存储原理
大数据·sql·oracle·kafka·json
数新网络6 小时前
《深入浅出Apache Spark》系列②:Spark SQL原理精髓全解析
大数据·sql·spark
师太,答应老衲吧8 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
NiNg_1_23410 小时前
高级 SQL 技巧详解
sql
全栈开发圈18 小时前
新书速览|Spark SQL大数据分析快速上手
sql·数据分析·spark
WeeJot嵌入式18 小时前
数据库----SQL
数据库·sql
矛取矛求18 小时前
高级sql技巧
数据库·sql·mysql