Monday, May 7, 2012

Spring Web Application part 3: Integrasi Spring-Security


Sesi kali ini merupakan lanjutan dari sesi sebelumnya, yaitu Spring Web Application part 2: Dynamic Tiles. Jadi agar mempermudah, saya akan melanjutkan project yang dihasilkan dari tutorial sebelumnya. Kali ini saya akan mencoba membahas bagaimana melakukan integrasi spring security pada aplikasi web yang sudah kita buat sebelumnya.


Library tambahan yang dibutuhkan:

Modifikasi konfigurasi:
Tambahkan filter spring-security pada web.xml:
<filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>

Tambahkan import security-context.xml pada applicationContext.xml

<import resource="security-context.xml" />


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:security= "http://www.springframework.org/schema/security"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 http://www.springframework.org/schema/security
 http://www.springframework.org/schema/security/spring-security-3.1.xsd">
    <security:global-method-security secured-annotations="enabled" />
   <security:http auto-config="true" use-expressions="true">
        <intercept-url pattern="/login" access="permitAll"/>
        <intercept-url pattern="/login?login_error*" access="permitAll"/>
        <intercept-url pattern="/resources/**" access="permitAll" />
        <intercept-url pattern="/**" access="isAuthenticated()" />
        <form-login login-page="/login" default-target-url="/" always-use-default-target="true" authentication-failure-url="/login?login_error=1" />
        <logout />
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>        
        <!-- 
            user:rahasia
            alex:rahasia
        --> 
            <security:password-encoder hash="sha"  />
            <security:user-service>
                <security:user name="admin" password="829b36babd21be519fa5f9353daf5dbdb796993e" authorities="ROLE_ADMIN" />
                <security:user name="user" password="829b36babd21be519fa5f9353daf5dbdb796993e" authorities="ROLE_USER" />
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>
</beans:beans>

Penjelasan security-context.xml:

<intercept-url pattern="/login" access="permitAll"/>
Baris ini mendefinisikan bahwa url dengan pattern /login dapat diakses oleh siapa saja.

<intercept-url pattern="/login?login_error*" access="permitAll"/>
Baris ini mendefinisikan bahwa url dengan pattern /login?login_error* dapat diakses oleh siapa saja.


<intercept-url pattern="/resources/**" access="permitAll" />
Baris ini juga mendefinisikan bahwa semua file dalam path /resources dapat diakses oleh semua. Hal ini dilakukan karena folder resource menyimpan css, js, image, dan resource lain yang harus dapat diakses langsung oleh client.

<intercept-url pattern="/**" access="isAuthenticated()" />
Baris ini mendefinisikan semua url path (kecuali yang telah didefinisikan di sebelumnya) hanya bias diakses oleh user yang terautentikasi.

<form-login login-page="/login" default-target-url="/" authentication-failure-url="/login?login_error=1" />
Baris ini akan meng-override path login default menjadi /login. Jika login berhasil, maka default url target yang ditampilkan adalah “/” atau root aplikasi ini. Jika login gagal, maka url yang ditampilkan adalah /login?login_error=1.

Di dalam tag <security:authentication-provider>, kita dapat medefinisikan bagaimana spring-security akan melakukan autentikasi user. Pada contoh kali ini, kita akan melakukan proses autentikasi yang paling sederhana, dimana masing-masing user yang dapat login didefinisikan di dalam tag <security:user-service>, dengan enkripsi password menggunakan sha.

Kemudian langkah berikutnya, kita akan membuat halaman login. Seperti contoh pada seri tutorial sebelumnya, kita perlu membuat controller dengan anotasi url @RequestMapping(value=”/login”). Pada contoh kali ini, anotasi tersebut saya letakkan pada LoginController.



package com.springwebapp.controller;

import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping(value="/login")
public class LoginController {
    private static final Logger logger = Logger.getLogger(LoginController.class);    
    @RequestMapping(value="")
    public ModelAndView login(HttpServletRequest request) {
        logger.info("request:" + request.getRequestURL());
        ModelAndView mv = new ModelAndView("login/login");
        return mv;
    }
}

Seperti kita lihat isi file LoginController berikut, kita hanya perlu melakukan rendering view untuk halaman login. Karena proses logic untuk autentikasi akan ditangani oleh spring-security. Agar autentikasi dapat diproses oleh spring-security, kita perlu mengirimkan field j_username dan j_password ke url j_spring_security_check.

Berikut ini contoh view untuk form login pada file /WEB-INF/jsp/login/login.jsp

<%@page import="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<link rel="stylesheet" href="<%=request.getContextPath()%>/resources/css/login.css" />

<c:if test="${not empty param.login_error}">
    <div id="t_a_ui_helper_message" class="error_message">
        ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
    </div>
</c:if>
<div class="center_content">
    <form id="loginForm" action="<c:url value='j_spring_security_check'/>" method="POST">
        <ul>
            <li>
                <label for="username">username</label><br/>
                <input type="text" id="username" name="<%=UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY%>" placeholder="Username" />
            </li>
            <li>
                <label for="password">password</label><br/>
                <input type="password" id="password" name="<%=UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY%>" placeholder="Password" />
            </li>
            <li>
                <input type="submit" value="<fmt:message key="login.submit"/>" />
            </li>
        </ul>
    </form>
</div>

${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message} berfungsi untuk menampilkan pesan kesalahan jika gagal dalam proses autentikasi.

Dengan konfigurasi yang relatif cukup sederhana seperti ini, kita sudah mendapatkan proses autentikasi yang cukup dapat diandalkan tanpa perlu melakukan pengecekan autentikasi di setiap halaman web. Spring-security juga mendukung proses autentikasi menggunakan JDBC, OpenID, dan lain-lain hingga jika kita ingin membuat proses autentikasi custom. Untuk lebih jelas tentang spring-security, dapat dilihat di situsnya: http://www.springsource.org/spring-security.

Seperti biasa, untuk lebih jelasnya, source code lengkap bisa dilihat pada repository yang saya sediakan di https://bitbucket.org/anggabastian/springwebapp.

No comments:

Post a Comment