// 获取微信认证页面的 url // 需要提供 redirect_uri func AuthURLHandler(ctx *gin.Context) { // MustAuthHandler(ctx) queryValues := ctx.Request.URL.Query() redirectURI := queryValues.Get("redirect_uri") if redirectURI == "" { ctx.JSON(200, errors.ErrBadRequest) return } if !strings.HasPrefix(redirectURI, "https:") && !strings.HasPrefix(redirectURI, "http:") { redirectURI = config.ConfigData.WebServer.BaseURL + redirectURI } tk := ctx.MustGet("sso_token").(*token.Token) ss := ctx.MustGet("sso_session").(*session.Session) ss.OAuth2State = string(random.NewRandomEx()) if err := session.Set(tk.SessionId, ss); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } authURL := oauth2.AuthCodeURL(config.ConfigData.Weixin.MP.AppId, redirectURI, "snsapi_userinfo", ss.OAuth2State, nil) resp := struct { *errors.Error URL string `json:"url"` }{ Error: errors.ErrOK, URL: authURL, } ctx.JSON(200, &resp) return }
// 获取请求用户授权的参数(appid, state, scope) func AuthParaHandler(ctx *gin.Context) { // MustAuthHandler(ctx) tk := ctx.MustGet("sso_token").(*token.Token) ss := ctx.MustGet("sso_session").(*session.Session) ss.OAuth2State = string(random.NewRandomEx()) if err := session.Set(tk.SessionId, ss); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } resp := struct { *errors.Error AppId string `json:"appid"` State string `json:"state"` Scope string `json:"scope"` }{ Error: errors.ErrOK, AppId: config.ConfigData.Weixin.Open.App.AppId, State: ss.OAuth2State, Scope: "snsapi_userinfo", } ctx.JSON(200, &resp) return }
// 申请发送一个校验码到邮箱. // uri?email=XXX func RequestForEmailHandler(ctx *gin.Context) { // MustAuthHandler(ctx) queryValues := ctx.Request.URL.Query() email := queryValues.Get("email") if email == "" { ctx.JSON(200, errors.ErrBadRequest) return } if !check.IsMailString(email) { ctx.JSON(200, errors.ErrBadRequest) return } tk := ctx.MustGet("sso_token").(*token.Token) ss := ctx.MustGet("sso_session").(*session.Session) code := generateCode() checkcode := session.CheckCode{ Key: email, Code: code, Times: 0, } ss.EmailCheckCode = &checkcode if err := session.Set(tk.SessionId, ss); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } if err := sendCodeToEmail(email, code); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } ctx.JSON(200, errors.ErrOK) return }
// 申请发送一个校验码到手机. // uri?phone=XXX func RequestForPhoneHandler(ctx *gin.Context) { // MustAuthHandler(ctx) queryValues := ctx.Request.URL.Query() phone := queryValues.Get("phone") if phone == "" { ctx.JSON(200, errors.ErrBadRequest) return } if !check.IsChinaMobileString(phone) { ctx.JSON(200, errors.ErrBadRequest) return } tk := ctx.MustGet("sso_token").(*token.Token) ss := ctx.MustGet("sso_session").(*session.Session) code := generateCode() checkcode := session.CheckCode{ Key: phone, Code: code, Times: 0, } ss.PhoneCheckCode = &checkcode if err := session.Set(tk.SessionId, ss); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } if err := sendCodeToPhone(phone, code); err != nil { glog.Errorln(err) ctx.JSON(200, errors.ErrInternalServerError) return } ctx.JSON(200, errors.ErrOK) 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 }