AWD平台2-配置读取和jwt鉴权模块

配置读取和jwt鉴权模块

配置读取

数据库这里的设置写死在代码里肯定是不行的,我们写到一个配置文件中去,然后去解析它。后续上传到仓库中的时候用gitignore就行了

通过通过viper库可以轻松完成配置读取github.com/spf13/viper

在启动时调用这个函数,就回去读取config包下的config.yml

// 初始化设置
func InitConfig() {
	workDir, _ := os.Getwd()
	viper.SetConfigName("config")
	viper.SetConfigType("yml")
	viper.AddConfigPath(workDir + "/config")
	err := viper.ReadInConfig()
	if err != nil {
		panic(err)
	}
}

获取配置项

这个函数是数据库模块的初始化过程,通过viper.Getxxx读取配置

func InitDB() {
	host := viper.GetString("datasource.host")
	port := viper.GetString("datasource.port")
	database := viper.GetString("datasource.database")
	username := viper.GetString("datasource.username")
	pwd := viper.GetString("datasource.pwd")
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		username, pwd, host, port, database)

	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	DB = db

	// 初始化一个管理员进去
	var admin model.Admin
	db.Where("name = ?", "admin").First(&admin)
	if admin.ID == 0 {
		admin.Name = "admin"
		admin.Pwd = auth.Encode("123456")
		db.Create(&admin)
	}
}

JWT鉴权

jwt是啥

jwt 是 json web token的一个简写,是一个标准,可以有多种实现,他的作用是做身份认证

jwt包含三部分,header、payload、signature

header指定用什么算法加密,payload防有效信息,signature是一个签证信息。

使用过程

客户端带着密码请求服务端后,服务端生成token发给客户端,由客户端保存,客户端之后的请求都带上这个token,供服务端验证身份。

项目中的使用

go标准库中没有jwt模块,我们使用github.com/dgrijalva/jwt-go 这个库

发放token

var jwtKey = []byte("a_secret_crect")

//两种角色,0代表是队伍,1代表是管理员
const TEAM uint8 = 0
const ADMIN uint8 = 1

type Claims struct {
	ID   uint
	Role uint8
	jwt.StandardClaims
}

func ReleaseToken(id uint, role uint8) (string, error) {
	validity := viper.GetDuration("auth.vilidity")              // 有效期写在config.yml里,通过viper读取
	expirationTime := time.Now().Add(validity * time.Hour)      //指定有效时间
	if role != TEAM && role != ADMIN {
		return "", errors.New("角色类型不正确")
	}
	claims := &Claims{
		ID:   id,
		Role: role,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expirationTime.Unix(),
			IssuedAt:  time.Now().Unix(),                           //这块的画,去查文档好了
			Issuer:    "evo",
			Subject:   "token",
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)      //这里用的是HMAC加密的方法,这个方法就够用了
	tokenString, err := token.SignedString(jwtKey)

	if err != nil {
		return "", err
	} else {
		return tokenString, nil
	}
}

解析

解析部分的代码就和能简单,到时候拿到这个token和claims去验证就行。

func ParseToken(tokenString string) (*jwt.Token, *Claims, error) {
	claims := &Claims{}

	token, err := jwt.ParseWithClaims(tokenString, claims,
		func(token *jwt.Token) (i interface{}, err error) {
			return jwtKey, nil
		})
	return token, claims, err
}

写了一个中间件

我这边写了一个中间件去鉴权,因为claims中我放了Role这个定义,然后我们的接口约定好了的,队伍相关的/team开头,管理员相关的是/manager开头,所以这里通过URL.PATH去判断,jwt那边通过再符合角色就能通过

func AuthMW() gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenStr := c.GetHeader("Authorization")
		if tokenStr == "" || !strings.HasPrefix(tokenStr, "Bearer") {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code": 401,
				"msg":  "权限不足",
			})
			c.Abort()
			return
		}
		tokenStr = tokenStr[7:]
		//没带token
		token, claims, err := auth.ParseToken(tokenStr)
		if err != nil || !token.Valid {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code": 401,
				"msg":  "权限不足",
			})
			// Abort 函数会终端这次请求,后面的函数不会再被调用
			c.Abort()
			return
		}
		//拿到url中的路径,判断是对选手端还是对管理端的请求
		url := c.Request.URL.Path
		var role uint8
		if strings.HasPrefix(url, "/team") {
			role = auth.TEAM
		} else {
			role = auth.ADMIN
		}
		if claims.Role != role {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code": 401,
				"msg":  "权限不足",
			})
		}
		c.Set("teamId", claims.ID)                  //把id放进上下文里,方便之后用
		c.Next()
	}
}

补充 gin的中间件

中间件的实现原理

这个原理其实不复杂,就是一个请求给你一个context,然后这个 context中有一个或者多个gin.HandlerFunc类型的方法,一次请求会依次执行这些方法(这些方法都被放在一个切片中)然后context可以控制他们的执行

上文中c.Next()就是这个中间件处理完了,交给下一个搞,c.Abort()用来打断这个传递的过程,c.Set()方法就用来传递数据,很方便。

源码:

image-20220711212037027

image-20220711212046369

posted @ 2022-07-11 21:23  博客是个啥?  阅读(189)  评论(3编辑  收藏  举报