Go(Golang)에서 HTTP 기본 인증을 구현하는 방법

22290 단어 gobeginners
오늘날 웹은 다양한 플랫폼을 사용하는 사람들로 가득 차 있습니다. 모든 플랫폼에는 해당 플랫폼에 특정한 사용자를 식별하는 자체 인증 메커니즘이 있습니다. 인증을 통해 애플리케이션은 애플리케이션에 요청을 보낸 사람이 실제로 그들이 말하는 사람임을 알 수 있습니다. 오늘 우리는 가장 간단하고 쉬운 인증 메커니즘 중 하나인 기본 인증을 구현하는 방법을 배웁니다. 이 튜토리얼은 기본 인증을 구현하는 Go 서버를 구현하는 방법을 안내합니다.

기본 액세스 인증



기본 인증은 HTTP 프로토콜에 내장된 간단한 인증 체계입니다. 쿠키, 세션 식별자 또는 로그인 페이지가 필요하지 않습니다. 클라이언트는 Basic이라는 단어와 공백 및 base64 인코딩 문자열이 포함된 표준 Authorization 헤더와 함께 HTTP 요청을 보냅니다username:password.
예를 들어 사용자 이름test 및 비밀번호secret의 헤더는 아래와 같습니다.

Authorisation: Basic dGVzdDpzZWNyZXQ=




이 다이어그램은 클라이언트와 서버 간의 기본 인증 흐름을 간략하게 보여줍니다.
  • 클라이언트가 Authorisation 헤더를 제공하지 않으면 서버는 401 Unauthorised 상태 코드를 반환하고 하나 이상의 챌린지를 포함하는 WWW 인증 응답 헤더로 인증하는 방법에 대한 정보를 제공합니다.
  • 클라이언트가 Authorisation를 제공하면 서버는 단순히 200 OK와 리소스로 응답합니다(이 경우 간단한 환영 메시지이지만 모든 정보가 될 수 있음).

  • After authenticating the server can perform various other checks like whether the client has access to the resource requested etc and respond to the client accordingly. In the above example, we are simply returning a welcome message without these checks.



    HTTP 서버 생성



    Go에서 간단한 HTTP 서버를 만들어 봅시다.

    In case you want to learn about it you can refer to my other blog Create Golang HTTP Server in 15 Lines



    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    )
    
    func greeting(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"meesage": "welcome to golang world!"}`))
        return
    }
    
    func main() {
        http.HandleFunc("/", greeting)
        fmt.Println("Starting Server at port :8080")
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    


    이제 인사말 처리기에서 기본 인증에 대한 확인을 추가합니다.

    func greeting(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "application/json")
        username, password, ok := r.BasicAuth()
        if !ok {
            w.Header().Add("WWW-Authenticate", `Basic realm="Give username and password"`)
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(`{"message": "No basic auth present"}`))
            return
        }
    
        if !isAuthorised(username, password) {
            w.Header().Add("WWW-Authenticate", `Basic realm="Give username and password"`)
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(`{"message": "Invalid username or password"}`))
            return
        }
    
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "welcome to golang world!"}`))
        return
    }
    


    위의 내용을 주의 깊게 살펴보면 r.BasicAuth() 방법을 사용하여 사용자 이름 비밀번호를 추출하고 있음을 알 수 있습니다. 다음은 메서드 서명func (r *Request) BasicAuth() (username, password string, ok bool)입니다.


    따라서 이 메서드는 단순히 인증 헤더가 없는지 확인하고 빈 사용자 이름과 암호를 반환하고 ok를 false로 반환합니다. 그렇지 않으면 base64 문자열을 디코딩하여 다음으로 분할하고 ok와 함께 사용자 이름과 암호를 true로 반환합니다.

    그런 다음 고객이 제공한 사용자 이름과 암호가 시스템에 있는지 확인합니다. 사용자 이름과 암호의 간단한 맵을 만들었습니다. 여기에 대한 코드가 있습니다.

    var users = map[string]string{
        "test": "secret",
    }
    
    func isAuthorised(username, password string) bool {
        pass, ok := users[username]
        if !ok {
            return false
        }
    
        return password == pass
    }
    


    이제 전체 예제를 살펴보겠습니다.

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    )
    
    var users = map[string]string{
        "test": "secret",
    }
    
    func isAuthorised(username, password string) bool {
        pass, ok := users[username]
        if !ok {
            return false
        }
    
        return password == pass
    }
    
    func greeting(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "application/json")
        username, password, ok := r.BasicAuth()
        if !ok {
            w.Header().Add("WWW-Authenticate", `Basic realm="Give username and password"`)
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(`{"message": "No basic auth present"}`))
            return
        }
    
        if !isAuthorised(username, password) {
            w.Header().Add("WWW-Authenticate", `Basic realm="Give username and password"`)
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(`{"message": "Invalid username or password"}`))
            return
        }
    
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"message": "welcome to golang world!"}`))
        return
    }
    
    func main() {
        http.HandleFunc("/", greeting)
        fmt.Println("Starting Server at port :8080")
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    


    curl 요청을 만들어 출력 결과를 보여드리겠습니다.

    $curl -v http://localhost:8080
    *   Trying ::1...
    * TCP_NODELAY set
    * Connected to localhost (::1) port 8080 (#0)
    > GET / HTTP/1.1
    > Host: localhost:8080
    > User-Agent: curl/7.64.1
    > Accept: */*
    > 
    < HTTP/1.1 401 Unauthorized
    < Content-Type: application/json
    < Www-Authenticate: Basic realm="Give username and password"
    < Date: Sun, 11 Oct 2020 12:20:23 GMT
    < Content-Length: 36
    < 
    * Connection #0 to host localhost left intact
    {"message": "No basic auth present"}* Closing connection 0
    


    위의 메시지가 표시되면 Authorisation 헤더 없이 전화를 걸었다는 것을 알 수 있습니다.

    $ curl -v -u test:secret http://localhost:8080
    *   Trying ::1...
    * TCP_NODELAY set
    * Connected to localhost (::1) port 8080 (#0)
    * Server auth using Basic with user 'test'
    > GET / HTTP/1.1
    > Host: localhost:8080
    > Authorization: Basic dGVzdDpzZWNyZXQ=
    > User-Agent: curl/7.64.1
    > Accept: */*
    > 
    < HTTP/1.1 200 OK
    < Content-Type: application/json
    < Date: Sun, 11 Oct 2020 12:21:15 GMT
    < Content-Length: 39
    < 
    * Connection #0 to host localhost left intact
    {"message": "welcome to golang world!"}* Closing connection 0
    


    이제 올바른 사용자 이름과 암호를 제공한 후 성공과 메시지 본문을 반환합니다.

    If you open the localhost:8080 in the browser it will give you a pop-up window to enter the username and password. This happens because of the header Www-Authenticate: Basic realm="Give username and password".



    클라이언트 만들기



    클라이언트를 생성하기 위해 func (r *Request) SetBasicAuth(username, password string)를 사용하여 헤더를 설정합니다. 기본적으로 사용자 이름과 암호를 가져온 다음 base 64를 사용하여 인코딩한 다음 헤더Authorisation: Basic <bas64 encoded string>를 추가합니다. Voila, 클라이언트 요청에 기본 인증을 성공적으로 추가했습니다. 전체 코드를 보여드리겠습니다.

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    )
    
    func main() {
        req, err := http.NewRequest(http.MethodGet, "http://localhost:8080", nil)
        if err != nil {
            panic(err)
        }
    
        req.SetBasicAuth("test", "secret")
        req.Header.Add("Content-Type", "application/json")
        req.Close = true
    
        client := http.Client{}
        response, err := client.Do(req)
        if err != nil {
            panic(err)
        }
    
        if response.StatusCode != http.StatusOK {
            panic("Non 2xx response from server, request" + response.Status)
        }
    
        defer response.Body.Close()
        body, err := ioutil.ReadAll(response.Body)
        if err != nil {
            panic(err)
        }
    
        log.Print(string(body))
    }
    


    I am using panic here to handle the error but when you are writing this for a production handle it gracefully either by returning it or by logging it.



    전체 코드를 확인하려면 here으로 이동하십시오.

    결론:

    Go에 대한 기본 인증 지원으로 클라이언트와 서버를 만드는 것은 매우 쉽습니다. 외부 라이브러리가 필요하지 않으며 내장된 지원만으로도 이를 구현하기에 충분합니다. 또한 이것을 더 잘 개선하기 위해 기본 인증 논리를 미들웨어로 옮긴 다음 해당 미들웨어를 기본 인증이 필요한 핸들러에 연결할 수 있습니다. 경고 기본 인증은 공용 API 인증을 위한 가장 안전한 메커니즘이 아닙니다.

    If you liked this post please share it with others so that it can help them as well. You can tag me on twitter . Also please follow me here and on twitter for future blogs which I write over here

    좋은 웹페이지 즐겨찾기