【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 服务日志


本文完。

相关推荐
XYiFfang13 小时前
【MYSQL】SQL学习指南:从常见错误到高级函数与正则表达式
sql·mysql·正则表达式·regexp_like·group_concat
十碗饭吃不饱15 小时前
sql报错:java.sql.SQLSyntaxErrorException: Unknown column ‘as0‘ in ‘where clause‘
java·数据库·sql
呆呆小金人16 小时前
SQL入门: HAVING用法全解析
大数据·数据库·数据仓库·sql·数据库开发·etl·etl工程师
l1t21 小时前
用parser_tools插件来解析SQL语句
数据库·sql·插件·duckdb
TDengine (老段)21 小时前
TDengine 数学函数 ABS() 用户手册
大数据·数据库·sql·物联网·时序数据库·tdengine·涛思数据
Hello.Reader1 天前
Apache StreamPark 快速上手从一键安装到跑起第一个 Flink SQL 任务
sql·flink·apache
养生技术人1 天前
Oracle OCP认证考试题目详解082系列第57题
运维·数据库·sql·oracle·开闭原则
养生技术人2 天前
Oracle OCP认证考试题目详解082系列第53题
数据库·sql·oracle·database·开闭原则·ocp
豆沙沙包?2 天前
2025年--Lc171--H175 .组合两个表(SQL)
数据库·sql
养生技术人2 天前
Oracle OCP认证考试题目详解082系列第48题
运维·数据库·sql·oracle·database·开闭原则·ocp