簡介
JWT是json web token
具體jwt的組成,加密方式等等自行百度解決,我這裡僅寫實現案例:
控制器程式碼
package controller
import (
"errors"
"fmt"
"gindemo/dto"
"gindemo/middleware"
"gindemo/middleware/jwt"
"gindemo/models"
"github.com/gin-gonic/gin"
"log"
)
func Login(c *gin.Context) {
loginInput := &dto.LoginInput{}//我這裡分層了,主要是把引數驗證這塊單獨分離出來了
if err := loginInput.BindingValidParams(c); err != nil {
middleware.ResponseError(c, 2001, err)
return
}
user := &models.User{}
fmt.Println(loginInput)
token, err := user.Login(loginInput.UserName, loginInput.Password)
if err != nil {
if err.Error() == "record not found" {
middleware.ResponseError(c, 500, errors.New("該使用者不存在"))
return
} else {
middleware.ResponseError(c, 500, errors.New("登入錯誤"))
return
}
}
middleware.ResponseSuccess(c,token)
return
}
//用於測試使用
func UserList(c *gin.Context) {
var user models.User
claims := c.MustGet("claims").(*jwt.CustomClaims)
users, err := user.ListUsers(claims.Name)
if err != nil {
log.Fatal(err)
}
middleware.ResponseSuccess(c, users)
}
引數驗證
package dto
import (
"errors"
"gindemo/public"
"github.com/gin-gonic/gin"
ut "github.com/go-playground/universal-translator"
"gopkg.in/go-playground/validator.v9"
zh_translations "gopkg.in/go-playground/validator.v9/translations/zh"
"strings"
)
type LoginInput struct {
UserName string `form:"username" validate:"required"`
Password string `form:"password" validate:"required"`
}
func (o *LoginInput) BindingValidParams(c *gin.Context) error {
//繫結資料
if err := c.ShouldBind(o); err != nil {
return err
}
v := c.Value("trans")
trans, ok := v.(ut.Translator)
if !ok {
trans, _ = public.Uni.GetTranslator("zh")
}
////驗證器註冊翻譯器
//e := zh_translations.RegisterDefaultTranslations(public.Validate, trans)
//if e != nil {
// return e
//}
//驗證
err := public.Validate.Struct(o)
if err != nil {
sliceErrs := []string{}
for _, e := range err.(validator.ValidationErrors) {
sliceErrs = append(sliceErrs, e.Translate(trans))
}
return errors.New(strings.Join(sliceErrs, ","))
}
return nil
}
生成token
package models
import (
"errors"
"fmt" "gindemo/database" "gindemo/middleware/jwt" jwtgo "github.com/dgrijalva/jwt-go"
"github.com/jinzhu/gorm"
"log" "time")
type User struct {
Id int `form:"id" json:"id" gorm:"PRIMARY_KEY"`
Name string `form:"username" json:"username"`
Email string `form:"email" json:"email",binding:"required"`
Password string `form:"password" json:"-",binding:"required"`
}
type LoginResult struct {
User interface{} `json:"user"`
Token string `json:"token"`
}
func (User) TableName() string {
return "users"
}
func (u *User) Login(name string, password string) (token LoginResult, err error) {
var user User
fmt.Println(name, password)
obj := database.GormPool.Where("name = ? and password=?", name, password).First(&user)
if err = obj.Error; err != nil {
return
}
generateToken := GenerateToken(user)
return generateToken, nil
}
//測試使用
func (u *User) ListUsers(name string) (users []User, err error) {
query := database.GormPool
if name != "" {
query = query.Where("name=?", name)
}
err = query.Find(&users).Error
if err != nil && err != gorm.ErrRecordNotFound {
return nil, err
}
return
}
// 生成令牌 建立jwt風格的token
func GenerateToken(user User) LoginResult {
j := &jwt.JWT{
[]byte("newtrekWang"),
}
claims := jwt.CustomClaims{
user.Id,
user.Name,
user.Password,
jwtgo.StandardClaims{
NotBefore: int64(time.Now().Unix() - 1000), // 簽名生效時間
ExpiresAt: int64(time.Now().Unix() + 3600), // 過期時間 一小時
Issuer: "cfun", //簽名的發行者
},
}
token, err := j.CreateToken(claims)
if err != nil {
return LoginResult{
User: user,
Token: token,
}
}
log.Println(token)
data := LoginResult{
User: user,
Token: token,
}
return data
}
建立jwt.go檔案
package jwt
import (
"errors"
"fmt"
"gindemo/middleware"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"log"
"time"
)
// JWTAuth 中介軟體,檢查token
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.Request.Header.Get("token")
if token == "" {
middleware.ResponseError(c, -1, errors.New("請求未攜帶token,無許可權訪問"))
c.Abort()
return
}
log.Print("get token: ", token)
j := NewJWT()
// parseToken 解析token包含的資訊
claims, err := j.ParseToken(token)
fmt.Println("claims", claims)
if err != nil {
if err == TokenExpired {
middleware.ResponseError(c, -1, errors.New("授權已過期"))
c.Abort()
return
}
middleware.ResponseError(c, -1, err)
c.Abort()
return
}
// 繼續交由下一個路由處理,並將解析出的資訊傳遞下去
c.Set("claims", claims)
}
}
// JWT 簽名結構
type JWT struct {
SigningKey []byte
}
// 一些常量
var (
TokenExpired error = errors.New("Token is expired")
TokenNotValidYet error = errors.New("Token not active yet")
TokenMalformed error = errors.New("That's not even a token")
TokenInvalid error = errors.New("Couldn't handle this token:")
SignKey string = "cfun"
)
// 載荷,可以加一些自己需要的資訊
type CustomClaims struct {
ID int `json:"userId"`
Name string `json:"name"`
Password string `json:"telephone"`
jwt.StandardClaims
}
// 新建一個jwt例項
func NewJWT() *JWT {
return &JWT{
[]byte(GetSignKey()),
}
}
// 獲取signKey
func GetSignKey() string {
return SignKey
}
// 這是SignKey
func SetSignKey(key string) string {
SignKey = key
return SignKey
}
// CreateToken 生成一個token
func (j *JWT) CreateToken(claims CustomClaims) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.SigningKey)
}
// 解析Tokne
func (j *JWT) ParseToken(tokenString string) (*CustomClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.SigningKey, nil
})
if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, TokenMalformed
} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
// Token is expired
return nil, TokenExpired
} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
return nil, TokenNotValidYet
} else {
return nil, TokenInvalid
}
}
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims, nil
}
return nil, TokenInvalid
}
// 更新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
jwt.TimeFunc = func() time.Time {
return time.Unix(0, 0)
}
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return j.SigningKey, nil
})
if err != nil {
return "", err
}
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
jwt.TimeFunc = time.Now
claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
return j.CreateToken(*claims)
}
return "", TokenInvalid
}
用到的公共返回方法
package middleware
import (
"encoding/json"
"github.com/gin-gonic/gin"
)
const (
SuccessCode ResponseCode = 200
)
type ResponseCode int
type Response struct {
ErrorCode ResponseCode `json:"errno"`
ErrorMsg string `json:"errmsg"`
Data interface{} `json:"data"`
}
func ResponseError(c *gin.Context, code ResponseCode, err error) {
resp := &Response{ErrorCode: code, ErrorMsg: err.Error(), Data: ""}
c.JSON(200, resp)
response, _ := json.Marshal(resp)
c.Set("response", string(response))
//c.AbortWithError(200, err)
}
func ResponseSuccess(c *gin.Context, data interface{}) {
resp := &Response{ErrorCode: SuccessCode, ErrorMsg: "", Data: data}
c.JSON(200, resp)
response, _ := json.Marshal(resp)
c.Set("response", string(response))
}
路由定義
func InitRouter() *gin.Engine {
file, _ := os.Create("logs/app/log")
gin.DefaultWriter = io.MultiWriter(file)
router := gin.Default()
router.Use(gin.Recovery(), gin.Logger())
//登入註冊
router.POST("/login", controller.Login)
router.POST("/register", controller.Register)
//使用者相關
userRoute := router.Group("user")
userRoute.Use(jwt.JWTAuth())//這裡使用Use,jwtAuth就行
userRoute.GET("/list", controller.UserList)
}
登入獲取token.
(localhost:8080/login?username=cfun&password=123456)
測試token資料合法性
(localhost:8080/user/list).token放到header裡面
裡面需要密碼未加密需要最佳化,也歡迎同學們指出其他錯誤,非常感謝,一起成長!!
本作品採用《CC 協議》,轉載必須註明作者和本文連結