Wednesday, March 31, 2010

Basic Authentication with Spring Security (Part 2)

So I double checked my configuration, nothing seems to be wrong.  In the end, I managed to get it to work for the first time with following added configuration:
In web.xml
<login-config>
    <auth-method>BASIC</auth-method>
</login-config>
<error-page>
    <error-code>401</error-code>
    <location>/WEB-INF/jsp/errors/401.jsp</location>
</error-page>
401.jsp
<%
  response.setHeader("WWW-Authenticate", "BASIC realm=\"My Realm\"");
%>
<html>
    <head>
        <meta http-equiv="refresh" content="1;url=https://www.mysite.com/subscribe/">
    </head>
    <body>
        <h1>HTTP Status 401</h1><br>
        Unauthorized Access.  Redirecting to registration page...
    </body>
</html>

Update: I was able to get the application running without above configuration.  All I need is moving the response.setHeader code to my welcome-file specified in the web.xml file, in my case, index.jsp.
<%
  response.setHeader("WWW-Authenticate",
      "BASIC realm=\"My Realm\"");
%>
<%@ page language="java"
    contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:redirect url="/home.do" />

Although I specified a logout in Spring Security configuration, it doesn't work very well.  Unless I close down all browsers opened at the same time, I'll be logged right back in by the browser's memory cache.  So I ended up adding a "You are now logged out!  Please close the main window to clean up browser cache!" message in the logout.html page.  And finally, Business decided to remove the logout link on the page.  Looks like a form-based authentication is a better choice.

Tuesday, March 30, 2010

Basic Authentication with Spring Security (Part 1)

About two years back, I learned to authenticate a user using CAS.  Back then, we were using a business customized framework integrated with Spring and Struts.  And there are a lot Java coding in the default jsp page to make the authentication work.

This year, I'm working on migrating an old project to Spring framework.  For the web application, I decide to use Spring Security to replace the custom Basic Authentication machanism implemented by the previous developer.

On my first tryout, I have following added to existing configuration:
In web.xml
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>
      org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
    <listener-class>
org.springframework.security.ui.session.HttpSessionEventPublisher
    </listener-class>
</listener>

In application context file, first add schema delaration:
<beans default-autowire="byName" default-lazy-init="true"
xmlns:security="http://www.springframework.org/schema/security"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">

Then add security configuration:
<security:http auto-config="false"
    access-denied-page="/noaccess.jsp"
    session-fixation-protection="none">
<security:intercept-url pattern="/index.jsp*"
    access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/logout.*" filters="none" />
<security:intercept-url pattern="/noaccess.jsp*" filters="none" />
<security:intercept-url pattern="/*.do*" access="ROLE_USER"/>
<security:http-basic />
<security:anonymous />
<security:logout logout-url="/logout.do"
    logout-success-url="/logout.html" />
<security:concurrent-session-control max-sessions="1"
    exception-if-maximum-exceeded="true" />
</security:http>

<security:authentication-provider
    user-service-ref="authenticationProvider" />

<bean name="authenticationProvider"
    class="com.my.company.web.CustomUserDetailsService" />
So far, everything is by Spring Security's documentation.

I need to use a CustomUserDetailsService which creates a CustomUserDetails object upon authentication process.  Besides authenticate a user, I also need a Customer object from CustomUserDetails to determin whether a user has certain authority to access certain area in the same page.  And in the Java code, I can get the Customer object by following code:
public static Customer getCustomer() {
    Object obj = SecurityContextHolder.getContext()
                                     .getAuthentication().getPrincipal();
    if (obj != null && obj instanceof CustomUserDetails)
        return ((CustomUserDetails) obj).getCustomer();
    return null;
}

At this point, everything seems to be in place except that when I run the application, it always gives me a HTTP Status 401 (Unauthorized Access).