Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
01 Feb 2025 · 6 mnt baca ·Artikel 74 / 119
Go

15 Membuat Middleware Session Based Authentication Pada Golang

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

Pada artikel kali ini, kita akan membahas langkah-langkah pembuatan middleware untuk Session-based Authentication menggunakan Golang dan library httprouter. Middleware ini berguna untuk memvalidasi sesi pengguna di setiap request yang masuk ke aplikasi.

Apa itu Session-based Authentication?

Session-based authentication adalah metode otentikasi di mana pengguna login melalui aplikasi dan server menyimpan informasi tentang sesi pengguna dalam bentuk session ID yang disimpan di server atau dalam cookie di browser pengguna. Setiap kali pengguna mengakses resource yang dilindungi, server akan memverifikasi sesi tersebut.

Untuk contoh kali ini, kita akan menggunakan httprouter, sebuah router HTTP yang sangat cepat dan simpel di Golang, untuk membuat middleware autentikasi berbasis sesi.

Langkah 1: Persiapan Proyek Golang

Jika belum, pastikan proyek Golang telah disiapkan dengan langkah berikut:

  1. Buat direktori proyek di terminal atau melalui IDE favorit Anda.
  2. Inisialisasi proyek Go.
    bash
    1go mod init session-auth-example

Langkah 2: Menginstal Dependensi

Pertama, kita perlu menambahkan dependensi untuk httprouter dan beberapa dependensi lain yang dibutuhkan, seperti gorilla/sessions untuk menangani sesi pengguna.

Instalasi httprouter dan gorilla/sessions dapat dilakukan dengan menjalankan perintah:

bash
1go get github.com/julienschmidt/httprouter
2go get github.com/gorilla/sessions

Langkah 3: Menyiapkan Middleware Autentikasi

Sekarang kita akan membuat middleware untuk memverifikasi sesi yang ada di setiap permintaan HTTP. Pada tahap ini, kita memerlukan:

  1. Store dari gorilla/sessions yang akan digunakan untuk menyimpan sesi di memori.
  2. Fungsi middleware untuk memeriksa apakah sesi yang sah tersedia.

Kode Program authMiddleware.go

go
 1package main
 2
 3import (
 4    "github.com/gorilla/sessions"
 5    "github.com/julienschmidt/httprouter"
 6    "net/http"
 7    "log"
 8)
 9
10var Store *sessions.CookieStore
11
12// Session memeriksa apakah sesi pengguna valid
13func Session(next httprouter.Handle) httprouter.Handle {
14	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
15		// Mengambil session dari request
16		session, err := Store.Get(r, "session_key")
17		if err != nil {
18			http.Error(w, "Could not get session", http.StatusInternalServerError)
19			return
20		}
21
22		// Mengecek apakah sesi "user" ada
23		user, ok := session.Values["user"]
24		if !ok || user == nil {
25			http.Error(w, "Unauthorized", http.StatusUnauthorized)
26			return
27		}
28
29		// Melanjutkan permintaan jika sudah terautentikasi
30		next(w, r, ps)
31	}
32}
33
34func main() {
35    // set cookies store
36	middleware.Store = sessions.NewCookieStore([]byte("1234567890"))
37    
38    // Membuat router dan route
39    router := httprouter.New()
40    router.GET("/protected-session", Session(ProtectedSessionHandler))
41
42    // Menjalankan server
43    log.Fatal(http.ListenAndServe(":8080", router))
44}
45
46// Handler untuk halaman private yang membutuhkan otentikasi
47func ProtectedSessionHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
48	w.Write([]byte("Welcome to the session private page"))
49}

Penjelasan kode:

  1. store adalah objek gorilla/sessions yang digunakan untuk menyimpan sesi menggunakan cookie.
  2. AuthMiddleware adalah middleware yang akan mengecek apakah sesi pengguna yang valid ada dalam cookie.
  3. Jika sesi valid, middleware akan meneruskan request ke handler berikutnya; jika tidak, akan mengirimkan respons dengan status 401 Unauthorized.

Langkah 4: Menyiapkan Endpoint Login untuk Menyimpan Sesi

Untuk menguji autentikasi, kita membutuhkan endpoint untuk login. Endpoint ini akan menginisialisasi sesi saat pengguna login berhasil.

Kode Program loginHandler.go

go
 1package main
 2
 3import (
 4    "github.com/julienschmidt/httprouter"
 5    "net/http"
 6    "log"
 7)
 8
 9func LoginHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
10	// Mengambil sesi
11	session, _ := midleware.Store.Get(r, "session_key")
12
13	// Set nilai sesi user (misalnya setelah login berhasil)
14	session.Values["user"] = "authenticated-user"
15	session.Save(r, w)
16
17	// Menampilkan pesan setelah login berhasil
18	w.Write([]byte("Login berhasil"))
19}
20
21func main() {
22    // Membuat router dan route
23    router := httprouter.New()
24    router.GET("/login", LoginHandler)
25    router.GET("/protected-session", Session(ProtectedSessionHandler))
26
27    // Menjalankan server
28    log.Fatal(http.ListenAndServe(":8080", router))
29}

Penjelasan:

  • Saat pengguna mengunjungi endpoint /login, sesi untuk pengguna akan diset dengan nilai "user".
  • Dengan begitu, sesi ini dapat digunakan untuk memvalidasi permintaan selanjutnya di endpoint yang dilindungi (/private).

Langkah 5: Menulis Unit Test untuk Middleware

Membuat unit test adalah bagian penting untuk memastikan middleware berfungsi dengan baik. Di sini, kita akan menggunakan paket testing standar dari Golang untuk menulis unit test.

Kode Program authMiddleware_test.go

go
  1package main
  2
  3import (
  4    "github.com/julienschmidt/httprouter"
  5    "net/http"
  6    "net/http/httptest"
  7    "testing"
  8)
  9
 10func TestSession(t *testing.T) {
 11	type args struct {
 12		setupRequest func() *http.Request
 13	}
 14	tests := []struct {
 15		name           string
 16		args           args
 17		wantStatusCode int
 18		wantNextCalled bool
 19	}{
 20		{
 21			name: "unauthorized request should return 401",
 22			args: args{
 23				setupRequest: func() *http.Request {
 24					req := httptest.NewRequest(http.MethodGet, "/", nil)
 25
 26					// Set empty session (no user)
 27					session, _ := Store.Get(req, "session_key")
 28					_ = session.Save(req, httptest.NewRecorder())
 29
 30					return req
 31				},
 32			},
 33			wantStatusCode: http.StatusUnauthorized,
 34			wantNextCalled: false,
 35		},
 36		{
 37			name: "success authorized",
 38			args: args{
 39				setupRequest: func() *http.Request {
 40					req := httptest.NewRequest(http.MethodGet, "/", nil)
 41
 42					// Set session with user
 43					session, _ := Store.Get(req, "session_key")
 44					session.Values["user"] = "someuser"
 45					_ = session.Save(req, httptest.NewRecorder())
 46
 47					return req
 48				},
 49			},
 50			wantStatusCode: http.StatusOK,
 51			wantNextCalled: true,
 52		},
 53		{
 54			name: "error when session not found",
 55			args: args{
 56				setupRequest: func() *http.Request {
 57					// Create request with invalid session cookie (simulate corrupted cookie)
 58					req := httptest.NewRequest(http.MethodGet, "/", nil)
 59
 60					// Manually inject a bad cookie
 61					req.AddCookie(&http.Cookie{
 62						Name:  "session_key",
 63						Value: "invalid_base64",
 64					})
 65
 66					return req
 67				},
 68			},
 69			wantStatusCode: http.StatusInternalServerError,
 70			wantNextCalled: false,
 71		},
 72	}
 73
 74	// Setup global Store before tests
 75	Store = sessions.NewCookieStore([]byte("test-secret-key"))
 76
 77	for _, tt := range tests {
 78		t.Run(tt.name, func(t *testing.T) {
 79			rr := httptest.NewRecorder()
 80			req := tt.args.setupRequest()
 81			params := httprouter.Params{}
 82
 83			// Dummy next handler
 84			nextCalled := false
 85			next := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
 86				nextCalled = true
 87				w.WriteHeader(http.StatusOK)
 88			}
 89
 90			handler := Session(next)
 91			handler(rr, req, params)
 92
 93			if rr.Code != tt.wantStatusCode {
 94				t.Errorf("handler returned wrong status code: got %v, want %v", rr.Code, tt.wantStatusCode)
 95			}
 96			if nextCalled != tt.wantNextCalled {
 97				t.Errorf("next handler call = %v, want %v", nextCalled, tt.wantNextCalled)
 98			}
 99		})
100	}
101}
102
103
104func TestLogin(t *testing.T) {
105    // Membuat router dan route untuk tes login
106    router := httprouter.New()
107    router.GET("/login", loginHandler)
108
109    // Membuat request HTTP untuk login
110    req, err := http.NewRequest("GET", "/login", nil)
111    if err != nil {
112        t.Fatal(err)
113    }
114
115    // Membangun ResponseRecorder
116    rr := httptest.NewRecorder()
117    router.ServeHTTP(rr, req)
118
119    // Memeriksa apakah response berhasil login
120    if rr.Code != http.StatusOK {
121        t.Errorf("Expected status OK, got %v", rr.Code)
122    }
123}

Penjelasan Unit Test:

  1. TestAuthMiddleware: Menguji apakah middleware mengembalikan status 401 Unauthorized jika sesi pengguna tidak valid.
  2. TestLogin: Menguji apakah request login berhasil dengan status 200 OK.

Langkah 6: Kesimpulan

Dengan menggunakan Golang, kita dapat membuat middleware untuk Session-based Authentication secara efisien menggunakan httprouter. Dalam contoh ini, kita menyiapkan middleware yang memverifikasi keberadaan sesi pengguna dalam cookie untuk mengamankan endpoint.

Untuk informasi lebih lanjut mengenai Golang dan HTTP router, Anda dapat mengunjungi:

Ini adalah dasar dari bagaimana sistem autentikasi berbasis sesi dapat diimplementasikan di Golang menggunakan middleware. Jangan ragu untuk menggali lebih dalam tentang teknik-teknik dan implementasi lainnya!

Artikel Terkait

💬 Komentar