小白启程1: Go语言 beego + JWT 实现注册登录验证

Go语言 beego + JWT 实现注册登录验证

  • 1.1 安装Beego
  • 1.2 安装JWT
  • 2. Beego + JWT 注册登录实现
  • 3. Golang JWT实例

1.1 安装Beego

go get -u github.com/beego/beego/v2
go get -u github.com/beego/bee/v2

beego 是一个快速开发 Go 应用的 HTTP 框架,他可以用来快速开发 API、Web 及后端服务等各种应用,是一个 RESTful 的框架,主要设计灵感来源于 tornado、sinatra 和 flask 这三个框架,但是结合了 Go 本身的一些特性(interface、struct 嵌入等)而设计的一个框架。

官方文档 https://beego.me/docs/intro/

1.2 安装JWT

go get github.com/dgrijalva/jwt-go

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

Github Repo: https://github.com/dgrijalva/jwt-go

2. Beego + JWT 注册登录实现

main.go
连接数据库 (这里我用的是mysql)+ 运行主程序


package main

import (
	"firstAPI/models"
	_ "firstAPI/routers"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
)

func init() {
     
	orm.Debug = true

	if err1 := orm.RegisterDriver("mysql", orm.DRMySQL); err1 != nil {
     
		logs.Error(err1.Error())
	}

	orm.RegisterModel(new(models.User))

	if err2 := orm.RegisterDataBase("default","mysql","root:12345678@tcp(127.0.0.1:3306)/testdb");err2 != nil {
     
		logs.Error(err2.Error())
		panic(err2.Error())
	}
	fmt.Println("Connected to the database")
	orm.RunSyncdb("default", false, true)
}

func main() {
     
	if beego.BConfig.RunMode == "dev" {
     
		beego.BConfig.WebConfig.DirectoryIndex = true
		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
	}

	beego.Run()

models/utils.go
产生token,验证token,更新token

package models

import(
	"crypto/rand"
	"errors"
	"fmt"
	"github.com/astaxie/beego/logs"
	"github.com/dgrijalva/jwt-go"
	"golang.org/x/crypto/scrypt"
	"io"
	"log"
	"time"
)

// JWT : HEADER PAYLOAD SIGNATURE
const (
	SecretKEY              string = "JWT-Secret-Key"
	DEFAULT_EXPIRE_SECONDS int    = 180 // default expired 1 minute
	PasswordHashBytes             = 16
)

// This struct is the payload
type MyCustomClaims struct {
     
	UserID int `json:"UserID"`
	jwt.StandardClaims
}

// This struct is the parsing of token payload
type JwtPayload struct {
     
	Username  string `json:"Username"`
	UserID    int    `json:"UserID"`
	IssuedAt  int64  `json:"Iat"`
	ExpiresAt int64  `json:"Exp"`
}

//generate token
func GenerateToken(loginInfo *LoginRequest, userID int, expiredSeconds int) (tokenString string, err error) {
     
	if expiredSeconds == 0 {
     
		expiredSeconds = DEFAULT_EXPIRE_SECONDS
	}

	// Create the Claims
	mySigningKey := []byte(SecretKEY)
	expireAt := time.Now().Add(time.Second * time.Duration(expiredSeconds)).Unix()
	logs.Info("Token will be expired at ", time.Unix(expireAt, 0))

	user := *loginInfo
	claims := MyCustomClaims{
     
		userID,
		jwt.StandardClaims{
     
			Issuer:    user.Username,
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: expireAt,
		},
	}

	// Create the token using your claims
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// Signs the token with a secret
	tokenStr, err := token.SignedString(mySigningKey)
	if err != nil {
     
		return "", errors.New("error: failed to generate token")
	}

	return tokenStr, nil
}

//validate token
func ValidateToken(tokenString string) (*JwtPayload, error) {
     
	token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{
     },
		func(token *jwt.Token) (interface{
     }, error) {
     
			return []byte(SecretKEY), nil
		})

	claims, ok := token.Claims.(*MyCustomClaims)
	if ok && token.Valid {
     
		log.Println("ok && token valid")
		logs.Info("%v %v", claims.UserID, claims.StandardClaims.ExpiresAt)
		logs.Info("Token was issued at ", time.Now().Unix())
		logs.Info("Token will be expired at ", time.Unix(claims.StandardClaims.ExpiresAt, 0))

		return &JwtPayload{
     
			Username:  claims.StandardClaims.Issuer,
			UserID:    claims.UserID,
			IssuedAt:  claims.StandardClaims.IssuedAt,
			ExpiresAt: claims.StandardClaims.ExpiresAt,
		}, nil
	} else {
     
		fmt.Println(err)
		return nil, errors.New("error: failed to validate token")
	}
}

//update token
func RefreshToken(tokenString string) (newTokenString string, err error) {
     
	// get previous token
	token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{
     },
		func(token *jwt.Token) (interface{
     }, error) {
     
			return []byte(SecretKEY), nil
		})

	claims, ok := token.Claims.(*MyCustomClaims)
	if !ok || !token.Valid {
     
		return "", err
	}

	mySigningKey := []byte(SecretKEY)
	expireAt := time.Now().Add(time.Second * time.Duration(DEFAULT_EXPIRE_SECONDS)).Unix() //new expired
	newClaims := MyCustomClaims{
     
		claims.UserID,
		jwt.StandardClaims{
     
			Issuer:    claims.StandardClaims.Issuer, //name of token issue
			IssuedAt:  time.Now().Unix(),            //time of token issue
			ExpiresAt: expireAt,// new expired
		},
	}

	// generate new token with new claims
	newToken := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
	// sign the token with a secret
	tokenStr, err := newToken.SignedString(mySigningKey)
	if err != nil {
     
		return "", errors.New("error: failed to generate new fresh json web token")
	}

	return tokenStr, nil
}


Hash密码

// generate salt
func GenerateSalt() (salt string, err error) {
     
	buf := make([]byte, PasswordHashBytes)
	if _, err := io.ReadFull(rand.Reader, buf); err != nil {
     
		return "", errors.New("error: failed to generate user's salt")
	}

	return fmt.Sprintf("%x", buf), nil
}

// generate password hash
func GeneratePassHash(password string, salt string) (hash string, err error) {
     
	h, err := scrypt.Key([]byte(password), []byte(salt), 16384, 8, 1, PasswordHashBytes)
	if err != nil {
     
		return "", errors.New("error: failed to generate password hash")
	}

	return fmt.Sprintf("%x", h), nil
}

验证前端传回来的token是否过期

func CheckStatus(tokenString string) (string,int64) {
     
	jp, err := ValidateToken(tokenString)

	if err != nil {
     
		// if token has already expired
		fmt.Println("Your token has expired, Please log in again! ")
		return "", -1
	}

	timeDiff := jp.ExpiresAt - time.Now().Unix()
	fmt.Println("timeDiff = ", timeDiff)
	if timeDiff <= 30 {
     
		// if token is close to expiration, refresh the token
		fmt.Println("Your token would soon be expired")
		newToken, err := RefreshToken(tokenString)
		if err == nil {
     
			return newToken, timeDiff
		}
	}
	// if token is valid, do nothing
	fmt.Println("Your token is good ")
	return tokenString,timeDiff
}

models/user.go
登录和注册 (request 根据自己需求定义)

func DoLogin(lr *LoginRequest) (*LoginResponse, int, error){
     
	// get username and password
	username := lr.Username
	password := lr.Password

	// validate user name and password is they are empty
	if len(username) == 0 || len(password) == 0 {
     
		return nil, http.StatusBadRequest,errors.New("error: username or password is empty")
	}

	o := orm.NewOrm()

	// check if the username exists
	user := &User{
     Username: username}
	err := o.Read(user,"Username")
	if err != nil {
     
		return nil, http.StatusBadRequest, errors.New("error: username doesn't exist")
	}

	// generate the password hash
	hash, err := GeneratePassHash(password,user.Salt)
	if err != nil {
     
		return nil, http.StatusBadRequest, err
	}
	if hash != user.Password {
     
		return nil, http.StatusBadRequest,errors.New("error: password is error")
	}

	// generate token
	tokenString, err := GenerateToken(lr, user.Id, 0)
	if err != nil {
     
		return nil, http.StatusBadRequest, err
	}

	return &LoginResponse{
     
		Username: user.Username,
		UserID: user.Id,
		Token: tokenString,
	},http.StatusOK,nil
}

func DoCreateUser(cr *CreateRequest)(*CreateResponse,int,error){
     
	o := orm.NewOrm()

	// check if username exists
	userNameCheck := User{
     Username: cr.Username}
	err := o.Read(&userNameCheck,"Username")
	if err == nil {
     
		return nil, http.StatusBadRequest, errors.New("username has already existed")
	}

	//generate salt
	saltKey, err := GenerateSalt()
	if err != nil {
     
		logs.Info(err.Error())
		return nil, http.StatusBadRequest, err
	}

	// generate password hash
	hash, err := GeneratePassHash(cr.Password,saltKey)
	if err != nil {
     
		logs.Info(err.Error())
		return nil, http.StatusBadRequest,err
	}

	// create user
	user := User{
     }
	user.Username = cr.Username
	user.Password = hash
	user.Salt = saltKey

	_, err = o.Insert(&user)
	if err != nil {
     
		logs.Info(err.Error())
		return nil, http.StatusBadRequest,err
	}

	return &CreateResponse{
     
		UserID:user.Id,
		Username: user.Username,
	}, http.StatusOK,nil
}

3. Golang JWT实例

  • 分享一个个人感觉非常好的Golang JWT实例 点我

感谢阅读~~~

你可能感兴趣的:(golang,go,jwt,mysql)