跳到主要内容

JWT

token

  • Token的作用 Token是一种身份验证和授权机制,用于在客户端和服务器之间传递用户身份信息。它可以将用户信息加密到Token中,并由服务器进行验证。通过使用Token,服务器可以验证用户的身份并控制其对资源的访问权限,从而保护敏感数据和资源。
  • JWT的作用 JWT(JSON Web Token)是一种基于Token的身份验证和授权机制,它使用JSON格式来表示用户身份信息。JWT由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。头部包含了JWT的算法和类型等信息,负载包含了用户的身份信息,签名用于验证Token的完整性和真实性。 JWT的主要作用是验证用户的身份和授权。当用户登录成功后,服务器会生成一个JWT,并将其返回给客户端。客户端在之后的请求中携带这个JWT,服务器通过验证JWT来确认用户的身份和授权状态。如果JWT有效,服务器将允许客户端访问相应的资源。

总结
Token和JWT都是用于身份验证和授权的机制,它们的作用是确保只有经过身份验证的用户才能访问敏感数据和资源。Token和JWT都可以将用户信息加密到其中,并通过服务器进行验证。而JWT则更进一步,使用JSON格式来表示用户身份信息,可以包含更多的信息,并且通过签名来验证Token的完整性和真实性。

JWT

如今有很多将身份验证内置到API中的方法 -JSON Web令牌只是其中之一。 JSON Web令牌(JWT)作为令牌系统而不是在每次请求时都发送用户名和密码,因此比其他方法(如基本身份验证)具有固有的优势。

测试样例

实现代码

package main

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"strings"
"time"
//"github.com/dgrijalva/jwt-go"
"github.com/form3tech-oss/jwt-go"
jwtmiddleware "github.com/auth0/go-jwt-middleware"

)

//第一次访问时的认证信息
type UserCredentials struct {
Username string `json:"username"`
Password string `json:"password"`
UserEmail string `json:"email"`
UserMobile string `json:"mobile"`
}

// 自定义类,如用户信息、浏览器信息等都可以存储在结构体中。
type CustomClaims struct {
UserId int64
UserName string
UserEmail string
UserMobile string
jwt.StandardClaims
}

//定义token使用的结构体,目的是使用该结构把token转换为json格式。
type Token struct {
Token string `json:"token"`
}

// token共享key
const secretKey = "wrfs88wr98mw3rfj"

func main() {

http.HandleFunc("/login", LoginHandler)
http.HandleFunc("/test1", handler_test1) //采用自定义的token校验函数。
http.Handle("/test2", AuthMiddleware(http.HandlerFunc(handler_test2))) //采用第三方模块

log.Println("Now listening 8080 ...")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}

}

func LoginHandler(w http.ResponseWriter, r *http.Request) {
//提得用户post上来的信息
var user UserCredentials
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusForbidden)
fmt.Fprint(w, "Error in request")
return
}
//fmt.Println(user)
//验证是身份:若用户是guofs且密码123321都正确,则生成token
if !strings.EqualFold(user.Username, "guofs") {
w.WriteHeader(http.StatusForbidden)
fmt.Println("Error logging in")
fmt.Fprint(w, "Invalid credentials")
return
} else if strings.Compare(user.Password, "123321") != 0 {
w.WriteHeader(http.StatusForbidden)
fmt.Println("Error logging in")
fmt.Fprint(w, "Invalid credentials")
return
}
//fmt.Println(user)
//fmt.Fprint(w, user,"\n")

//定义token信息,结构可自定义
customClaims := &CustomClaims{
UserId: 110,
UserName: "tom",
UserEmail: "guofs@139.com",
UserMobile: "137****0806",
StandardClaims: jwt.StandardClaims{
Audience: []string{"guofs"},
ExpiresAt: time.Now().Add(time.Minute * 10).Unix(), //过期时间
//Id: "", //非必填
IssuedAt: time.Now().Unix(), //颁发时间
Issuer: "gfs", //颁发者
//NotBefore: 0, //生效时间,就是在这个时间前不能使用,非必填
Subject: "测试项目",
},
}
token_cipher_context,_ := CreateToken(customClaims, []byte(secretKey))
//fmt.Printf("token密文:\n%v\n\n",token_cipher_context)

//将生成的token放入token结构体,并返回json流给client
response := Token{token_cipher_context}
json, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Header().Add("Content-Type", "application/json")
w.Write(json)
w.Write([]byte("\n"))
}

//创建token
func CreateToken(tokenClaims *CustomClaims, key []byte) (string, error) {
//将token信息编码为token码
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = *tokenClaims
tokenString, err := token.SignedString([]byte(key))
if err != nil {
return "", err
}
/*
defer func() {
e := recover()
if e != nil {
panic(e)
}
}()
*/
return tokenString, nil
}

//API接口
func handler_test1(w http.ResponseWriter, r *http.Request) {
//查验token是否有效
//得到客户端Header中的token值
//在Hear中,token存在Authorization键中,也可以修改为其它键,如Token等
tokenString := r.Header.Values("Authorization")[0]
_,err := CheckToken(tokenString, []byte(secretKey))
if err != nil {
w.Write([]byte("Token格式错误或失效或其它\n"))
return
}

var jsonData = map[string]interface{}{
"name": "郭同学",
"addr": "深圳",
}
buffer := new(bytes.Buffer)
err = json.NewEncoder(buffer).Encode(jsonData)
if err != nil {
log.Fatal("转换失败",err)
}
//fmt.Fprintf(w, buffer.String())
w.Write(buffer.Bytes())
}

//自定义函数校验token和解析token
func CheckToken(tokenString string, key []byte) (*CustomClaims, error) {
var errInfo error
//查看secretKey是否有效
if len(key) == 0 {
errInfo = errors.New("HTTP server unable to start, expected an APP_KEY for JWT auth")
return nil,errInfo
}

//定义token的Claims变量
CustomClaims_Context := &CustomClaims{}
//解析token
token, err := jwt.ParseWithClaims(
tokenString,
CustomClaims_Context,
func(token *jwt.Token) (interface{}, error) {
return []byte(secretKey), nil
},
)
if err != nil {
errInfo = errors.New("Token格式错误或失效\n")
return nil,errInfo
}
//校验token是否有效
if !(token.Valid) {
errInfo = errors.New("Token格式错误或失效\n")
return nil,errInfo
}
return CustomClaims_Context,nil
}


//AuthMiddleware是我们用来检查令牌是否有效的中间件。如果返回401状态无效,则返回给客户。
func AuthMiddleware(next http.Handler) http.Handler {
if len(secretKey) == 0 {
log.Fatalln("HTTP server unable to start, expected an APP_KEY for JWT auth")
return nil
}
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
return []byte(secretKey), nil
},
SigningMethod: jwt.SigningMethodHS256,
})
return jwtMiddleware.Handler(next)
}



//API接口
func handler_test2(w http.ResponseWriter, r *http.Request) {
var jsonData = map[string]interface{}{
"name": "王同学",
"addr": "深圳",
}
buffer := new(bytes.Buffer)
err := json.NewEncoder(buffer).Encode(jsonData)
if err != nil {
log.Fatal("转换失败",err)
}
w.Write(buffer.Bytes())
}

测试

# curl -i -H "Content-Type: application/json" -XPOST http://192.168.3.110:8080/login -d'
{
"username":"guofs",
"password":"1pp23321",
"email": "guofs@139.com",
"mobile": "13751090806"
}'
# 返回
HTTP/1.1 200 OK
Date: Wed, 10 Jan 2024 11:46:16 GMT
Content-Length: 320
Content-Type: text/plain; charset=utf-8

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjExMCwiVXNlck5hbWUiOiJ0b20iLCJVc2VyRW1haWwiOiJndW9mc0AxMzkuY29tIiwiVXNlck1vYmlsZSI6IjEzNyoqKiowODA2IiwiYXVkIjpbImd1b2ZzIl0sImV4cCI6MTcwNDg4OTQ2MywiaWF0IjoxNzA0ODg4ODYzLCJpc3MiOiJnZnMiLCJzdWIiOiLmtYvor5Xpobnnm64ifQ.iePjLxlwjmDoe5j9iphFsVAFsqs8m2wtw8DaztgucBc"}

# token验证(自定义函数)
# curl \
> -H "Content-Type: application/json" \
> -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjExMCwiVXNlck5hbWUiOiJ0b20iLCJVc2VyRW1haWwiOiJndW9mc0AxMzkuY29tIiwiVXNlck1vYmlsZSI6IjEzNyoqKiowODA2IiwiYXVkIjpbImd1b2ZzIl0sImV4cCI6MTcwNDg4OTQ2MywiaWF0IjoxNzA0ODg4ODYzLCJpc3MiOiJnZnMiLCJzdWIiOiLmtYvor5Xpobnnm64ifQ.iePjLxlwjmDoe5j9iphFsVAFsqs8m2wtw8DaztgucBc" \
> -XGET http://192.168.3.110:8080/test1
# 返回
{"addr":"深圳","name":"郭同学"}

# 采用 `github.com/auth0/go-jwt-middleware`中间件方式
# curl \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjExMCwiVXNlck5hbWUiOiJ0b20iLCJVc2VyRW1haWwiOiJndW9mc0AxMzkuY29tIiwiVXNlck1vYmlsZSI6IjEzNyoqKiowODA2IiwiYXVkIjpbImd1b2ZzIl0sImV4cCI6MTcwNDg4OTQ2MywiaWF0IjoxNzA0ODg4ODYzLCJpc3MiOiJnZnMiLCJzdWIiOiLmtYvor5Xpobnnm64ifQ.iePjLxlwjmDoe5j9iphFsVAFsqs8m2wtw8DaztgucBc" \
-XGET http://192.168.3.110:8080/test2
# 返回
{"addr":"深圳","name":"王同学"}