// 跳转后的页面请求处理. // redirectURL?code=CODE&state=STATE // 授权 // redirectURL?state=STATE // 不授权 func RedirectURLHandler(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { // TODO: 处理 error return } state := r.FormValue("state") // TODO: 处理这个 state 参数, 判断是否是有效的 _ = state // NOTE: 实际开发中不要有这个 // 假定 state 有效 if code := r.FormValue("code"); code != "" { // 授权 client := oauth2.Client{ OAuth2Config: oauth2Config, } info, err := client.Exchange(code) if err != nil { // TODO: ... return } // TODO: 这里把 info 根据 info.OpenId 缓存起来,以后可以直接用 // 做相应的 session 处理。 _ = info // NOTE: 示例代码 } else { // 不授权 // TODO: 不授权的相应代码 } }
// 在需要用户授权的时候引导用户到授权页面 func SomeHandler(w http.ResponseWriter, r *http.Request) { var hasAuth bool // 判断是否授权 // TODO: 判断 session 里是否有 openid 字段,如果有则表明已经授权,没有则没有授权 if hasAuth { var info *oauth2.OAuth2Info // TODO: 根据 openid 字段 找到 info(type == *oauth2.OAuth2Info) client := oauth2.Client{ OAuth2Config: oauth2Config, OAuth2Info: info, } // 可以根据这个 info 做相应的操作,比如下面的获取用户信息 userinfo, err := client.UserInfo("zh_CN") if err != nil { // TODO: ... return } // 处理 userinfo _ = userinfo // NOTE: 实际开发中可不是这样的 } else { var state = "state" // NOTE: 实际上是一串不可预测的随机数 // TODO: state 做相应处理,好识别下次跳转回来的 state 参数 http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) } }
// 微信 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 } }