【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子目录
- Rest2sql目录下新建token子目录
- 拷贝 【REST2SQL】11 基于jwt-go生成token与验证 的mytoken.go
- mytoken.go改名为 token.go
2 token.go的代码重构
- 包名改为token
- 屏蔽或删除 main()入口函数
- 重构全局变量,都改为外部不可见,即变量名改为首字母改为小写即可
- 只暴露生成token函数GenerateTokenHandler()和验证token函数ValidateTokenHandler()
- 暴露函数第二个参数重构
重构完成的代码如下:
package tokenimport ("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() // 时间戳,用于定期更新密钥keytimeSecond int64 = 60 * 60 * 24 * 7 //一周时间的秒数,用于7天修改一次KeytimeExpires 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 {// 修改KeyKey, 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) {// 设置Claimsclaims := customClaims{userid: userid,passwd: pwd,StandardClaims: jwt.StandardClaims{ExpiresAt: time.Now().Add(time.Second * 5217).Unix(), // 设置过期时间Issuer: iss, // 设置签发者},}// 创建Tokentoken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)// 定期生成随机KeygenerateRandomKey()// 签名Token,这里使用硬编码的密钥,实际生产环境中应使用更安全的密钥管理方式signedToken, err := token.SignedString([]byte(key))if err != nil {return "", err}return signedToken, nil
}// 验证Token
func validateToken(tokenString string) (*customClaims, error) {// 解析Tokentoken, 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的数据库校验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访问路由
重构的核心代码如下:
- 请求路径错误提示代码:
// 2请求路径Pathreq["Path"] = r.URL.Pathpath := strings.Split(r.URL.Path, "/")if len(path) < 3 {w.Write([]byte("400 Bad Request错误请求。请尝试/rest/xxx or /sql/xxx or /TOKEN/xxx"))return}
- 允许的请求路径代码:
// 3 请求类型REST or SQL or Tokenrors := 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结构代码:
// 8 请求头 Authorizationreq["Authorization"] = r.Header.Get("Authorization") // 假设Token在Authorization头中
- 请求参数增加userid和passw代码:
// 9 请求参数query := r.URL.Query()req["Userid"] = query.Get("userid") // 登录用户req["Passwd"] = query.Get("passwd") // 登录密码
3 dothing.go代码重构
分支代码:
case "TOKEN":// Token 生成与校验doTOKEN(w, req)
- doTiken函数代码:
// 根据请求参数执行不同的TOKEN操作 ///
func doTOKEN(w http.ResponseWriter, req map[string]interface{}) {// token操作, generate or validateresToken := 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=8999case "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
http://127.0.0.1:5217/TOKEN/generate-token?userid=9998&passwd=8999
Step 3 验证Token
curl http://localhost:5217/token/validate-token -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDk4Njk0OTEsImlzcyI6Imd1d3V5In0.7CLaQKNXZOhnirLfOb_1meYYnc6KVDkXUhrxbfvYgKw"