青训营抖音项目总结|青训营笔记

157 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记。

项目设计

三层架构

Repository层

采用单例模式+工厂模式封装数据库操作对象,特定场景下使用事务确保数据一致性。

Service层

业务逻辑层,执行业务逻辑

Controller层

接口层,使用request结构体接受客户端的请求字段,调用serivce层的相应业务接口,返回response消息给客户端。

技术点

中间件JWT鉴权

Jwt的额外字段为用户名及用户id

type JwtAuth struct {
        Username string `json:"username"`
        Uid      int64  `json:"uid"`
        jwt.StandardClaims
}

可以使用中间件方便地将jwt中的用户名及id存入gin上下文的全局变量中

func AuthMiddleware(auth Auth, optional bool) func(c *gin.Context) {
        return func(c *gin.Context) {
                var token string
                if token = c.Query("token"); len(token) == 0 {
                        token = c.PostForm("token")
                }
                err := auth.ParseToken(token)
                if err != nil && !optional {
                        c.AbortWithStatusJSON(
                                http.StatusUnauthorized,
                                controller.Response{
                                        StatusCode: -1,
                                        StatusMsg:  "Session Expired, Please Relogin.",
                                },
                        )
                        return
                }

                c.Set("username", auth.GetUsername())
                c.Set("uid", auth.GetUid())
                c.Next()
        }
}

如果jwt验证失败,则使用AbortWithStatusJSON方法,不再执行函数链后续的函数,并且返回401 Unauthorized。如果jwt验证成功,则在gin.Context中设置相应的全局变量。同时,该中间件工厂函数接受一个参数optional。如果optional为True,则产生一个非强制性鉴权,此时的鉴权中间件起到如果用户登录,则正常验证,并设置全局变量。如果用户未登录,也允许其访问资源,但是不设置全局变量。

视频封面截取

使用ffmpeg截取视频的第一帧作为封面图片

// ExtractFrame 提取videoPath视频的第n帧保存至framePath中
func ExtractFrame(videoPath string, framePath string) error {
        err := ffmpeg_go.Input(videoPath).
                Output(framePath, ffmpeg_go.KwArgs{
                        "vframes": "1",
                }).
                OverWriteOutput().
                Run()
        return err
}

密码加密

首先为了避免明文保存密码,所以使用哈希算法,仅保存密码的哈希值。保存哈希值后的密码仍有验证功能,但却无法逆向回密码原文。

但是保存哈希值还需要应对彩虹表碰撞问题,有以下对策:

  • 每个密码哈希时使用一随机盐值,尽量使没有两个密码的盐值相同
  • 使用破解成本更高的算法,如在 2015 年获得 Password Hashing Competition 冠军的Argon2算法

本项目使用argon2算法+随机盐值的组合,用户注册时随机生成一个盐值,在存储时与用户名,密码哈希值一起存储至数据库中。生成盐值时,使用随机的UUID v4作为盐值。UUID v4使用密码学安全的随机数生成器CSPRNG进行生成,可以保证随机性和安全性。