Dalam dunia komputer, sebuah aplikasi web memiliki satu standar yang tidak boleh tidak ada di dalamnya, standar tersebut adalah keamanan. Kelengkapan sebuah aplikasi web ditandai oleh ada tidaknya fitur keamanan data dalam aplikasi web itu sendiri. Keamanan data digunakan aplikasi web untuk menjaga data-data dari tangan-tangan jahil yang hendak merusak data. Dengan adanya fitur keamanan dalam sebuah aplikasi web, hanya orang-orang tertentu saja yang bisa mengakses aplikasi web tersebut.
Keamanan data dalam sebuah aplikasi web, biasanya memiliki tingkatan-tingkatan tersendiri, tingkatan-tingkatan ini diwujudkan dalam konsep boleh atau tidaknya seorang user mengakses suatu data. Tingkatan-tingkatan ini disebut dengan istilah ROLE. Role ini yang akan mengatur apakah suatu user boleh mengakses suatu data atau tidak. Sebagai contoh “user dengan role ADMIN bisa mengakses seluruh data, termasuk data master, dan user dengan role USER hanya boleh mengakses data-data transaksi”. Jika dilihat dari sisi kepemilikan, satu user boleh memiliki lebih dari satu role. Sehingga skenario role ini bisa saja menjadi seperti “user dengan role ADMIN boleh mengakses data master maupun data transaksi”. Penentuan role ini sendiri biasanya bergantung dari fungsi dan kegunaan aplikasi web itu.
Untuk tutorial lanjutan kita kali ini tentang spring-boot, kita akan mencoba membuat aplikasi web yang sudah kita pelajari selama ini memiliki sistem keamanan, sehingga data-data dalam aplikasi web kita bisa terlindung dengan baik seperti layaknya standar sebuah aplikasi web. Kita juga akan mencoba mengimplementasikan tingkatan-tingkatan sistem keamanan di dalamnya. Yang tentunya menggunakan skenario boleh tidaknya suatu user dengan role tertentu mengakses suatu data dalam aplikasi web kita nantinya.
Spring framework memiliki koleksi pustaka (library) yang cukup lengkap untuk membuat sebuah aplikasi web yang kokoh, efisien dan juga efektif, termasuk sistem keamanan. Spring framework juga dilengkapi dengan pustaka keamanan (security libraries) yang cukup handal, dan banyak digunakan di seluruh dunia. Namun demikian library spring security ini memang terpisah dari distribusi spring framework ataupun spring-boot, ini dilakukan agar para pengguna spring framework bisa memilih sistem keamanan yang disukai masing-masing.. Mari kita coba impelementasikan sistem keamanan ini pada aplikasi web kita.
Bundled library yang akan kita gunakan untuk mengimplementasikan sistem keamanan dalam aplikasi web kita bernama spring-security-framework. Oleh karena library ini terpisah dari distribusi spring-boot, maka kita harus menambahkan sendiri library spring-security tersebut. Tambahkanlah kode-kode berikut ini dalam file pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
Untuk lebih mengenal seperti apa spring-security tersebut jika kita impelementasikan ke dalam aplikasi web kita, maka silahkan langsung jalankan saja aplikasi web kita. Kemudian bukalah browser dan ketiklah dalam browser alamat url http://localhost:8080/. Jika mungkin anda lupa bagaimana cara menjalankan aplikasi web ini, silahkan buka kembali tutorial sebelumnya di sini. Ingat sebelumnya kita sudah pernah membuat mapping untuk url “/” dalam controller IndexController. Apakah setelah kita eksekusi tampilan halaman web aplikasi kita ini seperti gambar berikut ini:
Jika ya, maka aplikasi web kita sudah terlindung dengan sistem keamanan. Lantas apakah kita sudah selesai? Tentu saja belum, ini baru pengenalan awal tentang spring-security. Baiklah mari kita bahas, ketika kita menambahkan library spring-boot-security-starter pada file pom.xml, spring-boot sudah langsung mengenali bahwa aplikasi web yang sedang dijalankan memiliki suatu keamanan data. Karena belum ada satupun konfigurasi yang kita lakukan untuk security ini, maka spring-security akan menganggap seluruh url dilindungi oleh keamanan, sehingga muncul prompt seperti gambar diatas. Lalu bagaimana dengan user dan password yang harus dimasukkan dalam prompt yang muncul pada aplikasi web tersebut?. Secara default spring-security mengenali “user” sebagai username dan password yang dihasilkan secara random dan dicetak di dalam konsol ketika kita menjalankan aplikasi web tersebut. Meski kita nantinya tidak akan menggunakan default setting spring-security ini, tapi ada baiknya kita coba dengan memasukkan “user” pada kolom username tersebut dan password-nya dapat anda temukan pada konsol ketika kita menjalankan aplikasi web ini, kalau di komputer saya tercetak seperti gambar dibawah ini:
Lihatlah ada tulisan default security password dalam konsol tersebut. Silahkan anda cari dalam konsol anda dan masukkan default password tersebut dalam kolom password pada prompt yang keluar tadi.
Dalam spring-security banyak sekali metode autentikasi yang bisa diimplementasikan seperti metode in memory, jdbc, openId, oauth, ldap dan lain-lain. Maksudnya spring-security bisa dikonfigurasi untuk bisa terintegrasi dengan metode-metode diatas, sehingga user-user yang bisa mengakses aplikasi web kita diambil dari sistem-sistem yang kita integrasikan. Lebih lengkapnya silahkan baca dokumentasi tentang spring-security pada link ini. Tapi untuk percobaan kita kali ini kita hanya akan mencoba dua metode saja yaitu “in memory” dan “jdbc” based authentication.
In memory user authentication maksudnya adalah user-user yang bisa mengakses web aplikasi kita di simpan dalam memory ketika aplikasi web kita jalankan, bukan di simpan dalam database, atau sistem lain seperti ldap atau openId atau yang lain. Metode “in memory user authentication” ini banyak digunakan oleh para developer aplikasi web untuk membuat mock-up aplikasi web yang tujuannya untuk presentasi atau dalam ruang lingkup development saja. Keunggulan dari metode ini adalah tidak perlu me-maintain user-user yang bisa mengakses aplikasi web tersebut. Mari kita coba metode ini.
Langkah pertama yang harus kita lakukan supaya spring-security tidak menjalankan fungsi default-nya adalah membuat sebuah class yang berfungsi sebagai class konfigurasi spring-security ini, kita beri nama class ini dengan nama “SecurityConfig” dan kita letakkan dalam package “org.josescalia.blog.simple.config”. Adapun isi dari class ini adalah sebagai berikut.
Kemudian pada fungsi yang kedua kita mendeklarasikan daftar user yang bisa mengakses aplikasi web kita, konfigurasi inilah yang saya maksud dengan “in memory user authentication”. Kita memberikan role “USER” pada user “adi” dan memberikan role “ADMIN” pada user “administrator” di fungsi kedua tersebut. Konfigurasi user authentication semacam ini sangatlah sederhana, kita tidak memerlukan penyimpanan tertentu untuk menampung user-user dengan role-role-nya. Namun kita akan sangat kerepotan jika metode ini kita terapkan dalam aplikasi web yang sebenarnya, sebab biasanya aplikasi web sebenarnya memiliki user management sendiri. Dan kita tidak mungkin meng-coding, meng-compile ulang aplikasi web tersebut setiap kali ada penambahan user atau modikasi role pada masing-masing user.
Langkah selanjutnya silahkan jalankan aplikasi web tersebut, dan jika tidak ada error pada konsol ketika kita menjalankan aplikasi web tersebut, cobalah akses url-url berikut ini, dan kemudian simpulkan sendiri tentang konfigurasi spring-security yang sudah kita buat.
Jika ya, maka aplikasi web kita sudah terlindung dengan sistem keamanan. Lantas apakah kita sudah selesai? Tentu saja belum, ini baru pengenalan awal tentang spring-security. Baiklah mari kita bahas, ketika kita menambahkan library spring-boot-security-starter pada file pom.xml, spring-boot sudah langsung mengenali bahwa aplikasi web yang sedang dijalankan memiliki suatu keamanan data. Karena belum ada satupun konfigurasi yang kita lakukan untuk security ini, maka spring-security akan menganggap seluruh url dilindungi oleh keamanan, sehingga muncul prompt seperti gambar diatas. Lalu bagaimana dengan user dan password yang harus dimasukkan dalam prompt yang muncul pada aplikasi web tersebut?. Secara default spring-security mengenali “user” sebagai username dan password yang dihasilkan secara random dan dicetak di dalam konsol ketika kita menjalankan aplikasi web tersebut. Meski kita nantinya tidak akan menggunakan default setting spring-security ini, tapi ada baiknya kita coba dengan memasukkan “user” pada kolom username tersebut dan password-nya dapat anda temukan pada konsol ketika kita menjalankan aplikasi web ini, kalau di komputer saya tercetak seperti gambar dibawah ini:
Lihatlah ada tulisan default security password dalam konsol tersebut. Silahkan anda cari dalam konsol anda dan masukkan default password tersebut dalam kolom password pada prompt yang keluar tadi.
Dalam spring-security banyak sekali metode autentikasi yang bisa diimplementasikan seperti metode in memory, jdbc, openId, oauth, ldap dan lain-lain. Maksudnya spring-security bisa dikonfigurasi untuk bisa terintegrasi dengan metode-metode diatas, sehingga user-user yang bisa mengakses aplikasi web kita diambil dari sistem-sistem yang kita integrasikan. Lebih lengkapnya silahkan baca dokumentasi tentang spring-security pada link ini. Tapi untuk percobaan kita kali ini kita hanya akan mencoba dua metode saja yaitu “in memory” dan “jdbc” based authentication.
In memory user authentication maksudnya adalah user-user yang bisa mengakses web aplikasi kita di simpan dalam memory ketika aplikasi web kita jalankan, bukan di simpan dalam database, atau sistem lain seperti ldap atau openId atau yang lain. Metode “in memory user authentication” ini banyak digunakan oleh para developer aplikasi web untuk membuat mock-up aplikasi web yang tujuannya untuk presentasi atau dalam ruang lingkup development saja. Keunggulan dari metode ini adalah tidak perlu me-maintain user-user yang bisa mengakses aplikasi web tersebut. Mari kita coba metode ini.
Langkah pertama yang harus kita lakukan supaya spring-security tidak menjalankan fungsi default-nya adalah membuat sebuah class yang berfungsi sebagai class konfigurasi spring-security ini, kita beri nama class ini dengan nama “SecurityConfig” dan kita letakkan dalam package “org.josescalia.blog.simple.config”. Adapun isi dari class ini adalah sebagai berikut.
package org.josescalia.blog.simple.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/**
* Created by josescalia on 26/02/16.
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http)throws Exception{
http.authorizeRequests()
.antMatchers("/").permitAll() //izinkan semua
.antMatchers("/css/**").permitAll() //izinkan semua
.antMatchers("/js/**").permitAll() //izinkan semua
.antMatchers("/fonts/**").permitAll() //izinkan semua
.antMatchers("/login").permitAll() //izinkan semua (termasuk default spring-security login page
.anyRequest().authenticated() //selain yang di atas harus authenticated
.and()
.formLogin() //login config
.and()
.logout() //logout config
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
;
}
@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("adi").password("adi123").roles("USER");
auth.inMemoryAuthentication().withUser("administrator").password("admin123").roles("ADMIN");
}
}
Ok, perhatikan masing-masing fungsi pada kode-kode diatas, fungsi yang pertama adalah fungsi yang berisi bagaimana kita mengkonfigurasi aplikasi web kita, disana kita lihat url-url seperti “/”, “/css/**”, “/js/**”, “/fonts/**”, “/login”, boleh diakses tanpa harus ada otentikasi. Selain url tersebut, semuanya harus diotentikasi atau memerlukan hak akses untuk bisa masuk ke dalamnya. Kenapa kita set seperti itu untuk url-url-nya, begini penjelasannya, folder css, js, fonts adalah folder yang berisi static-content yang tidak perlu di proteksi, sementara url “/login” adalah url-default milik spring-security untuk bisa menampilkan form login. Kalo url-url tersebut kita proteksi tentunya jadi lucu, gimana bisa masuk ke login form, orang login form-nya aja diproteksi, sementara jika folder css, atau js, atau fonts kita proteksi, pastinya tampilan aplikasi web kita akan hancur berantakan tanpa styling dan fungsi-fungsi action javascript yang kita buat sebelumnya tidak akan berfungsi. Kemudian masih pada fungsi yang pertama kita bisa melihat cara kita mengkonfigurasi form login dan url logout disana. Jika user sukses melakukan logout, maka user akan di arahkan ke url “/”. Kemudian pada fungsi yang kedua kita mendeklarasikan daftar user yang bisa mengakses aplikasi web kita, konfigurasi inilah yang saya maksud dengan “in memory user authentication”. Kita memberikan role “USER” pada user “adi” dan memberikan role “ADMIN” pada user “administrator” di fungsi kedua tersebut. Konfigurasi user authentication semacam ini sangatlah sederhana, kita tidak memerlukan penyimpanan tertentu untuk menampung user-user dengan role-role-nya. Namun kita akan sangat kerepotan jika metode ini kita terapkan dalam aplikasi web yang sebenarnya, sebab biasanya aplikasi web sebenarnya memiliki user management sendiri. Dan kita tidak mungkin meng-coding, meng-compile ulang aplikasi web tersebut setiap kali ada penambahan user atau modikasi role pada masing-masing user.
Langkah selanjutnya silahkan jalankan aplikasi web tersebut, dan jika tidak ada error pada konsol ketika kita menjalankan aplikasi web tersebut, cobalah akses url-url berikut ini, dan kemudian simpulkan sendiri tentang konfigurasi spring-security yang sudah kita buat.
- http://localhost:8080/
- http://localhost:8080/author/paginated_list
- http://localhost:8080/author/edit?id=1
Bagaimana kesimpulan para pembaca semua?, mudah-mudahan ada pengetahuan baru yang didapatkan dari hasil percobaan di atas.
Selanjutnya kita akan mencoba menerapkan metode “Jdbc User Authentication” sebagai implementasi spring-security yang lain dalam aplikasi web kita. "Jdbc User Authentication" adalah suatu metode dalam spring-security dimana user-user yang bisa mengakses suatu aplikasi web disimpan dalam database, dengan cara ini pemeliharaan (maintain) user management akan lebih mudah. “Jdbc User Authentication” memerlukan dua buah tabel dalam database sebagai media penyimpanan data-data user tersebut atau biasa dikenal dengan istilah “user credential”. Oleh karena aplikasi web yang kita buat dari awal sudah mendukung pembuatan tabel secara otomatis berdasarkan object model yang kita definisikan sebagai entity, maka dua tabel yang diperlukan spring-security ini pun akan kita buat menggunakan class-class model.
Class model yang pertama yang akan kita buat dalam rangkan implementasi “Jdbc User Authentication” dalam spring-security ini adalah tabel “users”. Buatlah sebuah class dengan nama “User” dan letakkan class tersebut pada package “org.josescalia.blog.simple.model”. Adapun isi dari class “User” ini adalah sebagai berikut:
package org.josescalia.blog.simple.model;
import javax.persistence.*;
/**
* Created by josescalia on 23/03/15.
*/
@Entity
@Table(name = "users")
public class User {
private String username;
private String password;
private Boolean enabled;
public User() {
}
@Id
@Column(name = "username" , length = 100, unique = true)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Column(name = "password" , length = 100)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name = "enabled")
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", enabled=" + enabled +
'}';
}
}
Class “User” ini memiliki 3 buah properti yaitu username, password, dan enable. Properti-properti ini merupakan properti minimal yang harus dimiliki oleh tabel yang akan dipergunakan dalam implementasi spring-security. Jika dikatakan minimal berarti ada properti-properti yang lain yang bisa digunakan tetapi sifatnya opsional, boleh ditambahkan boleh juga tidak, silahkan cari tahu properti-properti lain yang bisa ditambahkan dalam class “User” tersebut pada situs resmi spring-security. Pada baris kode diatas, kita men-set kolom username pada tabel users nantinya sebagai suatu data yang unique, artinya tidak boleh ada dua user dengan nama yang sama. Dan user ini juga kita set sebagai primary key pada tabel tersebut.
Kemudian class model yang kedua yang harus ada dalam implementasi spring-security adalah “Authorities”. Class “Authorities” dalam konsep spring-security merupakan object relational yang memiliki foreign key terhadap tabel users namun relasi tabel-nya bersifat many to one atau bisa dipahami dengan skenario “satu data user boleh memiliki banyak data authoritites”. Langsung saja kita buat class “Authorities” ini dan letakkan dalam package yang sama dengan letaknya class “User” sebelumnya. Adapun isi dari class “Authorities” ini adalah sebagai berikut:
Kemudian langkah selanjutnya yang akan kita lakukan adalah mengganti mekanisme ”In memory User Authentication” pada class SecurityConfig menjadi mekanisme “Jdbc User Authentication”, adapun modifikasi yang kita lakukan pada class SecurityConfig adalah seperti berikut ini:
Spring-security menggunakan service JdbcUserDetailsManager dalam menerapkan metode “Jdbc User Authentication” ini. Yang kita perlu lakukan terhadap service tersebut hanya menge-set datasource, dan datasource yang kita set untuk class JdbcUserDetailsManager ini adalah class DatabaseConfig yang sudah kita buat sebelumnya. Dan selanjutnya untuk AuthenticationManager instance dari class JdbcUserDetailsManager ini kita set ke dalam class AuthenticationManagerBuilder bersamaan dengan enkripsi password yang sudah kita definisikan. Dan tentunya kita juga menge-set datasource class AuthenticationManagerBuilder ini ke class DatabaseConfig yang sudah kita buat sebelumnya.
Sekarang langkah selanjutnya adalah jalankan aplikasi web tersebut dan perhatikan log konsol ketika aplikasi web tersebut dijalankan, seharusnya ada log yang menandakan tabel users dan tabel authorities dibuat otomatis oleh aplikasi web kita ke dalam database, seperti gambar dibawah ini:
Dalam log tersebut terlihat aplikasi web kita mendeteksi bahwa tabel users dan tabel authorities tidak ditemukan di dalam database, kemudian aplikasi web kita meng-update database tersebut dengan cara membuatkan tabel users dan table authorities ke dalam database kita. Silahkan pastikan kembali ke dalam database anda, apakah benar tabel users dan table authorities sudah terbuat secara otomatis di dalamnya.
Jika anda mencoba mengakses aplikasi web tersebut dengan kondisi seperti ini, saya jamin anda tidak akan bisa mengakses data-data dalam aplikasi web anda tersebut, kenapa demikian? Sebab beberapa url seperti "author/**" atau "book/**" atau "publisher/**" sudah diproteksi dengan spring-security bukan?, dan anda belum memiliki user dan password untuk bisa mengaksesnya, sebab tabel users dan authorites masih kosong. Bagaimana jika kita mengisi tabel-tabel tersebut secara manual?, tentu anda akan selalu gagal otentikasi ketika mencoba mengakses dengan username dan password yang anda buat secara manual, sebab kita sudah men-set enkripsi password dalam konfigurasi spring-security sebelumnya.
Lalu bagaimana cara kita untuk bisa mengatasi persoalan tersebut? Kita akan membuat sebuah class yang kita beri nama “DataInitializer”. Class ini bertugas memeriksa apakah tabel users dan authorities ada datanya atau tidak, jika tidak ada, kita akan meng-insert data default yang kita tentukan sendiri. Dan class ini harus dijalankan pada saat pertama kali aplikasi web kita boot-up. Trik ini cukup ampuh untuk mengisi aplikasi web kita dengan “pre-defined” data. Kita juga bisa mengisi data-data default seperti data author, data publisher selain data users itu sendiri. Trik ini banyak digunakan para developer aplikasi agar ketika aplikasi web itu pertama kali di-install atau deploy, data di dalamnya tidak kosong sama sekali.
Mari kita buat class “DataInitializer” tersebut. Buat class “DataInitializer” dan letakkan dalam package yang sama dengan class-class konfigurasi seperti class DatabaseConfig dan class SecurityConfig. Adapun isi dari class “DataInitializer” ini adalah sebagai berikut:
Sekarang jalankan kembali aplikasi web kita tersebut, dan cobalah akses url-url yang diproteksi dengan menggunakan username dan password yang sudah kita definisikan sebelumnya pada class “DataInitializer”. Sampai disini kita telah selesai mengimplementasi fitur keamanan dalam aplikasi web kita dengan spring-security. Mungkin pada tulisan selanjutnya kita akan mencoba meng-custom tampilan form login spring-security tersebut dan menerapkan level otorisasi hak akses pada url-url aplikasi web kita ini. Untuk source-code latihan pada tulisan ini dapat dilihat dan di akses di sini.
Semoga Bermanfaat
Depok, 28 Februari 2016
Salam
Josescalia
Kemudian class model yang kedua yang harus ada dalam implementasi spring-security adalah “Authorities”. Class “Authorities” dalam konsep spring-security merupakan object relational yang memiliki foreign key terhadap tabel users namun relasi tabel-nya bersifat many to one atau bisa dipahami dengan skenario “satu data user boleh memiliki banyak data authoritites”. Langsung saja kita buat class “Authorities” ini dan letakkan dalam package yang sama dengan letaknya class “User” sebelumnya. Adapun isi dari class “Authorities” ini adalah sebagai berikut:
package org.josescalia.blog.simple.model;
import javax.persistence.*;
/**
* Created by josescalia on 23/03/15.
*/
@Entity
@Table(name = "authorities",uniqueConstraints =@UniqueConstraint(columnNames = {"username","authority"}))
public class Authorities {
private Long id;
private User user;
private String authority;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID", length = 30)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@ManyToOne
@JoinColumn(name = "username")
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Column(name = "authority", length = 30, nullable = false)
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
@Override
public String toString() {
return "Authorities{" +
"user=" + user +
", authority='" + authority + '\'' +
'}';
}
}
Hal yang mesti kita perhatikan dalam kode-kode di atas adalah relasi antara class “Authoritites” ini dengan class “User”, pada metode getUser kita menggunakan anotasi @ManyToOne untuk menandakan bahwa hubungannya dengan class “User” ini sebagai relasi “many to one”. Kemudian juga perhatikan definisi tabel pada anotasi @Table, disana kita mendefinisikan supaya ada Unique Constraint yang merupakan gabungan kolom “username” dan kolom “authority”, artinya tidak boleh ada data yang sama pada kolom “username” dan “authority”, seperti ilustrasi data berikut ini:Kemudian langkah selanjutnya yang akan kita lakukan adalah mengganti mekanisme ”In memory User Authentication” pada class SecurityConfig menjadi mekanisme “Jdbc User Authentication”, adapun modifikasi yang kita lakukan pada class SecurityConfig adalah seperti berikut ini:
package org.josescalia.blog.simple.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import java.util.ArrayList;
import java.util.List;
/**
* Created by josescalia on 26/02/16.
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
DatabaseConfig databaseConfig;
…
@Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
/*auth.inMemoryAuthentication().withUser("adi").password("adi123").roles("USER");
auth.inMemoryAuthentication().withUser("administrator").password("admin123").roles("ADMIN");*/
JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
userDetailsService.setDataSource(databaseConfig.getDataSource());
org.springframework.security.crypto.password.PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.jdbcAuthentication().dataSource(databaseConfig.getDataSource());
}
}
Sekarang mari kita bahas kode-kode di atas, ada beberapa tambahan pada class SecurityConfig tersebut, yaitu class DatabaseConfig yang di inisialisasi dengan anotasi @Autowired dan import-import package yang diperlukan untuk mengkonfigurasi spring-security dengan metode “Jdbc User Authentication”. Namun coba perhatikan baris-baris di dalam fungsi configureGlobal, kita meng-comment metode “In Memory User Authentication” dan menggantinya dengan “Jdbc User Authentication”. Password untuk user authentication ini kita encode dengan class BcryptPasswordEncoder.Spring-security menggunakan service JdbcUserDetailsManager dalam menerapkan metode “Jdbc User Authentication” ini. Yang kita perlu lakukan terhadap service tersebut hanya menge-set datasource, dan datasource yang kita set untuk class JdbcUserDetailsManager ini adalah class DatabaseConfig yang sudah kita buat sebelumnya. Dan selanjutnya untuk AuthenticationManager instance dari class JdbcUserDetailsManager ini kita set ke dalam class AuthenticationManagerBuilder bersamaan dengan enkripsi password yang sudah kita definisikan. Dan tentunya kita juga menge-set datasource class AuthenticationManagerBuilder ini ke class DatabaseConfig yang sudah kita buat sebelumnya.
Sekarang langkah selanjutnya adalah jalankan aplikasi web tersebut dan perhatikan log konsol ketika aplikasi web tersebut dijalankan, seharusnya ada log yang menandakan tabel users dan tabel authorities dibuat otomatis oleh aplikasi web kita ke dalam database, seperti gambar dibawah ini:
Dalam log tersebut terlihat aplikasi web kita mendeteksi bahwa tabel users dan tabel authorities tidak ditemukan di dalam database, kemudian aplikasi web kita meng-update database tersebut dengan cara membuatkan tabel users dan table authorities ke dalam database kita. Silahkan pastikan kembali ke dalam database anda, apakah benar tabel users dan table authorities sudah terbuat secara otomatis di dalamnya.
Jika anda mencoba mengakses aplikasi web tersebut dengan kondisi seperti ini, saya jamin anda tidak akan bisa mengakses data-data dalam aplikasi web anda tersebut, kenapa demikian? Sebab beberapa url seperti "author/**" atau "book/**" atau "publisher/**" sudah diproteksi dengan spring-security bukan?, dan anda belum memiliki user dan password untuk bisa mengaksesnya, sebab tabel users dan authorites masih kosong. Bagaimana jika kita mengisi tabel-tabel tersebut secara manual?, tentu anda akan selalu gagal otentikasi ketika mencoba mengakses dengan username dan password yang anda buat secara manual, sebab kita sudah men-set enkripsi password dalam konfigurasi spring-security sebelumnya.
Lalu bagaimana cara kita untuk bisa mengatasi persoalan tersebut? Kita akan membuat sebuah class yang kita beri nama “DataInitializer”. Class ini bertugas memeriksa apakah tabel users dan authorities ada datanya atau tidak, jika tidak ada, kita akan meng-insert data default yang kita tentukan sendiri. Dan class ini harus dijalankan pada saat pertama kali aplikasi web kita boot-up. Trik ini cukup ampuh untuk mengisi aplikasi web kita dengan “pre-defined” data. Kita juga bisa mengisi data-data default seperti data author, data publisher selain data users itu sendiri. Trik ini banyak digunakan para developer aplikasi agar ketika aplikasi web itu pertama kali di-install atau deploy, data di dalamnya tidak kosong sama sekali.
Mari kita buat class “DataInitializer” tersebut. Buat class “DataInitializer” dan letakkan dalam package yang sama dengan class-class konfigurasi seperti class DatabaseConfig dan class SecurityConfig. Adapun isi dari class “DataInitializer” ini adalah sebagai berikut:
package org.josescalia.blog.simple.config;
import org.apache.log4j.Logger;
import org.josescalia.blog.simple.model.Author;
import org.josescalia.blog.simple.model.Book;
import org.josescalia.blog.simple.model.Publisher;
import org.josescalia.blog.simple.repository.AuthorRepository;
import org.josescalia.blog.simple.repository.BookRepository;
import org.josescalia.blog.simple.repository.PublisherRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
/**
* Created by josescalia on 03/02/16.
*/
@Component
public class DataInitializer {
static Logger logger = Logger.getLogger(DataInitializer.class.getName());
@Autowired
DatabaseConfig databaseConfig;
@PostConstruct //need to trigger this to fill up first Data
public void initData(){
logger.info("Init Data Invoked");
/*harus di taro disini sebab spring-security akan menginisialiasi spring security lebih dahulu, baru kemudian mengeksekusi @PostConstruct untuk pengisian data*/
JdbcUserDetailsManager userDetailsService = new JdbcUserDetailsManager();
userDetailsService.setDataSource(databaseConfig.getDataSource());
org.springframework.security.crypto.password.PasswordEncoder encoder = new BCryptPasswordEncoder();
if(!userDetailsService.userExists("user")) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
UserDetails userDetails = new User("user", encoder.encode("password"), authorities);
userDetailsService.createUser(userDetails);
}
if(!userDetailsService.userExists("administrator")){
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
UserDetails userDetails = new User("administrator",encoder.encode("admin123"),authorities);
userDetailsService.createUser(userDetails);
}
}
}
Class “DataInitializer” ini memiliki satu buah fungsi yang bernama “InitData” dan fungsi ini diberikan anotasi @PostConstruct yang menandakan bahwa class ini akan dijalankan setiap kali aplikasi web kita dijalankan pertama kali. Isi dari fungsi ini kurang lebih sama dengan skenario kita yaitu memeriksa tabel users, jika user “administrator” dan “user” tidak ada dalam database maka buatlah kedua user tersebut dan masukkan ke dalam database. Kita membuat dua buah user default dengan detail user dengan nama “user” memiliki password “password” dan memiliki role sebagai “ROLE_USER”, dan kemudian user dengan nama “administrator” memiliki password “admin123” dan memiliki “ROLE_USER” dan “ROLE_ADMIN” sebagai authority-nya. Sekarang jalankan kembali aplikasi web kita tersebut, dan cobalah akses url-url yang diproteksi dengan menggunakan username dan password yang sudah kita definisikan sebelumnya pada class “DataInitializer”. Sampai disini kita telah selesai mengimplementasi fitur keamanan dalam aplikasi web kita dengan spring-security. Mungkin pada tulisan selanjutnya kita akan mencoba meng-custom tampilan form login spring-security tersebut dan menerapkan level otorisasi hak akses pada url-url aplikasi web kita ini. Untuk source-code latihan pada tulisan ini dapat dilihat dan di akses di sini.
Semoga Bermanfaat
Depok, 28 Februari 2016
Salam
Josescalia
No comments:
Post a Comment