代码
package main
import (
"errors"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
)
func main() {
// gin.Default 会使用Logger, Recover中间件,
// 这里改用gin.New 新建一个没有任何默认中间件的路由
r := gin.New()
// 全局中间件
// Logger 中间件将日志写入 gin.DefaultWriter,即使你将 GIN_MODE 设置为 release。
// By default gin.DefaultWriter = os.Stdout
r.Use(gin.Logger())
// Recovery 中间件会 recover 任何 panic。如果有 panic 的话,会写入 500。
r.Use(gin.Recovery())
// 定义路由组
v1 := r.Group("/v1")
{
v1.POST("/auth", func(c *gin.Context) {
username, ok := c.GetPostForm("username")
if !ok {
c.JSON(400, gin.H{"msg": "The form is missing username."})
return
}
password, ok := c.GetPostForm("password")
if !ok {
c.JSON(400, gin.H{"msg": "The form is missing password."})
return
}
func(u, p string) {}(username, password) // 验证username, password, 没写验证逻辑
tokenString, err := generateToken(username, time.Hour*24*7)
if err != nil {
c.JSON(400, gin.H{
"msg": err.Error(),
"username": username,
})
return
}
c.JSON(200, gin.H{
"token": tokenString,
"username": username,
})
})
v1.GET("/home", JWTAuthMiddleware(), func(c *gin.Context) {
username, _ := c.Get("username")
c.JSON(200, gin.H{
"status": "ok",
"username": username,
})
})
}
r.Run(":8080")
}
// For HMAC signing method, the key can be any []byte. It is recommended to generate
// a key using crypto/rand or something equivalent. You need the same key for signing
// and validating.
var hmacSampleSecret []byte = []byte(`key`)
type MyClaims struct {
Username string `json:"username"`
jwt.StandardClaims
}
// 生成 Token
func generateToken(userName string, expire time.Duration) (tokenString string, err error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, MyClaims{
userName,
jwt.StandardClaims{
NotBefore: time.Now().Unix(),
ExpiresAt: time.Now().Add(expire).Unix(),
},
})
// Sign and get the complete encoded token as a string using the secret
tokenString, err = token.SignedString(hmacSampleSecret)
return
}
// ParseToken 解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
// 解析token
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (i interface{}, err error) {
return hmacSampleSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*MyClaims); ok && token.Valid { // 校验token
return claims, nil
}
return nil, errors.New("invalid token")
}
// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
// 这里假设Token放在Header的Authorization中,并使用Bearer开头
// 这里的具体实现方式要依据你的实际业务情况决定
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
c.JSON(http.StatusOK, gin.H{
"code": 2003,
"msg": "请求头中auth为空",
})
c.Abort()
return
}
// 按空格分割
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusOK, gin.H{
"code": 2004,
"msg": "请求头中auth格式有误",
})
c.Abort()
return
}
// parts[1]是获取到的tokenString,我们使用之前定义好的解析JWT的函数来解析它
mc, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 2005,
"msg": "无效的Token",
})
c.Abort()
return
}
// 将当前请求的username信息保存到请求的上下文c上
c.Set("username", mc.Username)
c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息
}
}