• gin权限控制JWT结合gin实现权限控制


    一. 如何使用JWT结合gin实现token生成

    1. 创建文件,在mxshop-api/user-web/middlewares 下面添加固定文件

    package middlewares
    
    import (
    	"errors"
    	"github.com/dgrijalva/jwt-go"
    	"github.com/gin-gonic/gin"
    	"mxshop-api/user-web/global"
    	"mxshop-api/user-web/models"
    	"net/http"
    	"time"
    )
    
    func JWTAuth() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localSstorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
    		token := c.Request.Header.Get("x-token")
    		if token == "" {
    			c.JSON(http.StatusUnauthorized, map[string]string{
    				"msg": "请登录",
    			})
    			c.Abort()
    			return
    		}
    		j := NewJWT()
    		// parseToken 解析token包含的信息
    		claims, err := j.ParseToken(token)
    		if err != nil {
    			if err == TokenExpired {
    				if err == TokenExpired {
    					c.JSON(http.StatusUnauthorized, map[string]string{
    						"msg": "授权已过期",
    					})
    					c.Abort()
    					return
    				}
    			}
    
    			c.JSON(http.StatusUnauthorized, "未登陆")
    			c.Abort()
    			return
    		}
    		c.Set("claims", claims)
    		c.Set("userId", claims.ID)
    		c.Next()
    	}
    }
    
    type JWT struct {
    	SigningKey []byte
    }
    
    var (
    	TokenExpired     = errors.New("Token is expired")
    	TokenNotValidYet = errors.New("Token not active yet")
    	TokenMalformed   = errors.New("That's not even a token")
    	TokenInvalid     = errors.New("Couldn't handle this token:")
    )
    
    func NewJWT() *JWT {
    	return &JWT{
    		[]byte(global.ServerConfig.JWTInfo.SigningKey), //可以设置过期时间
    	}
    }
    
    // 创建一个token
    func (j *JWT) CreateToken(claims models.CustomClaims) (string, error) {
    	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    	return token.SignedString(j.SigningKey)
    }
    
    // 解析 token
    func (j *JWT) ParseToken(tokenString string) (*models.CustomClaims, error) {
    	token, err := jwt.ParseWithClaims(tokenString, &models.CustomClaims{}, func(token *jwt.Token) (i interface{}, e 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 token != nil {
    		if claims, ok := token.Claims.(*models.CustomClaims); ok && token.Valid {
    			return claims, nil
    		}
    		return nil, TokenInvalid
    
    	} else {
    		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, &models.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
    		return j.SigningKey, nil
    	})
    	if err != nil {
    		return "", err
    	}
    	if claims, ok := token.Claims.(*models.CustomClaims); ok && token.Valid {
    		jwt.TimeFunc = time.Now
    		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
    		return j.CreateToken(*claims)
    	}
    	return "", TokenInvalid
    }
    

      2. 添加request固定文件

    mxshop-api/user-web/models下面创建request.go文件
    package models
    
    import (
    	"github.com/dgrijalva/jwt-go"
    )
    
    type CustomClaims struct {
    	ID          uint
    	NickName    string
    	AuthorityId uint
    	jwt.StandardClaims
    }
    

      3. 在user.go api接口中的使用

    func PassWordLogin(ctx *gin.Context) {
    	//密码登录的接口
    	//1.表单验证, 在forms中定义
    	//ctx.JSON(http.StatusOK, "密码登录")
    	passWordLoginForm := forms.PassWordLoginForm{}
    
    	//固定格式
    	if err := ctx.ShouldBind(&passWordLoginForm); err != nil {
    		HandleValidatorError(ctx, err)
    		return
    	}
    
    	//拨号连接用户RPC服务
    	userConn, err := grpc.Dial(fmt.Sprintf("%s:%d", global.ServerConfig.UserSrvInfo.Host, global.ServerConfig.UserSrvInfo.Port), grpc.WithTransportCredentials(insecure.NewCredentials()))
    	if err != nil {
    		zap.S().Errorw("[GetUserList]连接失败【用户服务失败】", "msg", err.Error())
    	}
    	//生成grpc的client并调用接口
    	userSrvClient := proto.NewUserClient(userConn)
    
    	//	登录的逻辑, 查询是否存在,密码是否相等
    	if rsp, err := userSrvClient.GetUserByMobile(context.Background(), &proto.MobileRequests{
    		Mobile: passWordLoginForm.Mobile,
    	}); err != nil {
    		if e, ok := status.FromError(err); ok {
    			switch e.Code() {
    			case codes.NotFound:
    				ctx.JSON(http.StatusBadRequest, map[string]string{
    					"mobile": "用户不存在",
    				})
    			default:
    				ctx.JSON(http.StatusBadRequest, map[string]string{
    					"mobile": "登录失败",
    				})
    			}
    			return
    		}
    	} else {
    		//	只是查询了用户而已,并没有检查密码
    		if passRsp, pasErr := userSrvClient.CheckPassWord(context.Background(), &proto.PasswordCheckInfo{
    			Password:          passWordLoginForm.PassWord,
    			EncryptedPassword: rsp.PassWord,
    		}); pasErr != nil {
    			ctx.JSON(http.StatusInternalServerError, map[string]string{
    				"password": "登录失败",
    			})
    		} else {
    			if passRsp.Success {
    				//生成token
    				j := middlewares.NewJWT()
    				claims := models.CustomClaims{
    					ID:          uint(rsp.Id),
    					NickName:    rsp.NickName,
    					AuthorityId: uint(rsp.Role),
    					StandardClaims: jwt.StandardClaims{
    						NotBefore: time.Now().Unix(),               //签名的生效时间
    						ExpiresAt: time.Now().Unix() + 60*60*24*30, //30day过期
    						Issuer:    "wanghui",
    					},
    				}
    				token, err := j.CreateToken(claims)
    				if err != nil {
    					ctx.JSON(http.StatusInternalServerError, map[string]string{
    						"msg": "生成token失败",
    					})
    					return
    				}
    
    				ctx.JSON(http.StatusOK, gin.H{
    					"id":         rsp.Id,
    					"nick_name":  rsp.NickName,
    					"token":      token,
    					"expired_at": time.Now().Unix() + 60*60*24*30*1000,
    				})
    			} else {
    				ctx.JSON(http.StatusBadRequest, map[string]string{
    					"msg": "登录失败",
    				})
    			}
    
    		}
    
    	}
    }
    

      

     

    二. 如何使用JWT结合gin实现URL权限控制

    1.如上已经配置了JWTAuth方法,直接使用即可

    2.在路由分组中使用

    增加.Use(middlewares.JWTAuth())

    例如:

    package router
    
    import (
    	"github.com/gin-gonic/gin"
    	"mxshop-api/user-web/api"
    	"mxshop-api/user-web/middlewares"
    )
    
    func InitUserRoute(Router *gin.RouterGroup) {
    	UserRouter := Router.Group("user").Use(middlewares.JWTAuth())
    	{
    		UserRouter.GET("list", api.GetUserList)
    		UserRouter.POST("pwd_login", api.PassWordLogin)
    
    	}
    }
    

      

    2.在单个URL使用,控制权限

    例如:

    package router
    
    import (
    	"github.com/gin-gonic/gin"
    	"mxshop-api/user-web/api"
    	"mxshop-api/user-web/middlewares"
    )
    
    func InitUserRoute(Router *gin.RouterGroup) {
    	UserRouter := Router.Group("user")
    	{
    		UserRouter.GET("list", middlewares.JWTAuth(), api.GetUserList)
    		UserRouter.POST("pwd_login", api.PassWordLogin)
    
    	}
    }
    

      

     3. 获取当前用户

    我们怎么拿到当前用户的ID信息呢

    在上面的文件上,已经可以对用户信息放到gin全局变量中

    c.Set("claims", claims)
    c.Set("userId", claims.ID)

    func JWTAuth() gin.HandlerFunc {
    	return func(c *gin.Context) {
    		// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localSstorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
    		token := c.Request.Header.Get("x-token")
    		if token == "" {
    			c.JSON(http.StatusUnauthorized, map[string]string{
    				"msg": "请登录",
    			})
    			c.Abort()
    			return
    		}
    		j := NewJWT()
    		// parseToken 解析token包含的信息
    		claims, err := j.ParseToken(token)
    		if err != nil {
    			if err == TokenExpired {
    				if err == TokenExpired {
    					c.JSON(http.StatusUnauthorized, map[string]string{
    						"msg": "授权已过期",
    					})
    					c.Abort()
    					return
    				}
    			}
    
    			c.JSON(http.StatusUnauthorized, "未登陆")
    			c.Abort()
    			return
    		}
    		c.Set("claims", claims)
    		c.Set("userId", claims.ID)
    		c.Next()
    	}
    }
    

      

    三. 如何使用JWT结合gin实现admin权限分级

    1.在middlewares上创建admin.go文件

    package middlewares
    
    import (
    	"github.com/gin-gonic/gin"
    	"mxshop-api/user-web/models"
    	"net/http"
    )
    
    func IsAdminAuth() gin.HandlerFunc {
    	return func(ctx *gin.Context) {
    		claims, _ := ctx.Get("claims")
    		currentUser := claims.(*models.CustomClaims)
    
    		if currentUser.AuthorityId != 2 {
    			//	非管理员
    			ctx.JSON(http.StatusForbidden, gin.H{
    				"msg": "无权限",
    			})
    			ctx.Abort()
    			return
    		}
    		ctx.Next()
    	}
    }
    

      

    2.在路由中使用

    package router
    
    import (
    	"github.com/gin-gonic/gin"
    	"mxshop-api/user-web/api"
    	"mxshop-api/user-web/middlewares"
    )
    
    func InitUserRoute(Router *gin.RouterGroup) {
    	UserRouter := Router.Group("user")
    	{
    		UserRouter.GET("list", middlewares.JWTAuth(), middlewares.IsAdminAuth(), api.GetUserList)
    		UserRouter.POST("pwd_login", api.PassWordLogin)
    
    	}
    }
    

      

     

  • 相关阅读:
    软件工程(2019)第二次作业
    软件工程(2019)第一次作业
    Java基础篇之Java特性
    软件工程(2019)结对编程第二次作业
    软件工程(2019)结对编程第一次作业
    软件工程(2019)第三次作业
    软件工程(2019)第二次作业
    软件工程(2019)第一次作业
    结对编程作业 2
    结对编程作业 1
  • 原文地址:https://www.cnblogs.com/wlike/p/16847767.html
Copyright © 2020-2023  润新知