尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

OAuth2.0集成:第三方登录

OAuth2.0集成:第三方登录 # OAuth2.0集成:第三方登录摘要:OAuth2.0第三方登录完整实现涵盖授权码流程原理、golang.org/x/oauth2库使用、GitHub登录集成、State参数防CSRF附回调地址匹配踩坑经验。产品经理有天突然找我说用户反馈注册流程太麻烦要求加GitHub和Google登录。竞品都有这个功能了我们也要搞。原话就是这样。第三方登录确实能降低注册门槛OAuth2.0流程也不算复杂。但第一次接的时候有些细节容易忽略我自己踩了几个坑。今天把完整的实现方案讲一遍。OAuth2.0授权码流程OAuth2.0最常用的是授权码模式Authorization Code流程分四步。第一步用户点击GitHub登录跳转到GitHub授权页面。第二步用户同意授权GitHub回调你的应用并带上code。第三步你的服务端用code向GitHub换access_token。第四步用access_token调GitHub API获取用户信息。为什么要有code这一步不直接返回token因为code通过浏览器跳转传递暴露在前端。code只能用一次而且需要client_secret才能换token。client_secret只存在服务端不会泄露。GitHub登录实现Go里用golang.org/x/oauth2库官方维护。packagemainimport(contextcrypto/randencoding/hexencoding/jsonfmtionet/httposgolang.org/x/oauth2golang.org/x/oauth2/github)// GitHub用户信息结构typeGitHubUserstruct{IDint64json:id// GitHub用户IDLoginstringjson:login// GitHub用户名Namestringjson:name// 显示名称AvatarURLstringjson:avatar_url// 头像URL}// OAuth2配置ClientID和Secret从环境变量读取varoauthConfigoauth2.Config{ClientID:os.Getenv(GITHUB_CLIENT_ID),ClientSecret:os.Getenv(GITHUB_CLIENT_SECRET),Scopes:[]string{user:email},// 申请的权限范围Endpoint:github.Endpoint,// GitHub的OAuth端点RedirectURL:http://localhost:8080/auth/callback,// 回调地址}// 登录入口重定向到GitHub授权页面funcloginHandler(w http.ResponseWriter,r*http.Request){// 生成随机state防止CSRF攻击state:generateState()// state存入Cookie回调时验证http.SetCookie(w,http.Cookie{Name:oauth_state,Value:state,Path:/,HttpOnly:true,MaxAge:300,// 5分钟有效})// 重定向到GitHub授权页面http.Redirect(w,r,oauthConfig.AuthCodeURL(state),http.StatusTemporaryRedirect)}// GitHub回调处理funccallbackHandler(w http.ResponseWriter,r*http.Request){// 验证state防止CSRF攻击cookie,err:r.Cookie(oauth_state)state:r.URL.Query().Get(state)iferr!nil||state!cookie.Value{http.Error(w,state验证失败,http.StatusBadRequest)return}// 用授权码换取access token服务端到服务端secret不暴露code:r.URL.Query().Get(code)token,err:oauthConfig.Exchange(context.Background(),code)iferr!nil{http.Error(w,换取Token失败,http.StatusInternalServerError)return}// 用token获取GitHub用户信息user,err:getGitHubUser(token.AccessToken)iferr!nil{http.Error(w,获取用户信息失败,http.StatusInternalServerError)return}// 实际项目应该签发自己的JWT这里简化输出fmt.Fprintf(w,登录成功! 用户: %s, ID: %d,user.Name,user.ID)}// getGitHubUser 用token调GitHub API获取用户信息funcgetGitHubUser(accessTokenstring)(*GitHubUser,error){req,_:http.NewRequest(GET,https://api.github.com/user,nil)req.Header.Set(Authorization,token accessToken)// 带上tokenresp,err:http.DefaultClient.Do(req)iferr!nil{returnnil,err}deferresp.Body.Close()user:GitHubUser{}json.NewDecoder(resp.Body).Decode(user)returnuser,nil}// generateState 生成随机state字符串funcgenerateState()string{b:make([]byte,16)io.ReadFull(rand.Reader,b)// crypto/rand生成安全随机数returnhex.EncodeToString(b)}funcmain(){http.HandleFunc(/auth/login,loginHandler)http.HandleFunc(/auth/callback,callbackHandler)fmt.Println(服务启动在 :8080)http.ListenAndServe(:8080,nil)}安全防护要点OAuth2.0集成有三个安全点必须注意。State参数防CSRF。攻击者构造一个携带自己code的回调URL诱导用户点击。没有state验证的话服务端会用攻击者的code换token把攻击者的GitHub账号绑定到受害者会话上。state是随机生成的存Cookie里回调时验证一致性。PKCE增强安全。PKCEProof Key for Code Exchange在移动端和SPA场景下特别重要防止授权码被截获。golang.org/x/oauth2支持PKCE调用AuthCodeURL时传入oauth2.GenerateVerifier()即可。HTTPS传输。生产环境回调地址必须用HTTPS。GitHub从2020年起强制要求OAuth回调地址是HTTPSlocalhost除外。Token走HTTP传输会被中间人截获。独家踩坑:回调地址不匹配这个坑花了我一个下午。本地开发一切正常部署到测试环境后GitHub登录一直报redirect_uri_mismatch。GitHub OAuth App里配的回调地址是http://localhost:8080/auth/callback代码里RedirectURL也是这个。排查了很久才发现测试环境通过Nginx反向代理外部访问的是https://test.example.com但代码里RedirectURL还是localhost。GitHub收到授权请求后会比较请求中的redirect_uri和OAuth App注册的回调地址不一致就拒绝。// 问题代码硬编码了localhostvarconfigoauth2.Config{RedirectURL:http://localhost:8080/auth/callback,}// 修复方案从环境变量读取varconfigoauth2.Config{RedirectURL:os.Getenv(OAUTH_REDIRECT_URL),}还有一个更隐蔽的坑。GitHub对回调地址是精确匹配不支持通配符。注册的是http://localhost:8080/auth/callback请求时传http://localhost:8080/auth/callback/多了个斜杠也会报mismatch。浏览器有时自动补斜杠导致莫名其妙的失败。解决办法是注册地址和代码里传的地址保持严格一致连末尾斜杠都不能差。对比分析方案协议复杂度安全性用户体验适用场景OAuth2.0高高好第三方登录SAML很高高中企业SSOOIDC中高好身份认证自建账号低中一般独立应用OAuth2.0是授权协议OIDC是认证协议。OAuth2.0解决用户授权第三方应用访问自己的数据OIDC在此基础上解决证明用户是谁。实际做第三方登录用OAuth2.0获取用户信息后在服务端建立会话也够用。Go的oauth2库比Python的authlib简洁比Java的Spring Security OAuth2轻量很多。Spring Security OAuth2功能全面但学习曲线陡峭配置一个完整的OAuth2客户端要写一堆配置类。Go这边几十行代码就能跑起来。总结OAuth2.0第三方登录的核心流程就是跳转授权、回调换码、换码取Token、取Token拉用户信息。State参数和HTTPS是安全底线回调地址必须精确匹配。多平台集成时用接口抽象每加一个平台实现一个Provider就行。这个模块到这里就结束了。从Echo框架到WebSocket从文件上传到认证授权Go Web开发的核心场景基本都覆盖了。下个模块我们进入数据库和缓存聊聊Go怎么优雅地操作MySQL和Redis。
返回列表