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
}
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
}
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里面
里面需要密码未加密需要优化,也欢迎其他能人志士指出其他错误,非常感谢,一起成长!!
有问题,可以加微信:cfun666,进go语言学习交流群