实例:jwt身份认证

jwt身份认证

1.整合jwt身份认证

  • jwt使用go-jwt库
go get github.com/dgrijalva/jwt-go
  • jwt工具方法

pkg/helper/jwt.go

package helper

import (
"errors"
"github.com/dgrijalva/jwt-go"
"time"
)
var (
	key []byte
	expire int64
)
type Claims struct {
	Username string `json:"username"`
	Wid string `json:"wid"`
	jwt.StandardClaims
}

func init() {
	key = []byte("sockstack")
	expire = 7200
}

func Encode(c Claims, keys []byte) (string, error) {
	if c.ExpiresAt == 0 {
		c.ExpiresAt = time.Now().Unix() + expire
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// Sign and get the complete encoded token as a string using the secret
	if keys != nil {
		key = keys
	}
	return token.SignedString(key)
}

func Decode(s string, keys []byte) (*Claims, error) {
	var err error
	// sample token is expired.  override time so it parses as valid
	if keys != nil {
		key = keys
	}
	if s == "" {
		return &Claims{}, errors.New("token不能为空")
	}
	token, err := jwt.ParseWithClaims(s, &Claims{}, func(token *jwt.Token) (i interface{}, err error) {
		return key, nil
	})
	if err != nil {
		return &Claims{}, err
	}

	if !token.Valid {
		if ve, ok := err.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				err = errors.New("That's not even a token")
			} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
				// Token is either expired or not active yet
				err = errors.New("Timing is everything")
			} else {
				err = errors.New("Couldn't handle this token:")
			}
		} else {
			err = errors.New("Couldn't handle this token:")
		}
		return &Claims{}, err
	}
	claims, ok := token.Claims.(*Claims)
	if  !ok {
		return &Claims{}, errors.New("Couldn't handle this token:")
	}

	return claims, nil
}
  • 整合jwt

services/user/user_service.go

package user

import (
	"cn.sockstack/gin_demo/dto"
	"cn.sockstack/gin_demo/pkg/hash"
	"cn.sockstack/gin_demo/pkg/helper"
	user2 "cn.sockstack/gin_demo/repositories/user"
	"errors"
	"github.com/dgrijalva/jwt-go"
	"strconv"
)

type UserService struct {
}

//Register 注册
func (user UserService) Register(userDto dto.UserDto) error {
	//密码加密
	bytes, err := hash.NewHash().Make([]byte(userDto.Password))
	if err != nil {
		return errors.New(err.Error())
	}
	userDto.Password = string(bytes)
	return user2.CreateUser(userDto)
}

//Login 登陆
func (user UserService)Login(userDto dto.UserDto) (string, error) {
	model := user2.GetUserByUsername(userDto.Username)
	if model.ID == 0 {
		return "", errors.New("账号不存在")
	}

	err := hash.NewHash().Check([]byte(model.Password), []byte(userDto.Password))
	if err != nil {
		return "", errors.New("密码错误")
	}

	//整合jwt
	claims := helper.Claims{
		Username:       model.Username,
		Wid:            strconv.Itoa(int(model.ID)),
		StandardClaims: jwt.StandardClaims{},
	}

	//生成token
	token, err := helper.Encode(claims, helper.Key)
	if err != nil {
		return "", errors.New("token生成失败")
	}

	return token, nil
}
  • 控制器
package v1

import (
	"cn.sockstack/gin_demo/dto"
	"cn.sockstack/gin_demo/pkg/e"
	v1 "cn.sockstack/gin_demo/requests/v1"
	"cn.sockstack/gin_demo/services/user"
	"github.com/gin-gonic/gin"
)

//RegisterHandle 注册
func RegisterHandle(c *gin.Context) (interface{}, error) {
	request := v1.RegisterRequest{}

	err := c.ShouldBind(&request)
	if err != nil {
		return nil, e.ApiError{
			Status:  422,
			Code:    40004,
			Message: "参数校验失败",
			Data: err,
		}
	}

	userDto := dto.UserDto{
		Username: request.Username,
		Password: request.Password,
	}

	service := user.UserService{}
	err = service.Register(userDto)
	if err != nil {
		return nil, e.ApiError{
			Status:  422,
			Code:    40005,
			Message: err.Error(),
		}
	}

	return "注册成功", nil
}

//LoginHandle 登陆
func LoginHandle(c *gin.Context) (interface{}, error) {
	request := v1.LoginRequest{}

	err := c.ShouldBind(&request)
	if err != nil {
		return nil, e.ApiError{
			Status:  422,
			Code:    40004,
			Message: "参数校验失败",
			Data: err,
		}
	}

	userDto := dto.UserDto{
		Username: request.Username,
		Password: request.Password,
	}

	service := user.UserService{}
	token, err := service.Login(userDto)

	if err != nil {
		return nil, e.ApiError{
			Status:  422,
			Code:    40005,
			Message: err.Error(),
		}
	}

	return token, nil
}
  • 定义中间件

middlewares/jwt_check.go

package middlewares

import (
	"cn.sockstack/gin_demo/pkg/e"
	"cn.sockstack/gin_demo/pkg/helper"
	"github.com/gin-gonic/gin"
	"strings"
)

func JwtCheck() gin.HandlerFunc {
	return func(c *gin.Context) {
		header := c.GetHeader("Authorization")
		split := strings.Split(header, " ")
		if split[0] != "Bearer" && split[1] == "" {
			c.JSON(422, e.ApiError{
				Status:  422,
				Code:    40007,
				Message: "不允许访问",
			})
			c.Abort()
			return
		}

		decode, err := helper.Decode(split[1], helper.Key)
		if err != nil {
			c.JSON(422, e.ApiError{
				Status:  422,
				Code:    40008,
				Message: "解析token失败",
			})
			c.Abort()
			return
		}

		c.Set("X-ID", decode.Wid)
		c.Set("X-USERNAME", decode.Username)
		c.Next()
	}
}
  • 路由

routers/v1/user.go

package v1

import (
	v1 "cn.sockstack/gin_demo/api/v1"
	"cn.sockstack/gin_demo/middlewares"
	"cn.sockstack/gin_demo/pkg/e"
	"github.com/gin-gonic/gin"
	"net/http"
)

func UserRouter(r *gin.Engine)  {
	//注册
	r.POST("/register", e.ErrorWrapper(v1.RegisterHandle))

	//登陆
	r.POST("/login", e.ErrorWrapper(v1.LoginHandle))

	//使用JwtCheck中间件测试jwt
	r.GET("/test/jwt", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{"message": "jwt检查通过"})
	}).Use(middlewares.JwtCheck())
}