简介
JWT是json web token
具体jwt的组成,加密方式等等自行百度解决,我这里仅写实现案例:
控制器代码
package controllerimport ( "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 dtoimport ( "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 modelsimport ( "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风格的tokenfunc 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 jwtimport ( "errors" "fmt" "gindemo/middleware" "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" "log" "time")// JWTAuth 中间件,检查tokenfunc 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()), }}// 获取signKeyfunc GetSignKey() string { return SignKey}// 这是SignKeyfunc SetSignKey(key string) string { SignKey = key return SignKey}// CreateToken 生成一个tokenfunc (j *JWT) CreateToken(claims CustomClaims) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(j.SigningKey)}// 解析Toknefunc (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}// 更新tokenfunc (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 middlewareimport ( "encoding/json" "github.com/gin-gonic/gin")const ( SuccessCode ResponseCode = 200)type ResponseCode inttype 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里面
里面需要密码未加密需要优化,也欢迎同学们指出其他错误,非常感谢,一起成长!!