Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
23 Jan 2025 · 5 mnt baca ·Artikel 70 / 119
Go

11 Membuat Middleware Basic Authentication HTTP Router pada Golang

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Basic Authentication adalah metode autentikasi sederhana di mana klien mengirimkan kredensial (username dan password) dalam header setiap permintaan HTTP. Pada artikel ini, kita akan mempelajari cara membuat middleware Basic Authentication di Golang menggunakan library httprouter .

Artikel ini ditujukan untuk programmer pemula dengan langkah-langkah detail dan penjelasan untuk mempermudah pemahaman.

Prasyarat

  1. Pemahaman dasar tentang Golang: Memahami fungsi dasar, struktur, dan cara kerja package.
  2. Golang terinstal: Pastikan Anda telah menginstal Go di komputer.
  3. Library pendukung: Instal library github.com/julienschmidt/httprouter dengan perintah berikut:
    bash
    1go get github.com/julienschmidt/httprouter

1. Struktur Proyek

Buat struktur proyek seperti berikut:

bash
1project-root/
2├── main.go
3├── handlers.go
4├── pkg/
5│   ├── middleware/
6│   │   └── auth.go
7│   └── utils/
8│       └── jwt.go

File main.go adalah entry point aplikasi, sedangkan middleware/ dan handlers/ digunakan untuk memisahkan middleware dan fungsi endpoint.


2. Membuat File Utama

Buka file main.go dan tambahkan kode berikut:

go
 1package main
 2
 3import (
 4	"fmt"
 5	"log"
 6	"net/http"
 7	"github.com/julienschmidt/httprouter"
 8	"project-root/middleware"
 9	"project-root/handlers"
10)
11
12func main() {
13	router := httprouter.New()
14
15		// sample testing generate basic authentication
16	fmt.Println("generate basic auth: " + base64.StdEncoding.EncodeToString([]byte("ihsan:password")))
17
18	// Endpoint publik tanpa autentikasi
19	router.GET("/public", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
20		fmt.Fprint(w, "Endpoint publik tidak membutuhkan autentikasi!\n")
21	})
22
23	// Endpoint privat menggunakan middleware Basic Authentication
24	router.GET("/private", middleware.BasicAuth(handlers.PrivateHandler))
25
26	log.Println("Server berjalan di http://localhost:8080")
27	log.Fatal(http.ListenAndServe(":8080", router))
28}

Kode di atas mendefinisikan endpoint publik dan endpoint privat yang dilindungi oleh middleware Basic Authentication.


3. Membuat Middleware Basic Authentication

3.1. Membuat Fungsi Middleware Basic Authentication

Buka file middleware/basic_auth.go dan tuliskan kode berikut:

go
 1package middleware
 2
 3import (
 4	"encoding/base64"
 5	"net/http"
 6	"strings"
 7	"github.com/julienschmidt/httprouter"
 8)
 9
10var validUsers = map[string]string{
11	"admin": "password123",
12	"user":  "password456",
13}
14
15func BasicAuth(next httprouter.Handle) httprouter.Handle {
16	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
17		authHeader := r.Header.Get("Authorization")
18		if authHeader == "" || !strings.HasPrefix(authHeader, "Basic ") {
19			http.Error(w, "Unauthorized", http.StatusUnauthorized)
20			return
21		}
22
23		// Decode Base64 dari kredensial
24		payload, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(authHeader, "Basic "))
25		if err != nil {
26			http.Error(w, "Invalid authentication token", http.StatusUnauthorized)
27			return
28		}
29
30		cred := strings.SplitN(string(payload), ":", 2)
31		if len(cred) != 2 || !validateUser(cred[0], cred[1]) {
32			http.Error(w, "Invalid username or password", http.StatusUnauthorized)
33			return
34		}
35
36		// Lanjutkan ke handler berikutnya jika valid
37		next(w, r, ps)
38	}
39}
40
41func validateUser(username, password string) bool {
42	if pass, ok := validUsers[username]; ok {
43		return pass == password
44	}
45	return false
46}

Middleware ini membaca header Authorization, men-decode kredensial yang dienkripsi dalam Base64, dan memvalidasi username serta password menggunakan data yang disimpan di memori (dalam map validUsers).

3.2. Menambahkan pengujian fungsi dengan unit test

Berikut pengujian menggunakan unit test functionality dibawah ini.

go
 1package midleware
 2
 3import (
 4	"encoding/base64"
 5	"fmt"
 6	"net/http"
 7	"net/http/httptest"
 8	"strings"
 9	"testing"
10
11	"github.com/julienschmidt/httprouter"
12)
13
14func TestBasicAuth(t *testing.T) {
15	type args struct {
16		username string
17		password string
18	}
19	tests := []struct {
20		name           string
21		args           args
22		mock           func(args args) string
23		wantHTTPStatus int
24		wantResponse   string
25	}{
26		{
27			name: "success valid basic authentication",
28			args: args{
29				username: "ihsan",
30				password: "password",
31			},
32			mock: func(args args) string {
33				return "Basic " + base64.StdEncoding.EncodeToString([]byte(args.username+":"+args.password))
34			},
35			wantHTTPStatus: http.StatusOK,
36			wantResponse:   fmt.Sprintln("Selamat datang di endpoint private menggunakan basic authentication! Anda berhasil terautentikasi."),
37		},
38		{
39			name: "invalid because authorization header not set",
40			args: args{},
41			mock: func(args args) string {
42				return ""
43			},
44			wantHTTPStatus: http.StatusUnauthorized,
45			wantResponse:   fmt.Sprintln("Unauthorized"),
46		},
47		{
48			name: "invalid username or password",
49			args: args{
50				username: "santekno",
51				password: "password",
52			},
53			mock: func(args args) string {
54				return "Basic " + base64.StdEncoding.EncodeToString([]byte(args.username+":"+args.password))
55			},
56			wantHTTPStatus: http.StatusUnauthorized,
57			wantResponse:   fmt.Sprintln("Invalid username or password"),
58		},
59	}
60	for _, tt := range tests {
61		t.Run(tt.name, func(t *testing.T) {
62			token := tt.mock(tt.args)
63
64			// membuat request dengan header yang valid
65			request := httptest.NewRequest(http.MethodGet, "/", nil)
66			request.Header.Set("Authorization", token)
67
68			// buat recorder untuk menangkap response
69			recorder := httptest.NewRecorder()
70
71			handler := BasicAuth(MockBasicAuthHandler)
72			handler(recorder, request, httprouter.Params{})
73
74			if recorder.Code != tt.wantHTTPStatus {
75				t.Errorf("expected 200 ok, got %d", recorder.Code)
76			}
77
78			if !strings.Contains(recorder.Body.String(), tt.wantResponse) {
79				t.Errorf("unexpected response body %s", recorder.Body.String())
80			}
81		})
82	}
83}
84
85func MockBasicAuthHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
86	fmt.Fprint(w, "Selamat datang di endpoint private menggunakan basic authentication! Anda berhasil terautentikasi.\n")
87}

4. Membuat Handler untuk Endpoint Privat

Buka file handlers/user.go dan tambahkan kode berikut:

go
 1package handlers
 2
 3import (
 4	"fmt"
 5	"net/http"
 6	"github.com/julienschmidt/httprouter"
 7)
 8
 9func PrivateHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
10	fmt.Fprint(w, "Selamat datang di endpoint privat! Anda berhasil terautentikasi.\n")
11}

Handler ini hanya akan diakses jika kredensial Basic Authentication yang diberikan valid.


5. Menguji Aplikasi

Jalankan aplikasi menggunakan perintah berikut:

bash
1go run main.go

Cobalah akses endpoint berikut menggunakan alat seperti curl atau Postman.

5.1. Endpoint Publik (Tanpa Autentikasi)

bash
1curl http://localhost:8080/public

Responsnya:

bash
1Endpoint publik tidak membutuhkan autentikasi!

5.2. Endpoint Privat (Dengan Autentikasi)

Untuk mengakses endpoint privat, tambahkan header Authorization dengan nilai Basic diikuti kredensial username dan password yang dienkode dalam Base64.

Misalnya, untuk username admin dan password password123:

bash
1curl -H "Authorization: Basic YWRtaW46cGFzc3dvcmQxMjM=" http://localhost:8080/private

Jika valid, responsnya adalah:

text
1Selamat datang di endpoint privat! Anda berhasil terautentikasi.

Jika kredensial salah, Anda akan menerima respons error 401 Unauthorized.


Kesimpulan

Dengan langkah-langkah di atas, Anda telah berhasil membuat middleware Basic Authentication di Golang menggunakan httprouter. Middleware ini adalah solusi sederhana untuk melindungi endpoint API, meskipun kurang aman untuk penggunaan di lingkungan produksi jika tidak dilengkapi protokol HTTPS.

Untuk pengamanan lebih baik, pertimbangkan menggunakan metode autentikasi yang lebih modern, seperti OAuth atau JWT, di aplikasi produksi Anda. Selamat mencoba!

Artikel Terkait

💬 Komentar