网站接入Google Oauth2.0第三方登录及Golang后端

1. Oauth2.0

2.Google第三方登录

2.1 跳转链接

https://accounts.google.com/o/oauth2/v2/auth

参数:
client_id,redirect_uri,response_type,scope,state
如:https://accounts.google.com/o/oauth2/v2/auth?client_id=xxxx&redirect_uri=https://pm.ljsea.top/tool/third_party_callback&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile&state=eyJ1dWlkIjoiYzljMGQwYTMtMDdmNy00NjUyLWE3NWItNDJhMzFlZDFlODc0IiwidHlwZSI6ImFkZCIsInByb2plY3QiOiJTQVciLCJwbGF0Zm9ybSI6Imdvb2dsZSIsInVzZXJfaWQiOjF9

使用state带上网站的内的业务处理标识

2.2 通过回调得到的Code换取AccessToken

url: https://www.googleapis.com/oauth2/v4/token

参数:client_id,redirect_uri,scope,grant_type=authorization_code,client_secret,code
该接口post请求,表单参数:
curl -X POST -d "client_id=xxx&client_secret=xxxxx&code=4/0Ab_5qlkwhKlXcj7v5481iZjZ7jACB4rwaf0wnIyZSOtRrMIHP4jP2Erk2IBZqpINpAa3RQ&grant_type=authorization_code&redirect_uri=https://pm.ljsea.top/tool/third_party_callback" https://www.googleapis.com/oauth2/v4/token

获取token

2.3 根据token获取用户信息

url:https://www.googleapis.com/oauth2/v2/userinfo
参数:access_token,请求方式GET

https://www.googleapis.com/oauth2/v2/userinfo?access_token=xxxx

获取用户信息后可进行具体业务信息绑定与授权

3.Golang后端实现

3.1 获取跳转链接

func GetThirdPartyAuthUrl(c *gin.Context) {
    platform := c.Query("platform")
    uuid := c.Query("uuid")
    hType := c.Query("type") //操作类型add,login
    var resp proto.GenerateResp
    if platform == "" || uuid == "" || hType == "" {
        resp.Code = proto.ParameterError
        resp.Message = "platform or uuid is empty"
        c.JSON(http.StatusOK, resp)
        return
    }
    var state proto.ThirdPartyLoginState
    state.UUID = uuid
    state.Type = hType
    state.Platform = platform
    state.Project = "SAW"
    if hType == "add" {
        //查看是否已经绑定
        token := c.Request.Header.Get("token")
        if token == "" {
            token = c.Query("token")
        }
        if token == "" {
            resp.Code = proto.ParameterError
            resp.Message = "token is empty"
            c.JSON(http.StatusOK, resp)
            return
        }
        userID, err := service.DecodeJWTToken(token)
        if err != nil {
            resp.Code = proto.ParameterError
            resp.Message = err.Error()
            c.JSON(http.StatusOK, resp)
            return
        }
        //需要将uuid绑定在该用户上
        worker.SetRedisWithExpire("user_add_platform_"+uuid, strconv.Itoa(userID), time.Minute*9)
        state.UserID = userID
    }

    stateStr, _ := json.Marshal(state)
    var respUrl string
    //base64编码
    stateBase64Str := base64.StdEncoding.EncodeToString(stateStr)
    switch platform {
    case "qq":
        params := url.Values{}
        params.Add("response_type", "code")
        params.Add("client_id", worker.AppId)
        params.Add("state", stateBase64Str)
        str := fmt.Sprintf("%s", params.Encode())
        respUrl = fmt.Sprintf("%s?%s", proto.QQAuthorizeBaseUrl, str)
    case "github":
        params := url.Values{}
        params.Add("client_id", proto.Config.GITHUB_CLIENT_ID)
        params.Add("login", uuid)
        params.Add("state", stateBase64Str)
        baseUri := proto.GitHuAuthorizeBaseUrl
        respUrl = fmt.Sprintf("%s?%s", baseUri, params.Encode())
    case "gitee":
        params := url.Values{}
        params.Add("client_id", proto.Config.GITEE_CLIENT_ID)
        //response_type=code
        params.Add("response_type", "code")
        params.Add("state", stateBase64Str)
        params.Add("redirect_uri", "https://pm.ljsea.top/tool/gitee_callback")
        baseUri := proto.GiteeAuthorizeBaseUrl
        respUrl = fmt.Sprintf("%s?%s", baseUri, params.Encode())
    case "google":
        params := url.Values{}
        params.Add("client_id", worker.GoogleClientID)
        params.Add("response_type", "code") //直接返回token
        redirectURL := "https://pm.ljsea.top/tool/third_party_callback"
        scope := "https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile"
        //params.Add("redirect_uri", "https://pm.ljsea.top/tool/third_party_callback")
        //params.Add("scope", "https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile")
        params.Add("state", stateBase64Str)
        baseUri := proto.GoogleAuthorizeBaseUrl
        respUrl = fmt.Sprintf("%s?%s", baseUri, params.Encode())
        respUrl = fmt.Sprintf("%s&redirect_uri=%s&scope=%s", respUrl, redirectURL, scope)
    case "facebook":
        params := url.Values{}
        params.Add("client_id", worker.FacebookClientID)
        params.Add("redirect_uri", "https://pm.ljsea.top/tool/third_party_callback")
        params.Add("response_type", "code") //直接返回token
        params.Add("state", stateBase64Str)
        respUrl = fmt.Sprintf("%s?%s", proto.FacebookAuthorizeBaseUrl, params.Encode())
    default:
        log.Println("platform not support:", platform)
    }
    resp.Message = "success"
    resp.Code = proto.SuccessCode
    resp.Data = respUrl
    c.JSON(http.StatusOK, resp)
}

3.2 回调接口

func handleThirdPartyCallback(c *gin.Context) {
    var resp proto.GenerateResp
    code := c.Query("code")            //code
    stateBase64Str := c.Query("state") //state
    //解析base64
    decodedBytes, err := base64.StdEncoding.DecodeString(stateBase64Str)
    if err != nil {
        fmt.Println("Decoding error:", err)
    } else {
        decodedStr := string(decodedBytes)
        //json解析
        var state proto.ThirdPartyLoginState
        err = json.Unmarshal([]byte(decodedStr), &state)
        log.Println("handle callback state:", decodedStr, "\tcode:", code)
        if err != nil {
            log.Println("json unmarshal error:", err)
        } else {
            service.DoThirdPartyCallBack(&state, code)
        }
    }
    resp.Code = 0
    resp.Message = "success"
    c.JSON(http.StatusOK, resp)
}

3.3 根据Code获取Token及获取用户信息

根据上面使用Golang发起请求即可

3.4 业务处理

func DoGoogleCallBack(state *proto.ThirdPartyLoginState, code string) {
    //根据code获取Access Token
    tokenResp, err := worker.GetGoogleAccessTokenByCode(code, "https://pm.ljsea.top/tool/third_party_callback", worker.GoogleClientID, worker.GoogleClientSecret)

    if tokenResp.AccessToken == "" {
        log.Println("get google access token is empty")
        return
    }
    log.Println("get google access token:", tokenResp)
    //获取用户信息
    userInfo, err := worker.GetGoogleUserInfo(tokenResp.AccessToken)
    if err != nil {
        log.Println("get google user info error:", err)
        return
    }
    log.Println("get google user info:", userInfo)
    var thirdPartyLoginStatus proto.ThirdPartyLoginStatus
    thirdPartyLoginStatus.Type = state.Platform
    thirdPartyUserInfo := proto.ThirdPartyUserInfo{UserID: userInfo.ID, Name: userInfo.Name, Avatar: userInfo.Picture, Email: userInfo.Email}
    HandleThirdPartyLoginStatusV2(state, &thirdPartyLoginStatus, &thirdPartyUserInfo)
    //更新redis中的第三方登录状态
    thirdPartyLoginStatusStr, _ := json.Marshal(thirdPartyLoginStatus)
    log.Println("do handle google callback success, third party login status:", string(thirdPartyLoginStatusStr))
    worker.SetRedisWithExpire(state.UUID, string(thirdPartyLoginStatusStr), time.Minute*10)
}
func HandleThirdPartyLoginStatusV2(state *proto.ThirdPartyLoginState, thirdPartyLoginStatus *proto.ThirdPartyLoginStatus, userInfo *proto.ThirdPartyUserInfo) {
    if state.Type == "login" {
        //根据第三方平台查找用户
        thirdPartyUserInfoList := dao.FindThirdPartyUserInfoByThirdPartyID(userInfo.UserID)
        if thirdPartyUserInfoList == nil || len(thirdPartyUserInfoList) == 0 {
            thirdPartyLoginStatus.Status = proto.ThirdPartyUserNotBinded //未绑定用户
        } else {
            thirdPartyUserInfo := thirdPartyUserInfoList[0]
            //获取用户信息
            user := GetUserByIDWithCache(thirdPartyUserInfo.UserID)
            if user.ID == 0 {
                thirdPartyLoginStatus.Status = proto.ThirdPartyUserNotBinded
                log.Println("get user by id error")
            } else {
                //成功
                thirdPartyLoginStatus.Status = proto.SuccessCode
                thirdPartyLoginStatus.UserInfo.UserID = int(user.ID)
                thirdPartyLoginStatus.UserInfo.Username = user.Name
                thirdPartyLoginStatus.UserInfo.Email = user.Email
                thirdPartyLoginStatus.UserInfo.Token, _ = GenerateJWTToken(int(user.ID), user.Name)
            }
        }
    } else if state.Type == "add" {
        //根据第三方平台查找用户
        thirdPartyUserInfoList := dao.FindThirdPartyUserInfoByThirdPartyID(userInfo.UserID)
        if thirdPartyUserInfoList != nil && len(thirdPartyUserInfoList) > 0 {
            thirdPartyLoginStatus.Status = 3 //已绑定用户
        } else {
            userIDStr := worker.GetRedis("user_add_platform_" + state.UUID)
            if userIDStr == "" {
                log.Println("user id is empty")
                thirdPartyLoginStatus.Status = 2 //未绑定用户
            } else {
                //字符串转int
                userID, _ := strconv.Atoi(userIDStr)
                //根据用户ID获取用户信息
                user := GetUserByIDWithCache(userID)
                if user.ID == 0 {
                    thirdPartyLoginStatus.Status = 4 //添加用户信息错误
                    log.Println("get user by id error")
                } else {
                    //需要创建数据库记录
                    data := dao.ThirdPartyUserInfo{UserID: userID, ThirdPartyID: userInfo.UserID, ThirdPartyPlatform: state.Platform, ThirdPartyUserAvatar: userInfo.Avatar, ThirdPartyUserName: userInfo.Name, ThirdPartyUserUrl: userInfo.Url, ThirdPartyEmail: userInfo.Email}
                    uid := dao.CreateThirdPartyUserInfoV2(&data)
                    if uid == 0 {
                        log.Println("create third party user info error")
                        thirdPartyLoginStatus.Status = proto.OperationFailed //操作错误
                    } else {
                        //成功
                        thirdPartyLoginStatus.Status = proto.SuccessCode
                        thirdPartyLoginStatus.UserInfo.UserID = int(user.ID)
                        thirdPartyLoginStatus.UserInfo.Username = user.Name
                        thirdPartyLoginStatus.UserInfo.Email = user.Email
                        thirdPartyLoginStatus.UserInfo.Token, _ = GenerateJWTToken(int(user.ID), user.Name)
                    }
                }
            }
        }
    } else {
        log.Println("DoGithubCallBack state type error:", state.Type)
        thirdPartyLoginStatus.Status = proto.ParameterError //参数错误
    }
    //更新userInfo到数据库
    err := dao.UpdateThirdPartyUserInfoByThirdPartyID(userInfo.UserID, state.Platform, userInfo.Name, userInfo.Avatar, userInfo.Url)
    if err != nil {
        log.Println("update third party user info error:", err)
    }
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇