【REST2SQL】11 基于jwt-go生成token与验证

7 篇文章 0 订阅

【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操作指南

0 token 与 jwt 简介

0.1 token简介

Token 本质是字符串,用于请求时附带在请求头中,校验请求是否合法及判断用户身份。
Token 请求时提供,用于校验用户是否具备访问接口的权限。
Token 的用途主要有三点:

  • 拦截无效请求,降低服务器处理压力;
  • 实现第三方 API授权,无需每次都输入用户名密码鉴权;
  • 身份校验,防止 CSRF 攻击。

0.2 jwt简介

JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519),它是一种安全的、轻量级的身份验证方式。

JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

头部(Header):JWT 的头部通常由两部分信息组成:令牌的类型(即JWT)和所使用的签名算法,例如:

{  "alg": "HS256",  "typ": "JWT"}

载荷(Payload):JWT的载荷包含了一些声明(Claim),用于描述用户信息、权限、过期时间等等,例如:

{  "sub": "1234567890",  "name": "John Doe",  "iat": 1516239022}

签名(Signature):JWT的签名由头部和载荷组成,并使用密钥进行加密生成,例如:

HMACSHA256(  base64UrlEncode(header) + "." +  base64UrlEncode(payload),  secret)

0.2.1 JWT 的使用流程如下:

用户使用用户名和密码进行登录,服务器验证用户信息是否正确。
服务器生成一个JWT,将用户信息、权限等信息写入载荷中,并使用密钥对头部和载荷进行签名。
服务器将生成的JWT返回给客户端,客户端将其存储在本地,通常是在浏览器的cookie或本地存储中。
客户端在后续的请求中,将JWT作为请求头部或请求参数传递给服务器。
服务器收到请求后,验证JWT的签名是否正确,如果正确则解析出用户信息、权限等信息,进行后续操作。

0.2.2 JWT 的优点:

无状态:JWT是无状态的,服务器不需要保存任何会话信息,可以轻松扩展和分布式环境下使用。
安全:JWT通过密钥对头部和载荷进行签名,保证了数据的完整性和安全性。
跨域支持:JWT可以跨域使用,可以在不同的域名和服务器之间使用。
简单易用:JWT使用简单,易于实现和维护。

0.2.3 JWT 的缺点:

载荷信息不能太多:JWT的载荷信息不能太多,否则会导致JWT的长度过长,增加网络传输的负担。
安全性依赖于密钥:JWT的安全性依赖于密钥的保护,如果密钥泄露,则JWT的安全性将受到威胁。
无法撤销:一旦JWT生成后,无法撤销,除非修改密钥或者设置短期的过期时间

1 基于jwt-go实现token服务端

JWT-Go是一个使用Go语言实现的JSON Web Token(JWT)库1。

JWT是一种跨域认证解决方案,属于一个开放的标准,它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。JWT-Go库提供了创建和验证JWT的方法,包括使用默认Claims和自定义Claims来创建Token对象,以及解析和验证Token的有效性等。JWT-Go库在Go语言社区中广泛使用,并且有许多配置选项可供选择,可以满足不同的业务需求。

1.1 安装jwt-go库

go get github.com/dgrijalva/jwt-go

1.2 创建mytoken项目

全部代码如下:

package main

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

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

// 定义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  = 60 * 60 * 24 * 7  //一周时间的秒数,用于7天修改一次Key
	timeExpires int64  = 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 := time.Now().Unix()
	if (timestamp-timeStamp > timeSecond) || len(Key) < 10 {
		// 修改Key
		Key, err := GenerateRandomString(16)
		if err != nil {
			log.Fatal(err)
		}
		timeStamp = timestamp // 更新Key修改的时间戳

		// 打印时间戳
		fmt.Println("timeStamp :", timeStamp, time.Unix(timeStamp, 0))
		fmt.Println("Random String:", Key)
	}
}

// 生成新的Token
func generateToken(userid string) (string, error) {
	// 设置Claims
	claims := CustomClaims{
		Userid: userid,
		Passwd: Pwd,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: time.Now().Add(time.Second * 5217).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, r *http.Request) {
	//请求参数,实际情况下,这里可能从请求参数或身份验证过程中获取
	query := r.URL.Query()
	Uid = query.Get("userid")
	Pwd = query.Get("passwd")
	// 这里加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, r *http.Request) {
	tokenString := r.Header.Get("Authorization") // 假设Token在Authorization头中

	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 server ...")
	fmt.Println("Http://localhost:8080/generate-token?userid=&passwd=")
	log.Fatal(http.ListenAndServe(":8080", nil))
}


2 实操演练

2.1 启动服务

在这里插入图片描述

2.2 获取token

打开浏览器地址栏输入:

 http://localhost:8080/generate-token?userid=9998&passwd=8999

在这里插入图片描述

返回token

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOiI5OTk4IiwicGFzc3dkIjoiODk5OSIsImV4cCI6MTcwOTcyMDU0MSwiaXNzIjoiZ3V3dXkifQ.UXiW-cgnDZfGUmLtv_yme6gzFZ9XDiKaNATIdFzJ2fY"
}

2.3 验证token

cmd窗口输入curl如下命令:

curl http://localhost:8080/validate-token -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOiI5OTk4IiwicGFzc3dkIjoiODk5OSIsImV4cCI6MTcwOTcyMDU0MSwiaXNzIjoiZ3V3dXkifQ.UXiW-cgnDZfGUmLtv_yme6gzFZ9XDiKaNATIdFzJ2fY"

在这里插入图片描述

返回token的失效时间和用户id:

{"expires":1709720541,"userid":"9998"}

其中时间戳 1709720541 对应的时间为:2024-03-06 18:22:21


本文完。

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值