建立授權應用
這裏的 Authorization callback URL
就是後邊介面的 redirect_uri
引數
授權後返回的地址建議使用 history
, hash
路由重定向後會有問題比如 http://localhost:5173/?code=079f6270a764f73036ed#/login
獲取 client_id、client_secret
找到對應的應用點選進入詳情
點選 Generate a new client secret
建立 client_secret
將獲取到的 client_id、client_secret
儲存一下後邊要用
授權流程
整體流程大概如下圖, 前端呼叫一個介面、後端呼叫兩個
我們來看下 github 官網文件猛擊訪問
文件有點不太好看懂,讓我們來看下 web 頁面如何完成授權登入
使用
https://github.com/login/oauth/authorize
填入相關引數重定向到 github 授權頁面授權成功會攜帶
code
引數重定向到上邊設定的redirect_uri
地址前端獲取到路由中
code
引數向後端發起登入請求後端接收到登入請求取出 code 向
https://github.com/login/oauth/access_token
發起 post 請求獲取access_token
資訊使用獲取到的
access_token
發起https://api.github.com/user
請求獲取使用者資訊後端返回登入成功資訊
前端實現
<script setup> import { Icon } from '@iconify/vue' import { useRoute, useRouter } from 'vue-router' import { ElLoading } from 'element-plus' import { ref } from 'vue' import { useUserStore } from '@/stores' import { githubLogin } from '@/api/login' const loadingDom = document.getElementById('login-container') as HTMLElement const loadingInstance = ref() function setLoginLoading(message: string) { loadingInstance.value = ElLoading.service({ target: loadingDom, text: message, background: 'rgba(0, 0, 0, 0.5)', }) } const { VITE_APP_GITHUB_CLIENT_ID } = import.meta.env function login() { /** * 介面引數檢視 * https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps */ const client_id = VITE_APP_GITHUB_CLIENT_ID const redirect_uri = `${window.location.origin}/login` const path = `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}` window.open(path, '_self') setLoginLoading('正在獲取 github 授權...') } const { setLoginResData } = useUserStore() const router = useRouter() const route = useRoute() const code = route.query.code as string function loginGithub() { setLoginLoading('正在登入...') githubLogin({ code }) .then((res) => { setLoginResData(res.data) ElMessage.success('登入成功!') }) .catch(() => { ElMessage.warning('登入失敗!') router.replace('/login') }) .finally(() => { loadingInstance.value?.close() }) } // code 存在執行 github 登入邏輯 if (code) loginGithub() </script> <template> <el-button type="primary" class="w-[340px]" color="black" @click="login"> <Icon icon="uil:github" class="cursor-pointer mr-4" width="24px" height="42px" /> 使用 GitHub 登入 </el-button> </template> <style scoped> </style>
client_id、redirect_uri
引數上邊有說明
授權頁面長這樣
拒絕授權會攜帶錯誤資訊重定向到上邊設定的 redirect_uri
頁面
確認授權會攜帶 code
引數重定向到上邊設定的 redirect_uri
頁面
https://kkdl.netlify.app/#/github?code=xxxxxxxx
授權範圍
scope 引數授權範圍不傳表示:授予對公共資訊的只讀訪問許可權(包括使用者個人資料資訊、儲存庫資訊和要點) 引數文件猛擊訪問
後端實現
後端使用 go、GoFrame 實現其他語言也幾乎一致 OAuth2.0 是一個通用協議
使用 OAuth2.0 可以簡化 github 介面的呼叫
安裝所需依賴
go get golang.org/x/oauth2 go get golang.org/x/oauth2/github
介面實現
package login import ( "context" "encoding/json" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "golang.org/x/oauth2" "golang.org/x/oauth2/github" "net/http" "time" ) // github 返回的使用者資訊 type GithubUserInfo struct { AvatarURL string `json:"avatar_url"` Bio string `json:"bio"` Company string `json:"company"` CreatedAt time.Time `json:"created_at"` Email *string `json:"email"` EventsURL string `json:"events_url"` Followers int `json:"followers"` FollowersURL string `json:"followers_url"` Following int `json:"following"` FollowingURL string `json:"following_url"` GistsURL string `json:"gists_url"` GravatarID string `json:"gravatar_id"` Hireable *bool `json:"hireable"` HTMLURL string `json:"html_url"` ID int `json:"id"` Location *string `json:"location"` Login string `json:"login"` Name string `json:"name"` NodeID string `json:"node_id"` OrganizationsURL string `json:"organizations_url"` PublicGists int `json:"public_gists"` PublicRepos int `json:"public_repos"` ReceivedEventsURL string `json:"received_events_url"` ReposURL string `json:"repos_url"` SiteAdmin bool `json:"site_admin"` StarredURL string `json:"starred_url"` SubscriptionsURL string `json:"subscriptions_url"` TwitterUsername *string `json:"twitter_username"` Type string `json:"type"` UpdatedAt time.Time `json:"updated_at"` URL string `json:"url"` } func getGithubUserInfo(ctx context.Context, code string) (*GithubUserInfo, error) { // 讀取配置 cfg := g.Cfg() githubOAuthConfig := &oauth2.Config{ ClientID: cfg.MustGet(ctx, "githubConfig.client_id").String(), ClientSecret: cfg.MustGet(ctx, "githubConfig.client_secret").String(), RedirectURL: cfg.MustGet(ctx, "githubConfig.redirect_uri").String(), Scopes: []string{"user"}, Endpoint: github.Endpoint, // 認證的端點這裏是 github } token, err := githubOAuthConfig.Exchange(ctx, code) if err != nil { return nil, gerror.New("獲取 github token失敗!") } client := githubOAuthConfig.Client(ctx, token) // 獲取 github 使用者資訊 resp, err := client.Get("https://api.github.com/user") if err != nil { return nil, gerror.New("獲取 github 使用者資訊失敗!") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, gerror.New("無法獲取 github 使用者資訊!") } var userInfo GithubUserInfo if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil { g.Log().Error(ctx, "Failed to parse user info:", err) return nil, gerror.New("格式化 github 使用者資訊失敗!") } return &userInfo, nil }
獲取到 github 使用者資訊後做相應的業務處理
比如判斷使用者是否已經建立
未建立則建立使用者然後將使用者資訊返回前端登入成功
已建立則將查詢到的使用者資訊返回前端登入成功