Belajar Membuat Dialog Login Di Griffon


Pada panduan sederhana ini, saya akan membuat sebuah aplikasi desktop yang menampilkan dialog login. Program baru akan ditampilkan bila dialog di-isi dengan nama pengguna dan password yang benar. Untuk keperluan tersebut, saya akan menggunakan Griffon 1.5 dan JXLoginPane dari SwingX. Untuk melakukan akses database, saya akan memakai simple-jpa 0.6. Karena simple-jpa sudah menyertakan SwingX builder, saya tidak perlu men-install swingx-builder lagi.

Langkah pertama yang saya lakukan adalah membuat sebuah proyek baru dengan perintah berikut ini:

C:\> griffon create-app login
C:\> cd login
C:\login> _

Setelah proyek baru dibuat, saya akan men-install plugin simple-jpa 0.6 dengan menggunakan perintah berikut ini:

C:\login> griffon install-plugin simple-jpa 0.6

Saya perlu menyiapkan koneksi ke sebuah database MySQL di komputer lokal dengan memberikan perintah berikut ini:

C:\login> griffon create-simple-jpa -user=snake -password=12345 -database=latihanLogin -rootPassword=admin12345

Bila ingin memakai database yang sudah ada, saya perlu menambahkan perintah -skip-database pada perintah di atas.

Karena terbiasa memakai IDE, saya akan membuat proyek IntelliJ IDEA dengan memberikan perintah berikut ini:

C:\login> griffon integrate-with --idea

Setelah ini, saya membuka file proyek yang dihasilkan dengan memilih menu File, Open… di IntelliJ IDEA.

Saya kemudian membuka file LoginController.groovy dan mengubahnya menjadi seperti berikut ini:

package login

import domain.DatabaseLoginService
import org.jdesktop.swingx.JXLoginPane

class LoginController {

    def view

    void mvcGroupInit(Map args) {
        DatabaseLoginService loginService = new DatabaseLoginService()
        loginService.buatUserDefault()
        execInsideUISync {
            JXLoginPane panel = new JXLoginPane(loginService)
            JXLoginPane.Status status = JXLoginPane.showLoginDialog(app.windowManager.getStartingWindow(), panel)
            if (status != JXLoginPane.Status.SUCCEEDED) {
                app.shutdown()
            }
        }
    }

}

Pada kode program di atas, saya memakai JXLoginPane dari SwingX. Selain itu, saya menggunakan app.windowManager.getStartingWindow() untuk mendapatkan JFrame yang biasanya ditampilkan pertama kali (sesuai dengan konfigurasi di Application.groovy).

Berikutnya, saya perlu membuat kode program untuk DatabaseLoginService yang akan memeriksa apakah login sukses atau tidak berdasarkan informasi yang tersimpan di database. Ini adalah kanditat yang tepat untuk services. Griffon mendukung application services dengan perintah create-service. Di Griffon, services adalah artifact sama seperti controller. Akan tetapi, agar sederhana, saya tidak akan menggunakan fasilitas dari Griffon tersebut. Saya akan menganggap DatabaseLoginService sebagai domain service (bila mengikuti domain driven design, domain juga memiliki services). Untuk itu, saya membuat file src\main\domain\DatabaseLoginService.groovy yang isinya seperti berikut ini:

package domain

import org.jdesktop.swingx.auth.LoginService
import simplejpa.transaction.Transaction

@Transaction
class DatabaseLoginService extends LoginService {

    @Override
    boolean authenticate(String nama, char[] password, String server) throws Exception {
        return false
    }

    void buatUserDefault() {}

}

Setelah itu, saya menambahkan import domain.DatabaseLoginService pada file LoginController.groovy.

Sampai disini, bila saya menjalankan kode program, saya akan memperoleh tampilan seperti pada gambar berikut ini:

Tampilan Dialog Login

Tampilan Dialog Login

Bila saya mengisi dengan nama pengguna atau password yang saya, saya akan memperoleh tampilan seperti pada gambar berikut ini:

Tampilan Dialog Login Bila Terjadi Kesalahan

Tampilan Dialog Login Bila Terjadi Kesalahan

Apapun yang saya isi, login tidak akan pernah sukses, karena saat ini method DatabaseLoginService.authenticate() selalu mengembalikan nilai false.

Saya akan membuat sebuah domain class yang mewakili pengguna. Class ini akan saya beri nama Pengguna. Untuk itu, saya memilih menu Tools, Griffon, Run Target, lalu mengisinya dengan:

create-domain-class Pengguna

Setelah itu, saya mengubah kode program Pengguna.groovy yang dihasilkan menjadi seperti berikut ini:

package domain

import groovy.transform.*
import simplejpa.DomainClass
import javax.persistence.*
import javax.validation.constraints.*
import org.hibernate.validator.constraints.*
import java.security.MessageDigest

@DomainClass @Entity @Canonical
class Pengguna {

    @NotBlank @Size(min=2, max=50)
    String nama

    @NotNull
    byte[] passwordHash

    private byte[] getMD5(String plain) {
        MessageDigest digester = MessageDigest.getInstance('MD5')
        digester.update(plain.bytes)
        digester.digest()
    }

    void setPassword(String password) {
        passwordHash = getMD5(password)
    }

    boolean login(String password) {
        if (password.isAllWhitespace()) return false 
        Arrays.equals(passwordHash, getMD5(password))
    }

}

Pada class di atas, saya tidak menyimpan password secara langsung melainkan hanya MD5 hash-nya saja. Walaupun tidak sangat aman, menyimpan dalam bentuk MD5 hash jauh lebih baik daripada menyimpan password apa adanya. Hal ini karena orang yang melihat isi tabel secara langsung tetap tidak bisa mengetahui apa password yang harus diketik. Saya memilih menyimpan passwordHash sebagai byte[]. Secara default, Hibernate JPA akan memetakan byte[] dengan sebuah kolom BLOB di tabel MySQL.

Untuk memastikan tidak ada yang salah pada class di atas, saya akan membuat sebuah unit test dengan memberikan perintah berikut ini:

create-unit-test Pengguna

Saya kemudian mengisi file PenggunaTests yang dihasilkan menjadi seperti berikut ini:

package login

import domain.Pengguna
import griffon.test.*

class PenggunaTests extends GriffonUnitTestCase {

    protected void setUp() {
        super.setUp()
    }

    protected void tearDown() {
        super.tearDown()
    }

    void testLogin() {
        Pengguna pengguna = new Pengguna(nama: 'Solid')
        pengguna.setPassword('Snake')

        // Kasus password benar
        assertTrue(pengguna.login('Snake'))

        // Kasus password salah
        assertFalse(pengguna.login('Liquid Snake'))
        assertFalse(pengguna.login(''))
    }
}

Saya kemudian menghapus file LoginControllerTests.groovy dan LoginModelTests.groovy (yang dihasilkan oleh Griffon) karena mereka tidak berisi test case dan tidak diperlukan pada proyek latihan ini.

Berikutnya saya perlu menjalankan unit test. Untuk itu, saya memberikan perintah berikut ini:

test-app unit:

Bila semuanya sesuai dengan harapan, saya akan harus memperoleh hasil seperti berikut ini:

...
-------------------------------------------------------
Running 1 unit test...
Running test login.PenggunaTests...PASSED
Tests Completed in 562ms ...
-------------------------------------------------------
Tests passed: 1
Tests failed: 0
-------------------------------------------------------

Setelah yakin bahwa kode program Pengguna sudah dapat berfungsi sebagaimana seharusnya, kini saat yang tepat untuk mengubah kode program DatabaseLoginService menjadi seperti berikut ini:

package domain

import org.jdesktop.swingx.auth.LoginService
import simplejpa.transaction.Transaction

@Transaction
class DatabaseLoginService extends LoginService {

    @Override
    boolean authenticate(String nama, char[] password, String server) throws Exception {
        Pengguna pengguna = findPenggunaByNama(nama)
        pengguna?.login(String.valueOf(password))

    }

    void buatUserDefault() {
        if (!findPenggunaByNama('Solid')) {
            Pengguna me = new Pengguna(nama: 'Solid')
            me.password = 'Snake'
            persist(me)
        }
    }

}

Pada kode program di atas, method authenticate() akan dipanggil untuk memeriksa apakah password benar atau salah. Selain itu, juga ada method buatUserDefault() yang akan membuat sebuah pengguna dengan nama dan password default (bila belum ada) sehingga setidaknya saya bisa login ke aplikasi walaupun database masih kosong.

Sekarang, saya dapat mencoba menjalankan aplikasi. Pada proyek latihan ini, saya bisa masuk ke dialog utama hanya bila mengisi nama pengguna dengan Solid dan password berupa Snake.

Perihal Solid Snake
I'm nothing...

3 Responses to Belajar Membuat Dialog Login Di Griffon

  1. Tolhah Hamzah mengatakan:

    berhasil! dengan beberapa modifikasi : D
    thanks banget mas : D

    mau tanya lagi mas.. kalau untuk penyimpanan login session, cara sederhananya gimana ya mas? sehingga session tersebut tetap hidup meski berpindah2 page.. lalu bisa dipakai saat input transaksi, misalnya untuk mengisi default value di pengguna_id… mirip seperti mekanisme $_SESSION di php pada umumnya..

    apakah ada referensi untuk melakukan hal ini, mas? atau bisa diimplementasikan dengan membuat atribut @Bindable String usernameSession dan @Bindable String levelSession di model MainGroupModel.groovy lalu memberikan nilai tertentu..

    mohon bantuannya mas,
    terima kasih banyak atas jawabannya : )

    • Solid Snake mengatakan:

      Pada aplikasi desktop, pengguna selalu berada pada session yang sama. Tidak dibutuhkan session id karena hanya satu pengguna yang bisa login pada saat bersamaan.

      Yang perlu kamu lakukan adalah menyimpan object Pengguna pada sebuah tempat dimana ia bisa diakses dengan mudah. Sebagai contoh, cara yang paling gampang adalah menyimpannya sebagai variabel statis, seperti:

      class DatabaseLoginService extends LoginServce {
         public static Pengguna currentPengguna;
      
         ...
      
         @Override
         boolean authenticate(String nama, char[] password, String server) throws Exception {
              Pengguna pengguna = findPenggunaByNama(nama)
              if (pengguna?.login(String.valueOf(password))) {
                     currentPengguna = pengguna
                     return true
              }
              false
         }
      }
      

      Pada lokasi lain, saya dapat memakai kode program seperti berikut ini untuk memeriksa informasi pengguna yang telah login (atau null bila belum login):

      println "Nama pengguna yang sedang login adalah ${DatabaseLoginService.currentPengguna}"
      

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: