Tuesday, November 16, 2010

Integration of GWT with Spring Security




GWTSpringSecurity : This example is used to integrate Spring Security with GWT.

AuthenticationService:

package com.seewah.sample.gwtspring.client;

import com.google.gwt.core.client.GWT;

import com.google.gwt.user.client.rpc.RemoteService;

import com.google.gwt.user.client.rpc.ServiceDefTarget;

public interface AuthenticationService extends RemoteService {

public static final String SERVICE_URI = "authenticationService";

public static class Util {

public static AuthenticationServiceAsync getInstance() {

AuthenticationServiceAsync instance = (AuthenticationServiceAsync) GWT.create(AuthenticationService.class);

ServiceDefTarget target = (ServiceDefTarget) instance;

target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI);

return instance;

}

}

/**

* Authenticates user.

*

* @param username

* @param password

* @return whether authentication is successful

*/

boolean authenticate(String username, String password);

/**

* Terminates a user's security session.

*/

void logout();

}

AuthenticationServiceAsync:

package com.seewah.sample.gwtspring.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface AuthenticationServiceAsync {

public void authenticate(String username, String password, AsyncCallback callback);

public void logout(AsyncCallback callback);

}

AutoErrorHandlingAsyncCallback:

package com.seewah.sample.gwtspring.client;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.rpc.AsyncCallback;

/**

* {@link AsyncCallback} switch recognises {@link ServiceSecurityException} and

* handles it gracefully.

*

*

*

*/

public abstract class AutoErrorHandlingAsyncCallback implements AsyncCallback {

final public void onFailure(Throwable throwable) {

if (throwable instanceof ServiceSecurityException) {

Window.alert("You do not have enough privilege to carry out the operation!");

} else {

Window.alert(throwable.getMessage());

}

}

}

Demo:

package com.seewah.sample.gwtspring.client;

import com.google.gwt.core.client.EntryPoint;

import com.google.gwt.event.dom.client.ClickEvent;

import com.google.gwt.event.dom.client.ClickHandler;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.ui.Button;

import com.google.gwt.user.client.ui.HTML;

import com.google.gwt.user.client.ui.HorizontalPanel;

import com.google.gwt.user.client.ui.Hyperlink;

import com.google.gwt.user.client.ui.Label;

import com.google.gwt.user.client.ui.RootPanel;

import com.google.gwt.user.client.ui.VerticalPanel;

/**

* GWT Spring Security demo entry point.

*

*

*

*/

public class Demo implements EntryPoint, ClickHandler {

private Button loginBtn;

private Button logoutBtn;

private Label loginStatus;

private Hyperlink publicOp;

private Hyperlink protectedOp;

private AuthenticationServiceAsync authenticationServiceAsync;

private DocumentServiceAsync documentServiceAsync;

@SuppressWarnings("deprecation")

public void onModuleLoad() {

authenticationServiceAsync = AuthenticationService.Util.getInstance();

documentServiceAsync = DocumentService.Util.getInstance();

VerticalPanel outerPanel = new VerticalPanel();

HorizontalPanel loginPanel = new HorizontalPanel();

outerPanel.add(loginPanel);

loginBtn = new Button("LOG IN");

loginBtn.addClickHandler(this);

logoutBtn = new Button("LOG OUT");

logoutBtn.addClickHandler(this);

loginStatus = new Label("your are currently NOT LOGGED IN");

loginPanel.add(loginBtn);

loginPanel.add(logoutBtn);

loginPanel.add(loginStatus);

outerPanel.add(new HTML("
"));

publicOp = new Hyperlink("Get number of public publications", "");

publicOp.addClickHandler(this);

outerPanel.add(publicOp);

protectedOp = new Hyperlink("Get number of private publications", "");

protectedOp.addClickHandler(this);

outerPanel.add(protectedOp);

RootPanel.get("container").add(outerPanel);

}

public void onClick(ClickEvent event) {

Object sender = event.getSource();

if (sender == loginBtn) {

// service does not require username and password for this demo

authenticationServiceAsync.authenticate("", "", new AutoErrorHandlingAsyncCallback() {

public void onSuccess(Boolean result) {

if (result) {

loginStatus.setText("your are currently LOGGED IN");

} else {

loginStatus.setText("your are currently NOT LOGGED IN");

}

}

});

} else if (sender == logoutBtn) {

authenticationServiceAsync.logout(new AutoErrorHandlingAsyncCallback() {

public void onSuccess(Object result) {

loginStatus.setText("your are currently NOT LOGGED IN");

}

});

} else if (sender == publicOp) {

documentServiceAsync.getNumberOfPublicPublications(new AutoErrorHandlingAsyncCallback() {

public void onSuccess(Integer result) {

Window.alert(result.toString());

}

});

} else if (sender == protectedOp) {

documentServiceAsync.getNumberOfPrivatePublications(new AutoErrorHandlingAsyncCallback() {

public void onSuccess(Integer result) {

Window.alert(result.toString());

}

});

}

}

}

DocumentService:

package com.seewah.sample.gwtspring.client;

import com.google.gwt.core.client.GWT;

import com.google.gwt.user.client.rpc.RemoteService;

import com.google.gwt.user.client.rpc.ServiceDefTarget;

/**

* Document service.

*

*

*

*/

public interface DocumentService extends RemoteService {

public static final String SERVICE_URI = "documentService";

public static class Util {

public static DocumentServiceAsync getInstance() {

DocumentServiceAsync instance = (DocumentServiceAsync) GWT.create(DocumentService.class);

ServiceDefTarget target = (ServiceDefTarget) instance;

target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI);

return instance;

}

}

/**

* Returns the number of public publications.

*

* @return

*/

int getNumberOfPublicPublications();

/**

* Returns the number of private publications.

*

* @return

* @exception ServiceSecurityException

*/

int getNumberOfPrivatePublications() throws ServiceSecurityException;

}

DocumentServiceAsync:

package com.seewah.sample.gwtspring.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface DocumentServiceAsync {

public void getNumberOfPublicPublications(AsyncCallback callback);

public void getNumberOfPrivatePublications(AsyncCallback callback);

}

ServiceSecurityException:

package com.seewah.sample.gwtspring.client;

@SuppressWarnings("serial")

public class ServiceSecurityException extends Exception {

}

DummyAuthenticationProvider:

package com.seewah.sample.gwtspring.server.security;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.AuthenticationException;

import org.springframework.security.authentication.AuthenticationProvider;

/**

* A dummy {@link AuthenticationProvider} implementation.

*

*

* @created 6 Jan 2009

*/

public class DummyAuthenticationProvider implements AuthenticationProvider {

public Authentication authenticate(Authentication authentication) throws AuthenticationException {

throw new IllegalStateException("This implementation is a dummy class, created purely so that "

+ "spring security namespace tags can be used in application context, and this method should "

+ "never be called");

}

@SuppressWarnings("rawtypes")

public boolean supports(Class clazz) {

throw new IllegalStateException("This implementation is a dummy class, created purely so that "

+ "spring security namespace tags can be used in application context, and this method should "

+ "never be called");

}

}

DummyEntryPoint:

package com.seewah.sample.gwtspring.server.security;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.AuthenticationException;

import org.springframework.security.web.AuthenticationEntryPoint;

/**

* A dummy {@link AuthenticationEntryPoint} implementation.

*

*

* @created 6 Jan 2009

*/

public class DummyEntryPoint implements AuthenticationEntryPoint {

@Override

public void commence(HttpServletRequest arg0, HttpServletResponse arg1,

AuthenticationException arg2) throws IOException, ServletException {

throw new IllegalStateException("This implementation is a dummy class, created purely so that "

+ "spring security namespace tags can be used in application context, and this method should "

+ "never be called");

}

}

AuthenticationService:

package com.seewah.sample.gwtspring.server.service;

/**

* "Spring managed" service layer interface for authentication service.

*

*

*

*/

public interface AuthenticationService {

/**

* Authenticates user.

*

* @param username

* @param password

* @return whether authentication is successful

*/

boolean authenticate(String username, String password);

/**

* Terminates a user's security session.

*/

void logout();

}

AuthenticationServiceImpl:

package com.seewah.sample.gwtspring.server.service;

import java.util.Arrays;

import org.springframework.security.core.Authentication;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.GrantedAuthorityImpl;

import org.springframework.security.core.context.SecurityContext;

import org.springframework.security.core.context.SecurityContextHolder;

import org.springframework.security.core.context.SecurityContextImpl;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import org.springframework.security.core.userdetails.User;

/**

* {@link AuthenticationService} implementation.

*

*

*

*/

public class AuthenticationServiceImpl implements AuthenticationService {

public boolean authenticate(String username, String password) {

// creating an authenticated user token for demo

// regardless of username and password values

GrantedAuthority[] authorities = new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_ADMIN") };

User user = new User("xxx", "yyy", true, true, true, true, Arrays.asList(authorities));

Authentication auth = new UsernamePasswordAuthenticationToken(user, password, Arrays.asList(authorities));

SecurityContext sc = new SecurityContextImpl();

sc.setAuthentication(auth);

SecurityContextHolder.setContext(sc);

return true;

}

public void logout() {

SecurityContextHolder.clearContext();

}

}

DocumentService:

package com.seewah.sample.gwtspring.server.service;

/**

* "Spring managed" service layer interface for document service.

*

*

*

*/

public interface DocumentService {

/**

* Returns the number of public publications.

*

* @return

*/

int getNumberOfPublicPublications();

/**

* Returns the number of private publications.

*

* @return

*/

int getNumberOfPrivatePublications();

}

DocumentServiceImpl:

package com.seewah.sample.gwtspring.server.service;

import org.springframework.beans.factory.annotation.Required;

import org.springframework.security.access.annotation.Secured;

/**

* {@link DocumentService} implementation.

*

*

*

*/

public class DocumentServiceImpl implements DocumentService {

private int privatePublicationCount;

private int publicPublicationCount;

@Required

public void setPrivatePublicationCount(int privatePublicationCount) {

this.privatePublicationCount = privatePublicationCount;

}

@Required

public void setPublicPublicationCount(int publicPublicationCount) {

this.publicPublicationCount = publicPublicationCount;

}

@Secured("ROLE_ADMIN")

public int getNumberOfPrivatePublications() {

return privatePublicationCount;

}

public int getNumberOfPublicPublications() {

return publicPublicationCount;

}

}

AuthenticationServiceServlet:

package com.seewah.sample.gwtspring.server;

import org.springframework.beans.factory.annotation.Autowired;

import com.seewah.sample.gwtspring.client.AuthenticationService;

/**

* {@link AuthenticationService} implementation.

*

*

*

*/

@SuppressWarnings("serial")

public class AuthenticationServiceServlet extends DependencyInjectionRemoteServiceServlet implements

AuthenticationService {

@Autowired

com.seewah.sample.gwtspring.server.service.AuthenticationServiceImpl authenticationService;

public boolean authenticate(String username, String password) {

return authenticationService.authenticate(username, password);

}

public void logout() {

authenticationService.logout();

}

}

DependencyInjectionRemoteServiceServlet:

package com.seewah.sample.gwtspring.server;

import java.lang.reflect.Field;

import java.util.HashSet;

import java.util.Set;

import javax.servlet.ServletException;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**

* {@link RemoteServiceServlet} that automatically injects IoC dependency.

* "org.springframework.beans.factory.annotation.Autowired" annotation is used

* for marking which fields to inject into.

Note that the current

* implementation will only inject "declared" fields, and not inherited fields.

* Fields can be private, protected, package or public.

*

*

* @created 27 Jun 2008

*/

@SuppressWarnings("serial")

public class DependencyInjectionRemoteServiceServlet extends RemoteServiceServlet {

@Override

public void init() throws ServletException {

super.init();

doDependencyInjection();

}

/**

* Carries out dependency injection. This implementation uses Spring IoC

* container.

*

* @exception NoSuchBeanDefinitionException

* if a suitable bean cannot be found in the Spring

* application context. The current implementation looks up

* beans by name

*/

protected void doDependencyInjection() {

for (Field field : getFieldsToDependencyInject()) {

try {

boolean isFieldAccessible = field.isAccessible();

if (!isFieldAccessible) {

field.setAccessible(true);

}

field.set(this, WebApplicationContextUtils.getWebApplicationContext(getServletContext()).getBean(field.getName()));

if (!isFieldAccessible) {

field.setAccessible(false);

}

} catch (IllegalArgumentException e) {

throw new RuntimeException(e);

} catch (IllegalAccessException e) {

throw new RuntimeException(e);

}

}

}

/**

* Find annotated fields to inject.

*

* @return a list of all the annotated fields

*/

private Set getFieldsToDependencyInject() {

Set fieldsToInject = new HashSet();

Field[] fields = this.getClass().getDeclaredFields();

for (Field field : fields) {

if (field.getAnnotation(Autowired.class) != null) {

fieldsToInject.add(field);

}

}

return fieldsToInject;

}

}

DocumentServiceServlet:

package com.seewah.sample.gwtspring.server;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.access.AccessDeniedException;

import com.seewah.sample.gwtspring.client.DocumentService;

import com.seewah.sample.gwtspring.client.ServiceSecurityException;

@SuppressWarnings("serial")

public class DocumentServiceServlet extends DependencyInjectionRemoteServiceServlet implements DocumentService {

@Autowired

com.seewah.sample.gwtspring.server.service.DocumentService documentService;

public int getNumberOfPrivatePublications() throws ServiceSecurityException {

try {

return documentService.getNumberOfPrivatePublications();

} catch (AccessDeniedException e) {

throw new ServiceSecurityException();

}

}

public int getNumberOfPublicPublications() {

return documentService.getNumberOfPublicPublications();

}

}

gwtspring.gwt.xml:

applicationContext-security.xml:

xmlns:security="http://www.springframework.org/schema/security"

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/security

http://www.springframework.org/schema/security/spring-security-3.0.xsd">

create-session="always" />

class="com.seewah.sample.gwtspring.server.security.DummyEntryPoint" />

class="com.seewah.sample.gwtspring.server.security.DummyAuthenticationProvider">

class="org.springframework.security.authentication.ProviderManager">

secured-annotations="enabled" jsr250-annotations="disabled" />

applicationContext.xml:

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">

class="com.seewah.sample.gwtspring.server.service.AuthenticationServiceImpl" />

class="com.seewah.sample.gwtspring.server.service.DocumentServiceImpl">

1

100

web.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


gwtspring.html


GoogleAssist: GWT + Spring Security, GWT with Spring Security Example, How to integrate Spring Security with GWT Technology

GWT Spring Security integration demo

5 comments:

Unknown said...

Hello,
nice entry to show how to handle GWT and Spring together.
Could you share the Eclipse project (or just all source files) too?
I think at the end of the post (the xml files) is something going wrong.

Thanks a lot .

Siddharatha Dhumale said...

Hi Stephan Beutel

Thanks for your comments.

Can you please share your email id. I will send all java source file and xml file for the same to you. As size of this POC (Integration of GWT with Spring Security)is 28 MB including jar file in lib, I will delete jar file from lib and will send whole project intact to you. So that while to make it in running mode you just need to add jar in lib.

Regards
Siddharatha Dhumale
+91 - 9850901565
India.

Siddharatha Dhumale said...

Hi Stephan B

I had uploaded whole project along with jar file :-) at below given link. Thanks to google Doc for assisting developer like me who always look for free space on net .....

https://docs.google.com/leaf?id=0B1EOBpl9Kvo1NzViNjA1ZjEtNmZlNy00ZmI2LTkwN2MtMTNlOGRkYjUxZWJi&hl=en

Do let me know if you had any issue and send me ur mail id so that same can be send through mail to you.
Take Care.

Regards
Siddharatha Dhumale.

Unknown said...

Hello Siddhartha,

thank you for uploading the project.
Yesterday I found a similar (the same?) blog entry in another blog:

http://seewah.blogspot.com/2009/02/gwt-and-spring-security.html

And here's a link to download the eclipse project.
http://seewah.blogspot.com/2009/06/gwt-and-spring-security-sample-demo.html

The original link isn't available anymore, but there's a comment with a link to an updated project using Spring 3.0.3 and GWT 2.1.0 M1.

I used this sources and updated it to use with Spring 3.0.5 and GWT 2.1.1. Additionally I integrated the dependency management with Maven to fit for my project.

At the moment I'm working on a solution showing a login window to secure the whole GWT application.

Cheers
Stephan

Siddharatha Dhumale said...

Hi Stephan

Thanks for update.I had also taken help from the same block :-).I would suggest you to use Spring 3.0.5 and Latest GWT Version 2.2.0 that was released by GWT on 11th Feb 2011. In that case you will have latest code to share with JAVArites.

Regards
Siddharatha Dhumale.