Go에서 Google을 사용하는 Oauth2

31991 단어 oauth2goexample
인증은 모든 애플리케이션에서 가장 일반적인 부분입니다. 자체 인증 시스템을 구현하거나 존재하는 많은 대안 중 하나를 사용할 수 있지만 이 경우에는 OAuth2를 사용합니다.

OAuth는 사용자가 Oauth2 gohere에 대해 자세히 알아보려는 경우 해당 서비스와 사용자 이름 및 암호를 공유하지 않고 데이터에 대한 액세스 권한을 위임할 수 있는 사양입니다.

Google 프로젝트 구성



먼저 Google 프로젝트를 만들고 OAuth2 자격 증명을 만들어야 합니다.
  • Google 클라우드 플랫폼으로 이동
  • 새 프로젝트를 만들거나 이미 있는 경우 선택하십시오.
  • 자격 증명으로 이동한 다음 "OAuth 클라이언트 ID"를 선택하여 새로 만듭니다
  • .
  • 이 예에서는 "인증된 리디렉션 URL"을 추가합니다localhost:8000/auth/google/callback.
  • client_id 및 클라이언트 암호를 복사합니다
  • .

    OAuth2가 Google에서 작동하는 방식



    인증 절차는 애플리케이션이 브라우저를 Google URL로 리디렉션할 때 시작됩니다. URL에는 요청 중인 액세스 유형을 나타내는 쿼리 매개변수가 포함됩니다. Google은 사용자 인증, 세션 선택 및 사용자 동의를 처리합니다. 결과는 애플리케이션이 액세스 토큰 및 새로 고침 토큰과 교환할 수 있는 권한 부여 코드입니다.

    애플리케이션은 나중에 사용할 새로 고침 토큰을 저장하고 액세스 토큰을 사용하여 Google API에 액세스해야 합니다. 액세스 토큰이 만료되면 애플리케이션은 새로 고침 토큰을 사용하여 새 토큰을 얻습니다.



    코드로 가자



    우리는 OAuth2 승인 및 인증된 HTTP 요청을 만들기 위한 지원을 제공하는 "golang.org/x/oauth2"패키지를 사용할 것입니다.

    내 경우 workdir에 새 프로젝트(폴더)를 만듭니다. 'oauth2-example'이라고 부르겠습니다. oauth2 패키지를 포함해야 합니다.
    go get golang.org/x/oauth2
    따라서 프로젝트에 main.go를 만듭니다.

    package main
    
    import (
        "fmt"
        "net/http"
        "log"
        "github.com/douglasmakey/oauth2-example/handlers"
    )
    
    func main() {
        server := &http.Server{
            Addr: fmt.Sprintf(":8000"),
            Handler: handlers.New(),
        }
    
        log.Printf("Starting HTTP Server. Listening at %q", server.Addr)
        if err := server.ListenAndServe(); err != http.ErrServerClosed {
            log.Printf("%v", err)
        } else {
            log.Println("Server closed!")
        }
    }
    
    


    http.Server를 사용하여 간단한 서버를 만들고 실행합니다.

    다음으로 애플리케이션의 핸들러가 포함된 'handlers' 폴더를 생성합니다. 이 폴더에는 'base.go'가 생성됩니다.

    package handlers
    
    import (
        "net/http"
    )
    
    func New() http.Handler {
        mux := http.NewServeMux()
        // Root
        mux.Handle("/",  http.FileServer(http.Dir("templates/")))
    
        // OauthGoogle
        mux.HandleFunc("/auth/google/login", oauthGoogleLogin)
        mux.HandleFunc("/auth/google/callback", oauthGoogleCallback)
    
        return mux
    }
    


    http.ServeMux를 사용하여 엔드포인트를 처리하고, 다음으로 'http. http.FileServer', 해당 템플릿은 'index.html'이고 '템플릿' 폴더에 있습니다.

    또한 Google "/auth/google/login"및 "/auth/google/callback"을 사용하여 Oauth에 대한 두 개의 끝점을 만듭니다. Google 콘솔에서 애플리케이션을 구성했을 때를 기억하십니까? 콜백 URL은 동일해야 합니다.

    다음으로 핸들러에 다른 파일을 만들고 'oauth_google.go'라고 합니다. 이 파일에는 애플리케이션에서 Google로 OAuth를 처리하는 모든 논리가 포함되어 있습니다.

    Google과 통신하기 위해 auth.Config와 함께 var googleOauthConfig를 선언합니다.
    범위: OAuth 2.0 범위는 액세스 토큰에 부여되는 액세스 양을 제한하는 방법을 제공합니다.

    var googleOauthConfig = &oauth2.Config{
        RedirectURL:  "http://localhost:8000/auth/google/callback",
        ClientID:     os.Getenv("GOOGLE_OAUTH_CLIENT_ID"),
        ClientSecret: os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET"),
        Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
        Endpoint:     google.Endpoint,
    }
    


    핸들러 oauthGoogleLogin



    이 처리기는 로그인 링크를 만들고 사용자를 여기로 리디렉션합니다.

    CSRF 공격으로부터 사용자를 보호하기 위한 토큰인 AuthCodeURL 수신 상태. 항상 비어 있지 않은 문자열을 제공하고 리디렉션 콜백의 상태 쿼리 매개변수와 일치하는지 확인해야 합니다. 이는 각 요청에 대해 무작위로 생성되는 것이 좋습니다. 그래서 간단한 쿠키를 사용합니다.

    func oauthGoogleLogin(w http.ResponseWriter, r *http.Request) {
    
        // Create oauthState cookie
        oauthState := generateStateOauthCookie(w)
        u := googleOauthConfig.AuthCodeURL(oauthState)
        http.Redirect(w, r, u, http.StatusTemporaryRedirect)
    }
    
    func generateStateOauthCookie(w http.ResponseWriter) string {
        var expiration = time.Now().Add(365 * 24 * time.Hour)
    
        b := make([]byte, 16)
        rand.Read(b)
        state := base64.URLEncoding.EncodeToString(b)
        cookie := http.Cookie{Name: "oauthstate", Value: state, Expires: expiration}
        http.SetCookie(w, &cookie)
    
        return state
    }
    
    


    핸들러 oauthGoogleCallback



    이 핸들러는 상태가 oauthStateCookie와 같은지 확인하고 코드를 getUserDataFromGoogle 함수에 전달합니다.

    func oauthGoogleCallback(w http.ResponseWriter, r *http.Request) {
        // Read oauthState from Cookie
        oauthState, _ := r.Cookie("oauthstate")
    
        if r.FormValue("state") != oauthState.Value {
            log.Println("invalid oauth google state")
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        data, err := getUserDataFromGoogle(r.FormValue("code"))
        if err != nil {
            log.Println(err.Error())
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        // GetOrCreate User in your db.
        // Redirect or response with a token.
        // More code .....
        fmt.Fprintf(w, "UserInfo: %s\n", data)
    }
    
    func getUserDataFromGoogle(code string) ([]byte, error) {
        // Use code to get token and get user info from Google.
    
        token, err := googleOauthConfig.Exchange(context.Background(), code)
        if err != nil {
            return nil, fmt.Errorf("code exchange wrong: %s", err.Error())
        }
        response, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)
        if err != nil {
            return nil, fmt.Errorf("failed getting user info: %s", err.Error())
        }
        defer response.Body.Close()
        contents, err := ioutil.ReadAll(response.Body)
        if err != nil {
            return nil, fmt.Errorf("failed read response: %s", err.Error())
        }
        return contents, nil
    }
    
    
    


    전체 코드 oauth_google.go




    package handlers
    
    import (
        "golang.org/x/oauth2"
        "golang.org/x/oauth2/google"
        "net/http"
        "fmt"
        "io/ioutil"
        "context"
        "log"
        "encoding/base64"
        "crypto/rand"
        "os"
        "time"
    )
    
    // Scopes: OAuth 2.0 scopes provide a way to limit the amount of access that is granted to an access token.
    var googleOauthConfig = &oauth2.Config{
        RedirectURL:  "http://localhost:8000/auth/google/callback",
        ClientID:     os.Getenv("GOOGLE_OAUTH_CLIENT_ID"),
        ClientSecret: os.Getenv("GOOGLE_OAUTH_CLIENT_SECRET"),
        Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
        Endpoint:     google.Endpoint,
    }
    
    const oauthGoogleUrlAPI = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
    
    func oauthGoogleLogin(w http.ResponseWriter, r *http.Request) {
    
        // Create oauthState cookie
        oauthState := generateStateOauthCookie(w)
    
        /*
        AuthCodeURL receive state that is a token to protect the user from CSRF attacks. You must always provide a non-empty string and
        validate that it matches the the state query parameter on your redirect callback.
        */
        u := googleOauthConfig.AuthCodeURL(oauthState)
        http.Redirect(w, r, u, http.StatusTemporaryRedirect)
    }
    
    func oauthGoogleCallback(w http.ResponseWriter, r *http.Request) {
        // Read oauthState from Cookie
        oauthState, _ := r.Cookie("oauthstate")
    
        if r.FormValue("state") != oauthState.Value {
            log.Println("invalid oauth google state")
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        data, err := getUserDataFromGoogle(r.FormValue("code"))
        if err != nil {
            log.Println(err.Error())
            http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
            return
        }
    
        // GetOrCreate User in your db.
        // Redirect or response with a token.
        // More code .....
        fmt.Fprintf(w, "UserInfo: %s\n", data)
    }
    
    func generateStateOauthCookie(w http.ResponseWriter) string {
        var expiration = time.Now().Add(365 * 24 * time.Hour)
    
        b := make([]byte, 16)
        rand.Read(b)
        state := base64.URLEncoding.EncodeToString(b)
        cookie := http.Cookie{Name: "oauthstate", Value: state, Expires: expiration}
        http.SetCookie(w, &cookie)
    
        return state
    }
    
    func getUserDataFromGoogle(code string) ([]byte, error) {
        // Use code to get token and get user info from Google.
    
        token, err := googleOauthConfig.Exchange(context.Background(), code)
        if err != nil {
            return nil, fmt.Errorf("code exchange wrong: %s", err.Error())
        }
        response, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)
        if err != nil {
            return nil, fmt.Errorf("failed getting user info: %s", err.Error())
        }
        defer response.Body.Close()
        contents, err := ioutil.ReadAll(response.Body)
        if err != nil {
            return nil, fmt.Errorf("failed read response: %s", err.Error())
        }
        return contents, nil
    }
    
    


    실행하고 테스트하자




    go run main.go
    


    코드가 있는 저장소Repo

    좋은 웹페이지 즐겨찾기