B.18. HTTP Basic Authentication
HTTP Basic Auth adalah salah satu spesifikasi yang mengatur otentikasi pada HTTP request. Metode ini mewajibkan client request untuk menyertakan username dan password dalam header request. Dengan menerapkan basic auth maka kita tidak perlu menggunakan token untuk mendapatkan session.
Lebih jelasnya mengenai spesifikasi Basic Auth bisa di lihat diRFC-7617
Informasi username dan password harus di-encode terlebih dahulu ke format yg sudah ditentukan sesuai spesifikasi, kemudian dijadikan value dari header Authentication
.
Berikut adalah contoh format penulisan basic auth:
// Request header
Authorization: Basic c29tZXVzZXJuYW1lOnNvbWVwYXNzd29yZA==
Informasi disisipkan dalam request header dengan key Authorization
, dan value adalah Basic
diikut karakter spasi dan hasil encode terhadap data username dan password. Data username dan password digabung dengan separator tanda titik dua (:
) lalu di-encode dalam format encoding Base64.
// Username password encryption
base64encode("someusername:somepassword")
// Hasilnya adalah c29tZXVzZXJuYW1lOnNvbWVwYXNzd29yZA==
Go menyediakan fasilitas untuk mengambil informasi basic auth dari suatu HTTP request dengan mudah, tanpa perlu untuk memparsing header request terlebih dahulu secara manual.
B.18.1. Struktur Folder Proyek dan Endpoint
Pada chapter ini kita akan membuat sebuah web service sederhana, isinya hanya satu buah endpoint. Endpoint ini didesain untuk bisa menerima query parameter atau tanpa query parameter.
- Endpoint
/student
menghasilkan response berisi semua data siswa - Endpoint
/student?id=s001
menghasilkan response berisi data siswa sesuai dengan id yang di minta
Data siswa sendiri merupakan slice object yang disimpan di variabel global.
OK, langsung saja kita praktekan. Siapkan 3 buah file berikut, tempatkan dalam satu folder proyek.
B.18.2. Routing
Buka main.go
, isi dengan kode berikut.
package main
import "net/http"
import "fmt"
import "encoding/json"
func main() {
http.HandleFunc("/student", ActionStudent)
server := new(http.Server)
server.Addr = ":9000"
fmt.Println("server started at localhost:9000")
server.ListenAndServe()
}
Lalu siapkan handler untuk rute /student
.
func ActionStudent(w http.ResponseWriter, r *http.Request) {
if !Auth(w, r) {
return
}
if !AllowOnlyGET(w, r) {
return
}
if id := r.URL.Query().Get("id"); id != "" {
OutputJSON(w, SelectStudent(id))
return
}
OutputJSON(w, GetStudents())
}
Di dalam rute /student
terdapat beberapa validasi.
- Validasi
!Auth(w, r)
; Nantinya kita siapkan fungsiAuth()
yang gunanya adalah untuk mengecek apakah request merupakan valid basic auth request atau tidak. - Validasi
!AllowOnlyGET(w, r)
; Akan dibuat juga fungsiAllowOnlyGET()
, tugasnya memastikan hanya request dengan methodGET
yang diperbolehkan masuk.
Setelah request lolos dari 2 validasi di atas, lanjut ke pengecekan berikutnya yaitu mendeteksi apakah request memiliki parameter student id.
- Ketika tidak ada parameter student id, maka endpoint ini mengembalikan semua data user yang ada. Fungsi
GetStudents()
dieksekusi. - Sedangkan jika ada parameter student id, maka hanya user dengan id yg diinginkan yg dijadikan nilai balik. Fungsi
SelectStudent(id)
dieksekusi.
Selanjutnya tambahkan satu fungsi lagi di main()
yaitu OutputJSON()
. Fungsi ini digunakan konversi data ke bentuk JSON string.
func OutputJSON(w http.ResponseWriter, o interface{}) {
res, err := json.Marshal(o)
if err != nil {
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(res)
}
Konversi dari objek atau slice ke JSON string dilakukan via json.Marshal()
. Lebih jelasnya mengenai fungsi tersebut di bahas di chapter A.53. JSON Data.
B.18.3. Data Student
Buka file student.go
, siapkan struct Student
dan variabel untuk menampung data yang bertipe []Student
. Data inilah yang nantinya dijadikan nilai balik endpoint /student
.
package main
var students = []*Student{}
type Student struct {
Id string
Name string
Grade int32
}
Buat fungsi GetStudents()
, fungsi ini mengembalikan semua data student. Buat juga fungsi SelectStudent(id)
, fungsi ini mengembalikan data student sesuai dengan id terpilih.
func GetStudents() []*Student {
return students
}
func SelectStudent(id string) *Student {
for _, each := range students {
if each.Id == id {
return each
}
}
return nil
}
Last but not least, implementasikan fungsi init()
yang didalamnya berisi pembuatan beberapa dummy data untuk ditampung variabel students
.
Fungsi
init()
adalah fungsi yang secara otomatis dipanggil ketika package tersebut di import atau di run.
func init() {
students = append(students, &Student{Id: "s001", Name: "bourne", Grade: 2})
students = append(students, &Student{Id: "s002", Name: "ethan", Grade: 2})
students = append(students, &Student{Id: "s003", Name: "wick", Grade: 3})
}
B.18.4. Fungsi Auth()
dan AllowOnlyGET()
Selanjutnya, ada dua fungsi lainnya yang perlu dipersiapkan yaitu Auth()
dan AllowOnlyGET()
.
◉ Fungsi Auth()
Buka middleware.go
, siapkan fungsi Auth()
.
package main
import "net/http"
const USERNAME = "batman"
const PASSWORD = "secret"
func Auth(w http.ResponseWriter, r *http.Request) bool {
username, password, ok := r.BasicAuth()
if !ok {
w.Write([]byte(`something went wrong`))
return false
}
isValid := (username == USERNAME) && (password == PASSWORD)
if !isValid {
w.Write([]byte(`wrong username/password`))
return false
}
return true
}
Tugas fungsi Auth()
adalah memvalidasi apakah request merupakan valid basic auth request, dan juga apakah credentials yang dikirim cocok dengan data pada aplikasi kita. Informasi acuan credentials sendiri di hardcode pada konstanta USERNAME
dan PASSWORD
.
Fungsi r.BasicAuth()
mengembalikan 3 informasi:
- Username
- Password
- Nilai balik ke-3 ini adalah representasi valid tidak nya basic auth request yang sedang berlangsung
Error dimunculkan ketika basic auth terdeteksi adalah tidak valid. Sedangkan jika ternyata valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang sudah di-hardcode.
◉ Fungsi AllowOnlyGET()
Fungsi ini bertugas memastikan bahwa request yang diperbolehkan hanya yang ber-method GET
. Selainnya, maka dianggap invalid request.
func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool {
if r.Method != "GET" {
w.Write([]byte("Only GET is allowed"))
return false
}
return true
}
B.18.5. Testing
Semuanya sudah siap, sekarang jalankan aplikasi.
go run *.go
Jangan menggunakan go run main.go
, dikarenakan dalam package main
terdapat beberapa file lain yang harus diikutsertakan pada saat runtime.
Test web service yang telah dibuat menggunakan command curl
.
$ curl -X GET --user batman:secret http://localhost:9000/student
$ curl -X GET --user batman:secret http://localhost:9000/student?id=s001