google-code-export / gwt-dispatch

Automatically exported from code.google.com/p/gwt-dispatch
1 stars 0 forks source link

Allow access to Session in RequestHandlers #38

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
I'm trying to reimplement my security RPC methods using Request/Result events, 
but I need access to the Session in the handler for the Login/Logout events.

Since I'm not using Guice, my event handlers are created at startup, so all 
requests for login go through the same instance of LoginHandler. Could a method 
be added to ExecutionContext that would return me either the 
HttpServletRequest, or the Session? It seems like it fits in with the "Context" 
name of the object.

Original issue reported on code.google.com by steve.ar...@gmail.com on 10 Jun 2010 at 8:12

GoogleCodeExporter commented 9 years ago
gwt-sl library has helper class ServletUtils which allows you to access HTTP 
request & response correctly. But you have to use it (gwt-sl) to access your 
dispatcher service. It is an integration library for Spring + GWT + other stuff.

Original comment by ladislav...@gmail.com on 7 Jul 2010 at 10:21

GoogleCodeExporter commented 9 years ago
I believe this issue would solve the following dilemma that I am facing:

Background info: We are using the GuiceSecureDispatchService and simple J2EE 
analog of the AppEngineSecureSessionValidator.

Here is the high level flow we envision:

1) User enters username and password and clicks Login

2) The action is handled on the server by a LoginActionHandler which looks up 
the username from our authentication service and validates the password.

3) If the password is a match we would like to store, on the server, a 
representation of the application specific UserSession and associate it with 
the JSESSIONID cookie provided to the browser upon first request.

4) When a secure resource is subsquently requested we can follow the model 
provided by the AppEngineSecureSessionValidator but instead of using the 
UserServiceFactory, which is GAE specific, we would look up the valid 
UserSession by using the JSESSIONID cookie.

I am pretty sure that we must rely on the JSESSIONID cookie in order provide 
the single sign on behavior we need.

Without the ability to access the Session (and potentially the 
HTTPServletRequest) in the LoginActionHandler it doesn't seem possible to 
implement this workflow or single sign on behavior  in general. Am I mistaken, 
is there some other way to access the JSESSIONID inside an ActionHandler as a 
part of gwt-dispatch? As a side note Spring integration is not an option for 
our team at the moment and it would be overkill to bring in the whole gwt-sl 
library just to get access to the JSESSIONID.

Original comment by adamben...@gmail.com on 26 Oct 2010 at 10:39

GoogleCodeExporter commented 9 years ago
In response to my previous comment, it would be very convenient to have access 
to the HttpServletRequest because there will almost certainly be information in 
the cookies that would be useful to the action handler.

And thanks in advance for any time spent to consider this issue.

Original comment by adamben...@gmail.com on 26 Oct 2010 at 11:01

GoogleCodeExporter commented 9 years ago
Binding your handlers with guice gives you access to the session, request and 
response objects via providers. They get automatically injected into the 
handler if your constructor looks something like this:
@Inject public StoreFooHandler(Logger logger, Provider<HttpSession> 
sessionProvider, Provider<HttpServletRequest> requestProvider, 
Provider<HttpServletResponse> responseProvider, PMF pmf){
...
}

e.g. if you want to access something in the session this looks like: 
this.sessionProvider.get().getAttribute("FooSessionParameter");

Hope this helps.

Original comment by pfiste...@gmail.com on 27 Oct 2010 at 8:48

GoogleCodeExporter commented 9 years ago
That did the trick, thank you!

Original comment by adamben...@gmail.com on 27 Oct 2010 at 7:37

GoogleCodeExporter commented 9 years ago
I've been using that Guice trick you mentioned to get the session, request and 
response, but I've been reading up on cold boot times on appengine and it seems 
that getting rid of Guice helps improve load times by a lot.

http://turbomanage.wordpress.com/2010/03/26/appengine-cold-starts-considered/

It would be nice to get access to them via the execute method of the 
dispatcher.  Couldn't this be done by passing them along in the execute method?

Original comment by ssaam...@gmail.com on 28 Oct 2010 at 4:42

GoogleCodeExporter commented 9 years ago
"It would be nice to get access to them via the execute method of the 
dispatcher.  Couldn't this be done by passing them along in the execute method?"

The issue was addressed here: 
http://code.google.com/p/gwt-dispatch/issues/detail?id=4

Original comment by butani.a...@gmail.com on 1 Nov 2010 at 10:02

GoogleCodeExporter commented 9 years ago
Hi,

I am not using Guice but Spring with GWT and am fairly new to this.

Can someone please elaborate how to get session objects in ActionHandlers?

I want to make a LoginActionHandler which checks user login credentials and 
then adds user details to session object.

Original comment by inet2...@gmail.com on 25 Mar 2011 at 10:39

GoogleCodeExporter commented 9 years ago
I would be interested in that too...

Original comment by michaelm...@gmail.com on 29 Mar 2011 at 2:59

GoogleCodeExporter commented 9 years ago
Re: Spring instead of Guice

We are currently running a project that uses Spring as the main DI engine on 
the Server and have found that to make use of GWT-Dispatch you have to use 
Guice as they are intimately tied together. One possibility is to create a 
series of bridge bindings that bind Guice to Spring using an approach similar 
to Guice's SpringIntegration class.  Essentially you are telling Guice to 
lookup any dependencies in the ApplicationContext so Spring is really doing the 
injection and because Guice doesnt care how it gets the instance this seems to 
work well. Unfortunately, we have not found a way around Guice entirely, but 
this bridge approach does the trick. 

To answer the question about session injection, we didnt find a good way to get 
the session injected directly because that would require that Guice builds your 
handlers, which if you follow the advice above doesnt happen - Spring is doing 
all the object construction. You can however access anything Spring knows about 
and manage injection like you normally would with Spring. In our case we 
created and inected a UserProvider that was aware of the SecurityContext and 
ApplicationContext so it could access almost anything it wanted including the 
httpsession. Via the API exposed by the UserProvider we were able to provide 
access to any handler.

As an architectural aside, I would recommend against putting your 
authentication/login into the GWT app, especially if you see a need for Spring 
Security down the road. You will be tied to the command pattern or rpc approach 
which isnt necessarily easy to connect to Spring's authentication mechanisms. 
We have had much more success moving the login to a separate JSP page and 
treating the GWT app itself as a 'trusted zone' so you dont have to deal with 
asynchronous services starting up and being dependent on being authenticated. 
It made for much less code and a much cleaner isolation of trusted/untrusted 
areas of the application.

In our app we have the following action mapping module

/**
 * Module which binds the actions and corresponding handlers
 */
public class ActionMappingModule extends HandlerModule
{
    private final ApplicationContext applicationContext;

    public ActionMappingModule(ApplicationContext applicationContext)
    {
        this.applicationContext = checkNotNull(applicationContext);
    }

    @Override
    protected void configureHandlers()
    {
        GuiceToSpringBinder guiceToSpringBinder = new GuiceToSpringBinder(applicationContext, binder());
        guiceToSpringBinder.bindGuiceToSpring();

        bindHandler(LoadUsersAction.class, LoadUsersActionHandler.class);
        bindHandler(GetUserByIdAction.class, GetUserByIdActionHandler.class);

    }
}

And the GuiceToSpringBinder looks like:

public class GuiceToSpringBinder
{
    private final ApplicationContext applicationContext;
    private final Binder binder;

    public GuiceToSpringBinder(final ApplicationContext applicationContext, final Binder binder)
    {
        this.binder = checkNotNull(binder);
        this.applicationContext = checkNotNull(applicationContext);
    }

    public void bindGuiceToSpring()
    {
        binder.bind(BeanFactory.class).toInstance(applicationContext);

        binder.bind(LoadUsersActionHandler.class).toProvider(SpringToGuiceInjector.fromSpring(LoadUsersActionHandler.class,
                                                                                               "loadUsersActionHandler"));
        binder.bind(GetUserByIdActionHandler.class).toProvider(SpringToGuiceInjector.fromSpring(GetUserByIdActionHandler.class,
                                                                                                   "getUserByIdActionHandler"));
    }
}

And SpringToGuiceInjector looks like this (note that this is a slight 
modification on the SpringSupport class included in Guice:

public class SpringToGuiceInjector
{
    private SpringToGuiceInjector()
    {
    }

    /**
     * Creates a provider which looks up objects from Spring using the given name.
     * Expects a binding to {@link org.springframework.beans.factory.BeanFactory}. Example usage:
     * 
     * <pre>
     * bind(DataSource.class).toProvider(fromSpring(DataSource.class, "dataSource"));
     * </pre>
     */
    public static <T> Provider<T> fromSpring(Class<T> type, String name)
    {
        return new InjectableSpringProvider<T>(type, name);
    }

    /**
     * Binds all Spring beans from the given factory by name. For a Spring bean
     * named "foo", this method creates a binding to the bean's type and {@code @Named("foo")}.
     * 
     * @see com.google.inject.name.Named
     * @see com.google.inject.name.Names#named(String)
     */
    public static void bindAll(Binder binder, ListableBeanFactory beanFactory)
    {
        binder = binder.skipSources(SpringIntegration.class);

        for (String name : beanFactory.getBeanDefinitionNames())
        {
            Class< ? > type = beanFactory.getType(name);
            bindBean(binder, beanFactory, name, type);
        }
    }

    static <T> void bindBean(Binder binder, ListableBeanFactory beanFactory, String name, Class<T> type)
    {
        SpringProvider<T> provider = SpringProvider.newInstance(type, name);
        try
        {
            provider.initialize(beanFactory);
        }
        catch (Exception e)
        {
            binder.addError(e);
            return;
        }

        binder.bind(type).annotatedWith(Names.named(name)).toProvider(provider);
    }

    static class SpringProvider<T> implements Provider<T>
    {

        BeanFactory beanFactory;
        boolean singleton;
        final Class<T> type;
        final String name;

        public SpringProvider(Class<T> type, String name)
        {
            this.type = checkNotNull(type, "type");
            this.name = checkNotNull(name, "name");
        }

        static <T> SpringProvider<T> newInstance(Class<T> type, String name)
        {
            return new SpringProvider<T>(type, name);
        }

        void initialize(BeanFactory beanFactory)
        {
            this.beanFactory = beanFactory;
            if (!beanFactory.isTypeMatch(name, type))
            {
                throw new ClassCastException("Spring bean named '" + name + "' does not implement " + type.getName() + ".");
            }
            singleton = beanFactory.isSingleton(name);
        }

        @SuppressWarnings("unchecked")
        @Override
        public T get()
        {
            return (T)(singleton ? getSingleton() : beanFactory.getBean(name));
        }

        volatile T instance;

        @SuppressWarnings("unchecked")
        private T getSingleton()
        {
            return (T)beanFactory.getBean(name);
        }
    }

    static class InjectableSpringProvider<T> extends SpringProvider<T>
    {

        InjectableSpringProvider(Class<T> type, String name)
        {
            super(type, name);
        }

        @Inject
        @Override
        void initialize(BeanFactory beanFactory)
        {
            super.initialize(beanFactory);
        }
    }
}

Original comment by adamben...@gmail.com on 29 Mar 2011 at 4:43