// 认证成功后创建 token 和 session func authSuccess(ctx *gin.Context, authType string, user *model.User) { sid, err := session.NewSessionId() if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } timestamp := time.Now().Unix() tk := token.Token{ SessionId: sid, TokenId: token.NewTokenId(), AuthType: authType, ExpirationAccess: token.ExpirationAccess(timestamp), ExpirationRefresh: token.ExpirationRefresh(timestamp), } tkEncodedBytes, err := tk.Encode() if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrTokenEncodeFailed) return } ss := session.Session{ TokenSignature: tk.Signatrue, UserId: user.Id, PasswordTag: user.PasswordTag, } if err = session.Add(sid, &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(tkEncodedBytes), } ctx.JSON(200, &resp) return }
func authGuest(ctx *gin.Context, queryValues url.Values) { sid, err := session.NewGuestSessionId() if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } tk := token.Token{ SessionId: sid, TokenId: token.NewTokenId(), AuthType: AuthTypeGuest, ExpirationAccess: 0, ExpirationRefresh: 0, } tkEncodedBytes, err := tk.Encode() if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrTokenEncodeFailed) return } ss := session.Session{ TokenSignature: tk.Signatrue, } if err = session.Add(sid, &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(tkEncodedBytes), } ctx.JSON(200, &resp) return }
// 刷新 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 }
// 微信 oauth2 认证 // 需要提供 code, state 参数. func AuthHandler(ctx *gin.Context) { // MustAuthHandler(ctx) queryValues := ctx.Request.URL.Query() code := queryValues.Get("code") if code == "" { ctx.JSON(200, errors.ErrBadRequest) return } state := queryValues.Get("state") if state == "" { ctx.JSON(200, errors.ErrBadRequest) return } ss := ctx.MustGet("sso_session").(*session.Session) // 比较 state 是否一致 if !security.SecureCompareString(state, ss.OAuth2State) { ctx.JSON(200, errors.ErrOAuthStateMismatch) return } oauth2Client := oauth2.Client{ Config: oauth2Config, } oauth2Token, err := oauth2Client.Exchange(code) if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } if oauth2Token.UnionId == "" { glog.Errorln("unionid is empty") ctx.JSON(200, errors.ErrInternalServerError) return } timestamp := time.Now().Unix() user, err := model.GetByWechat(oauth2Token.UnionId) switch err { default: glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return case model.ErrNotFound: var oauth2UserInfo openOAuth2.UserInfo if err = oauth2Client.GetUserInfo(&oauth2UserInfo, ""); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } user, err = model.AddByWechat(oauth2Token.UnionId, oauth2UserInfo.Nickname, timestamp) if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } fallthrough case nil: sid, err := session.NewSessionId() if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } tk2 := token.Token{ SessionId: sid, TokenId: token.NewTokenId(), AuthType: token.AuthTypeOAuthWechat, ExpirationAccess: token.ExpirationAccess(timestamp), ExpirationRefresh: token.ExpirationRefresh(timestamp), } tk2EncodedBytes, err := tk2.Encode() if err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrTokenEncodeFailed) return } ss2 := session.Session{ TokenSignature: tk2.Signatrue, UserId: user.Id, PasswordTag: user.PasswordTag, } if err = session.Add(sid, &ss2); 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 } }