package service import ( "encoding/base64" "encoding/json" "github.com/gin-gonic/gin" "net/http" "texas-poker-bk/internal/conf" "texas-poker-bk/internal/model/entity" "texas-poker-bk/internal/session" "texas-poker-bk/tool/collect" "texas-poker-bk/tool/security" "time" ) // http: 登录注册、创建房间 // ws: 大厅、房间、房间状态 var ( SubjectKey = "auth:session" aesTokenKeyBytes []byte // token aes 加密 key ) // Authorize 登录或注册 func Authorize(ctx *gin.Context) { username := ctx.PostForm("username") password := ctx.PostForm("password") //username := ctx.Param("username") //password := ctx.Param("password") if username == "" || password == "" { ctx.JSON(http.StatusBadRequest, gin.H{"message": "用户名或密码不能为空"}) return } user := userDao.FindUserByName(username) if user == nil { // 注册 user = registerUser(username, password) } else { // 校验密码 if user.Password != password { ctx.JSON(http.StatusUnauthorized, gin.H{"message": "用户名或密码有误"}) return } } sub := &session.Subject{Id: user.Id, Name: user.Username, Time: time.Now().UnixMilli()} token, err := EncodeSubject(sub) if err != nil { ctx.JSON(http.StatusUnauthorized, gin.H{"message": err.Error()}) return } ctx.JSON(http.StatusOK, gin.H{"data": gin.H{"token": token}}) } // 注册用户到DB func registerUser(username string, password string) *entity.User { user := &entity.User{ Username: username, Password: password, Nickname: username, } // TODO error userDao.dao.Save(user) return user } func init() { keyBytes, err := base64.StdEncoding.DecodeString(conf.Conf.Auth.AesTokenKey) if err != nil { panic(err) } aesTokenKeyBytes = keyBytes } // SubjectAuthFilter 认证过滤器 func SubjectAuthFilter(ctx *gin.Context) { // 检查是不需要登录的 url // ignore auth urls if collect.IsNotEmptySlice(conf.Conf.Auth.IgnoreUrl) { for _, ignoreUrl := range conf.Conf.Auth.IgnoreUrl { if ignoreUrl == ctx.Request.URL.Path { return } } } auth := ctx.GetHeader("Authorization") if auth == "" { ctx.JSON(http.StatusUnauthorized, gin.H{"message": "请登录后进行操作"}) ctx.Abort() } defer func() { // 捕获aes解析错误 if r := recover(); r != nil { ctx.JSON(http.StatusUnauthorized, gin.H{"message": "认证失败,请重新登录"}) ctx.Abort() } }() // 这里返回 error,不然后捕获后续执行 handler 的 panic subject, err := DecodeSubject(auth) if err != nil { ctx.JSON(http.StatusUnauthorized, gin.H{"message": "认证失败,请重新登录"}) ctx.Abort() } else { // 设置token用户到请求上下文 ctx.Set(SubjectKey, subject) } } // EncodeSubject 对subject对象aesCBC加密并返回base64Std编码的 token // session 客户端对象 func EncodeSubject(subject *session.Subject) (string, error) { bytes, err := json.Marshal(subject) if err != nil { return "", err } encode, err := security.EncryptAesCBC(bytes, aesTokenKeyBytes) if err != nil { return "", err } return base64.URLEncoding.EncodeToString(encode), nil } // DecodeSubject 解码并解密token返回subject对象 // auth base64Std 编码的 token func DecodeSubject(auth string) (*session.Subject, error) { bytes, err := base64.URLEncoding.DecodeString(auth) if err != nil { return nil, err } decode := security.DecryptAesCBC(bytes, aesTokenKeyBytes) subject := &session.Subject{} err = json.Unmarshal(decode, subject) if err != nil { return nil, err } return subject, nil } // GetSubject 从请求上下文中获取客户端对象, 请求经过了认证过滤器 func GetSubject(ctx *gin.Context) *session.Subject { subject, exists := ctx.Get(SubjectKey) if !exists { panic("request subject not exists!") } return subject.(*session.Subject) }