GWT version- 2.1
Spring Security 3.0.4
To integrate GWT with Spring security, just look at Spring security on its own. Another advantage of pulling the login page out of GWT framework is that the load time of initial page becomes much smaller. This also helps in DOS kind of attacks.
To integrate Spring first pull the required jars into maven. Here I am assuming the Spring is available in your environment otherwise you will have to pull extra jars for Spring core and others dependency
<dependency> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
Now put a login page in your web app root, which looks like as follows. Note that the form should be posted on /j_spring_security_check and the username name should be j_username and password should be j_password
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Lalit Bhatt</title>
<link rel="shortcut icon" type="image/x-icon" href="resources/favicon.ico">
</head>
<body>
<form method="post" action="/j_spring_security_check">
<label for="j_username">Username:</label>
<input type="text" name="j_username" />
<br />
<label for="j_password">Password:</label>
<input type="password" name="j_password" />
<br />
<input type="submit" value="Login" />
</form>
</body>
</html>
Put the Spring application context for security configuration at a suitable location, may be inside META-INF under webapps. A typical context file would look like
<?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:util="http://www.springframework.org/schema/util"
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-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<!-- HTTP security configurations -->
<http auto-config='true'>
<intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/resources/favicon.ico" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<logout logout-success-url="/login.html" />
<form-login login-page="/login.html" default-target-url="/index.gwt.html"
always-use-default-target="true" />
<session-management invalid-session-url="/login.html"
session-authentication-error-url="/login.html"
session-fixation-protection="newSession">
<concurrency-control max-sessions="1"
error-if-maximum-exceeded="false" />
</session-management>
</http>
<authentication-manager>
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource" />
</authentication-provider>
</authentication-manager>
</beans:beans>
This is a very very basic context XML. It assumes everything as per defaults. It assumes that you have the user table and authorities table as per the need of spring security. Any difference would need a customization of Spring security which again has nothing to do with GWT.
The final thing is to register the configurations in web.xml, to start the Spring container and to hook the security filters in place
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/application*.xml</param-value>
</context-param>
<!-- Creates the Spring Container -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>
If everything is fallen in place, then accessing the application will take you to login page first.
Handling session time out
This is quite tricky as when session time out happens, the GWT rpc call returns back the login page. The one way that I found non intrusive to handle is to first make an adapter to AsyncCallback and all the instantiations of AsyncCallback should happen using the adapter. This is a good way to handle all the failures in a common place.
public abstract class AsyncCallbackAdapter<T> implements AsyncCallback<T> {
@Override
public void onFailure(Throwable caught) {
if (caught instanceof InvocationException) {
InvocationException ie = (InvocationException) caught;
if(ie.getMessage().contains("j_spring_security_check"))
{
Window.alert("Session is timed out. Please login again");
Window.open(GWT.getHostPageBaseURL() + "login.jsp", "_self", null);
return;
}
}
//Do other error handling here
}
The problem I run into with this is now I'm on index.gwt.html, but teh client side knows nothign abotu who logged in
ReplyDeleteI guess when you are in login page it is not possible to know who has logged in till credentials are entered. Also are you planning to use GWT in a new application or working on some legacy code base?
DeleteLegacy code base
DeleteDo you have a working project checked in someplace that I can download and play with?
DeleteIt was wondering if I could use this write-up on my other website, I will link it back to your website though.Great Thanks.
ReplyDeleteSure..no problem.
Delete