登陆接口

登陆验证,在web开发中算是很常见的东西了,关于这个东西,也不做过多解释了,我这里并没有使用cookie和session,使用了jwt,关于这个,在go官方的网址,下载即可

go get github.com/dgrijalva/jwt-go

步骤也很简单,用密钥加密,然后返回给前端,前端访问其他接口的时候,通过中间件验证此token来确定用户身份。

所以,我们需要写一个加密函数。在middleware/jwt.go。首先去随便写一个jwt的key,也就是加密密钥,在config.ini中添加即可。然后和其他参数一样,在settings中解析。

/*
config.ini
*/
[server]
JwtKey = +d0(4=-$hy(cdl$tu^@509r#t$e60-1&v$
var JwtKey = []byte(utils.JwtKey)

var code int

type MyClaims struct {
	Username string `json:"username"`
	jwt.StandardClaims
}

// 生成token
func SetToken(username string) (string, int) {
	// 过期时间
	expirTime := time.Now().Add(10 * time.Hour)
	SetClaims := MyClaims{
		username,
		jwt.StandardClaims{
			// 过期时间
			ExpiresAt: expirTime.Unix(),
			// 签发人
			Issuer: "Salmon",
		},
	}
	// 参数:签发方法,签发命令
	reClaim := jwt.NewWithClaims(jwt.SigningMethodHS256, SetClaims)
	// 加签
	token,err := reClaim.SignedString(JwtKey)
	if err != nil{
		return "",errmsg.ERROR
	}
	return token,errmsg.SUCCSE
}

然后就是解密函数

// 验证token
func CheckToken(token string) (*MyClaims, int) {
	settoken,_ := jwt.ParseWithClaims(token, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return JwtKey,nil
	})
	if key,_ := settoken.Claims.(*MyClaims); settoken.Valid{
		return key,errmsg.SUCCSE
	}else {
		return nil,errmsg.ERROR
	}

}

最后把他写成一个中间件。

// jwt中间件
func JwtToken() gin.HandlerFunc{
	return func(c *gin.Context) {
    // 固定格式
		tokenHeader := c.Request.Header.Get("Authorizathion")
    // 如果没有带token,则返回一个错误。
		if tokenHeader == ""{
			code = errmsg.ERROR_TOKEN_EXIST
			//c.Abort()
		}
    // 分割参数
		checkToken := strings.SplitN(tokenHeader, " ", 2)
		if len(checkToken) != 2 && checkToken[0] != "Bearer" {
			code = errmsg.ERROR_TOKEN_TYPE_WRONG
			c.Abort()
		}
    // 解析token方法
		key,Tcode := CheckToken(checkToken[1])
		if Tcode == errmsg.ERROR {
			code = errmsg.ERROR_TOKEN_WRONG
			c.Abort()
		}
    // 如果过期,则返回错误
		if time.Now().Unix() > key.ExpiresAt{
			code = errmsg.ERROR_TOKEN_RUNTIME
			c.Abort()
		}
		c.JSON(http.StatusOK, gin.H{
			"code":code,
			"msg":errmsg.GetErrorMsg(code),
		})
		c.Set("username", key.Username)
		c.Next()
	}
}

API接口:

func Login(c *gin.Context)  {
	var formData model.User
	_ = c.ShouldBindJSON(&formData)
	var token string
	var code int
	// 验证用户名密码
	formData, code = model.CheckLogin(formData.Username, formData.Password)
	if code == errmsg.SUCCSE{
		// 成功则签发token
		token, code = middleware.SetToken(formData.Username)
	}
	c.JSON(http.StatusOK, gin.H{
		"status":  code,
		"message": errmsg.GetErrorMsg(code),
		"token": token,
	})
}

最后把路由组给搞一下,注册一下中间件routes/router.go

package routes

import (
	v1 "blog/api/v1"
	"blog/middleware"
	"blog/utils"
	"github.com/gin-gonic/gin"
)

func InitRouter()  {
	gin.SetMode(utils.AppMode)
	r := gin.Default()

	Auth := r.Group("")
	Auth.Use(middleware.JwtToken())
	{
		// User模块的路由接口
		Auth.PUT("user/:id/", v1.EditUser)
		Auth.DELETE("user/:id/", v1.DeleteUser)
		// Category模块的路由接口
		Auth.POST("category/", v1.AddCategory)
		Auth.PUT("category/:id/", v1.EditCategory)
		Auth.DELETE("category/:id/", v1.DeleteCategory)
		// Article模块的路由接口
		Auth.POST("article/", v1.AddArticle)
		Auth.PUT("article/:id/", v1.EditArticle)
		Auth.DELETE("article/:id/", v1.DeleteArticle)
	}

	router := r.Group("api/v1")
	{
		router.GET("user/", v1.GetUsers)
		Auth.POST("user/", v1.AddUser)
		router.POST("login/", v1.Login)
		router.GET("category/", v1.GetCategorys)
		router.GET("category/:id/", v1.GetCateInfo)
		router.GET("article/", v1.GetArticles)
		router.GET("article/:id/", v1.GetArticleInfo)
		router.GET("catelist/:id/", v1.GetCateArt)
	}


	_ = r.Run(utils.HttpPort)

}

文件上传

关于文件上传,我选择使用seaweedfs,这是一个很牛掰的文件存储系统,并且他也是使用go写的,所以,都是一家人,地址,最后再使用docker一部署,go的一揽子服务。奈斯。

关于部署seaweedfs和连接,请查看拙作seaweedfs部署详解+部署生产,这里的函数等都是我搭配着gin来使用的,在耦合方面没有什么难度。

package v1

import (
	"blog/utils"
	"blog/utils/errmsg"
	"blog/utils/goseaweed"
	"bytes"
	"github.com/gin-gonic/gin"
	"io"
	"log"
	"net/http"
	"time"
)

// 上传文件
func UploadFile(c *gin.Context){
	file, _, _ := c.Request.FormFile("file")
	// 初始化
	fs := goseaweed.NewSeaweedFs(utils.SeaweedAddress, time.Second * 10)
	code = errmsg.SUCCSE
	// 转换数据类型为bytes
	buf := bytes.NewBuffer(nil)
	if _, err := io.Copy(buf, file); err != nil {
		code = errmsg.ERROR
	}
	fid,err := fs.UploadFile("submit",buf.Bytes())
	// 上传文件
	if  err != nil {
		log.Fatalln(err)
	}
	c.JSON(http.StatusOK,gin.H{
		"code":code,
		"msg": errmsg.GetErrorMsg(code),
		"data": fid,
	})
}

后端接口方面还有诸多问题,不过我以实现功能为目的,先暂时这样,之后再慢慢补充。

未完待续….