// 检查客户端是否是认证状态, 并且不是 guest 认证.
// 如果是, 添加 sso_token_string<-->x-token, sso_token<-->*token.Token, sso_session<-->*session.Session, sso_user<-->*model.User  到 ctx *gin.Context;
// 如果否, 终止 Handlers Chain.
func MustAuthAndNotGuestHandler(ctx *gin.Context) {
	tkString := ctx.Request.Header.Get("x-token")
	if tkString == "" {
		ctx.JSON(200, errors.ErrTokenMissing)
		ctx.Abort()
		return
	}

	// 解析 token 并判断 AuthType 是否正确, 是否过期
	tk := &token.Token{}
	if err := tk.Decode([]byte(tkString)); err != nil {
		glog.Errorln(err)
		ctx.JSON(200, errors.ErrTokenDecodeFailed)
		ctx.Abort()
		return
	}
	if tk.AuthType == token.AuthTypeGuest {
		ctx.JSON(200, errors.ErrTokenShouldNotGuest)
		ctx.Abort()
		return
	}
	if time.Now().Unix() >= tk.ExpirationAccess {
		ctx.JSON(200, errors.ErrTokenAccessExpired)
		ctx.Abort()
		return
	}

	// 获取 Session 并判断与 token 是否匹配
	ss, err := session.Get(tk.SessionId)
	if err != nil {
		glog.Errorln(err)
		if err == errors.ErrNotFound {
			ctx.JSON(200, errors.ErrTokenInvalid)
			ctx.Abort()
			return
		}
		ctx.JSON(200, errors.ErrInternalServerError)
		ctx.Abort()
		return
	}
	if !security.SecureCompareString(tk.Signatrue, ss.TokenSignature) {
		ctx.JSON(200, errors.ErrTokenInvalid)
		ctx.Abort()
		return
	}

	// 获取用户信息并判断与 token, session 是否一致
	user, err := model.Get(ss.UserId)
	if err != nil {
		glog.Errorln(err)
		if err == model.ErrNotFound {
			ctx.JSON(200, errors.ErrTokenInvalid)
			ctx.Abort()
			return
		}
		ctx.JSON(200, errors.ErrInternalServerError)
		ctx.Abort()
		return
	}
	bindType, err := token.GetBindType(tk.AuthType)
	if err != nil {
		glog.Errorln(err)
		ctx.JSON(200, errors.ErrTokenInvalid)
		ctx.Abort()
		return
	}
	if user.BindTypes&bindType == 0 {
		ctx.JSON(200, errors.ErrTokenInvalid)
		ctx.Abort()
		return
	}
	if tk.AuthType == token.AuthTypeEmailPassword || tk.AuthType == token.AuthTypePhonePassword {
		if ss.PasswordTag != user.PasswordTag {
			ctx.JSON(200, errors.ErrTokenInvalid)
			ctx.Abort()
			return
		}
	}

	ctx.Set("sso_token_string", tkString)
	ctx.Set("sso_token", tk)
	ctx.Set("sso_session", ss)
	ctx.Set("sso_user", user)
}
Example #2
0
// 刷新 token
func RefreshHandler(ctx *gin.Context) {
	tkString := ctx.Request.Header.Get("x-token")
	if tkString == "" {
		ctx.JSON(200, errors.ErrTokenMissing)
		return
	}

	// 解析 token
	tk := &token.Token{}
	if err := tk.Decode([]byte(tkString)); err != nil {
		glog.Errorln(err)
		ctx.JSON(200, errors.ErrTokenDecodeFailed)
		return
	}

	// 如果是 guest 直接返回
	if tk.AuthType == token.AuthTypeGuest {
		resp := struct {
			*errors.Error
			Token string `json:"token"`
		}{
			Error: errors.ErrOK,
			Token: tkString,
		}
		ctx.JSON(200, &resp)
		return
	}

	timestamp := time.Now().Unix()
	if timestamp >= tk.ExpirationRefresh {
		ctx.JSON(200, errors.ErrTokenRefreshExpired)
		return
	}
	if timestamp+1200 < tk.ExpirationAccess { // 过早的刷新也是直接返回
		resp := struct {
			*errors.Error
			Token string `json:"token"`
		}{
			Error: errors.ErrOK,
			Token: tkString,
		}
		ctx.JSON(200, &resp)
		return
	}
	if tk.ExpirationAccess >= tk.ExpirationRefresh { // 提前 1200秒 暴力的结束, 防止客户端循环的刷新
		ctx.JSON(200, errors.ErrTokenRefreshExpired)
		return
	}

	// 现在我们真的需要刷新token啦!!!

	// 获取 Session 并判断与 token 是否匹配
	ss, err := session.Get(tk.SessionId)
	if err != nil {
		glog.Errorln(err)
		if err == errors.ErrNotFound {
			ctx.JSON(200, errors.ErrTokenInvalid)
			return
		}
		ctx.JSON(200, errors.ErrInternalServerError)
		return
	}
	if !security.SecureCompareString(tk.Signatrue, ss.TokenSignature) {
		ctx.JSON(200, errors.ErrTokenInvalid)
		return
	}

	// 获取用户信息并判断与 token, session 是否一致
	user, err := model.Get(ss.UserId)
	if err != nil {
		glog.Errorln(err)
		if err == model.ErrNotFound {
			ctx.JSON(200, errors.ErrTokenInvalid)
			return
		}
		ctx.JSON(200, errors.ErrInternalServerError)
		return
	}
	bindType, err := token.GetBindType(tk.AuthType)
	if err != nil {
		glog.Errorln(err)
		ctx.JSON(200, errors.ErrTokenInvalid)
		return
	}
	if user.BindTypes&bindType == 0 {
		ctx.JSON(200, errors.ErrTokenInvalid)
		return
	}
	if tk.AuthType == token.AuthTypeEmailPassword || tk.AuthType == token.AuthTypePhonePassword {
		if ss.PasswordTag != user.PasswordTag {
			ctx.JSON(200, errors.ErrTokenInvalid)
			return
		}
	}

	tk2 := token.Token{
		SessionId:         tk.SessionId,
		TokenId:           token.NewTokenId(),
		AuthType:          tk.AuthType,
		ExpirationAccess:  token.ExpirationAccess(timestamp),
		ExpirationRefresh: tk.ExpirationRefresh,
	}
	if tk2.ExpirationAccess > tk2.ExpirationRefresh {
		tk2.ExpirationAccess = tk2.ExpirationRefresh
	}
	tk2EncodedBytes, err := tk2.Encode()
	if err != nil {
		glog.Errorln(err)
		ctx.JSON(200, errors.ErrTokenEncodeFailed)
		return
	}

	ss.TokenSignature = tk2.Signatrue
	if err = session.Set(tk2.SessionId, ss); err != nil {
		glog.Errorln(err)
		ctx.JSON(200, errors.ErrInternalServerError)
		return
	}

	resp := struct {
		*errors.Error
		Token string `json:"token"`
	}{
		Error: errors.ErrOK,
		Token: string(tk2EncodedBytes),
	}
	ctx.JSON(200, &resp)
	return
}