iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 7
0

前言

今天的程式都在這個commit

流程

  1. 使用者點下去Google登入
  2. 後端回傳一個導向Google同意頁面的URL給前端 (API 1)
  3. 使用者點下同意授權
  4. 導向後端的API (API 2)
  5. 後端把拿到的authorization code,去取Token,再跟Google取得會員資料
  6. 產生JWT Token存在cookie
  7. 導向回前端頁面

簡單來說就兩支API

  1. 回傳導向Google的URL
  2. 實際做登入

開始吧!

下面的程式有到用gjson這套件
它可以用key直接取json的值,不用寫一個struct來json unmarshal。

config多加GoogleSecretKey、GoogleClientID
config/config.go

// Config Config
type Config struct {
	Mode string `mapstructure:"MODE"`
	Port string `mapstructure:"PORT"`

	GoogleSecretKey string `mapstructure:"GOOGLE_SECRET_KEY"`
	GoogleClientID  string `mapstructure:"GOOLE_CLIENT_ID"`
}

config.yaml

GOOGLE_SECRET_KEY: "{{GOOGLE_SECRET_KEY}}"
GOOLE_CLIENT_ID: "{{GOOLE_CLIENT_ID}}"

多加兩支API
main.go

api := r.Group("/api")
{
    api.GET("ouath/google/url", handler.GoogleAccsess)
    api.GET("ouath/google/login", handler.GoogleLogin)
}

要使用者導向到OAuth同意頁面
https://accounts.google.com/o/oauth2/v2/auth
參數:
client_id=你的client id 上一篇申請的
redirect_uri=使用者同意後要導向那裡 (目前測試先導到本機http://localhost:9090/api/ouath/google/login)
scope=https://www.googleapis.com/auth/userinfo.profile (授權使用者資料)
response_type=code (authorization code 後面要拿它去打API用)

handler/auth.go

// GoogleAccsess GoogleAccsess
func GoogleAccsess(c *gin.Context) {
	res.Success(c, gin.H{
		"url": oauthURL(),
	})
}

func oauthURL() string {
	u := "https://accounts.google.com/o/oauth2/v2/auth?client_id=%s&response_type=code&scope=%s&redirect_uri=%s"

	return fmt.Sprintf(u, config.Val.GoogleClientID, scope, redirectURL)
}

使用者同意後會導回
http://localhost:9090/api/ouath/google/login
Google導回來的時候,在url會多帶一個code給你
拿code去打這API,拿到token
https://www.googleapis.com/oauth2/v4/token
參數:
code=從url拿的
grant_type=authorization_code
client_id=你的client_id
client_secret=你的secret
redirect_uri=http://localhost:9090/api/ouath/google/login

拿到token,最後去取得會員資料
https://www.googleapis.com/oauth2/v1/userinfo?alt=json
參數:
access_token=token

// GoogleLogin GoogleLogin
func GoogleLogin(c *gin.Context) {
	code := c.Query("code")

	token, err := accessToken(code)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Debug("accessToken error")
		c.Redirect(http.StatusFound, "/")
		return
	}

	id, name, err := getGoogleUserInfo(token)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Debug("getGoogleUserInfo error")
		c.Redirect(http.StatusFound, "/")
		return
	}

    // 把值log出來看
	log.Infof("id: %v, name: %v", id, name)
}

func accessToken(code string) (token string, err error) {
	u := "https://www.googleapis.com/oauth2/v4/token"

	data := url.Values{"code": {code}, "client_id": {config.Val.GoogleClientID}, "client_secret": {config.Val.GoogleSecretKey}, "grant_type": {"authorization_code"}, "redirect_uri": {redirectURL}}
	body := strings.NewReader(data.Encode())

	resp, err := http.Post(u, "application/x-www-form-urlencoded", body)
	if err != nil {
		return token, err
	}

	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return token, err
	}

	token = gjson.GetBytes(b, "access_token").String()

	return token, nil
}

func getGoogleUserInfo(token string) (id, name string, err error) {
	u := fmt.Sprintf("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=%s", token)
	resp, err := http.Get(u)
	if err != nil {
		return id, name, err
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return id, name, err
	}

	name = gjson.GetBytes(body, "name").String()
	id = gjson.GetBytes(body, "id").String()

	return id, name, nil
}

最後在Google console的已授權的重新導向URI加上我們的API
https://ithelp.ithome.com.tw/upload/images/20200913/20129767bOjTc7keSG.png

直接來操作一次
https://ithelp.ithome.com.tw/upload/images/20200913/20129767Jc0Ygw857X.png

URL點下去
https://ithelp.ithome.com.tw/upload/images/20200913/20129767xdk9OvcuUu.png

同意了之後目前會是空白頁,因為頁面還沒做。
這時候看一下terminal
成功了把會員的名稱跟ID都要回來
https://ithelp.ithome.com.tw/upload/images/20200913/20129767rLR6nuDimM.png

明天會把JWT Token也補上

今天先這樣! 謝謝大家


上一篇
Day 6 Google OAuth2
下一篇
Day 8 來做Google登入吧 - 2
系列文
Golang & Vue.js 30天從0打造服務30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
obarisk
iT邦研究生 1 級 ‧ 2022-05-24 15:20:19

也可以用 oauth2 跟 oauth2/google

我要留言

立即登入留言