Push with Shiro

Hi,

I’m using shiro authentication and authorization in my Vaadin project.
I’d like to enable push with WebSocket.
So far I’m able to attach the Subject to the AtmosphereRequest using the ShiroInterceptor provided by Atmosphere.

The problem is to retrieve this Subject in my business logic.
On various post they suggest not to use SecurityUtils.getSubject() to retrieve current user but rather use the subject from the Atmosphere request (http://mikepilone.blogspot.de/2013/07/vaadin-shiro-and-push.html, https://vaadin.com/forum#!/thread/8644145, …)).
Fine, but my business logic does not have access to the Atmosphere request or VaadinSession.

In HTTP standard mode I have the ShiroFilter that does the job :

// First retrieve subject from HTTP session Subject subject = .... // Then call logic in an authenticated callback subject.execute(new Callable() { public Object call() throws Exception { // Call next filters and business logic return null; } }); With this my business logic thread is executed with authenticated user.
I would like to do something equivalent using Vaadin/Atmosphere, but I can’t find the right place to hook that kind of code.
I tried extending PushHandler but it’s rather closed, most methods/callbacks are not visible.

Any idea on how to achieve this so that my non-UI business logic can be executed in authenticated threads ?

Cheers,

Fred

Hi,

I figured out a way to do this. As I use Spring as well and defined my Shiro Web securityManager in it, we a second securityManager which is not a Web securityManager to execute the callback outside a HTTP request/response call.

Once this is done, I need to define a a org.apache.shiro.web.env.EnvironmentLoaderListener.EnvironmentLoaderListener in my container define the ShiroInterceptor and finally add a the Shiro WebEnvironment.

I need to override my VaadinServlet.createServletService to define a customer ShiroPushRequestHandler instead of standard Vaadin PushRequestHandler. Unless Vaadin has a better way to hook your own pushRequestHandler, this is how I did it :

@Override

protected VaadinServletService createServletService(DeploymentConfiguration deploymentConfiguration)

        throws ServiceException {

    VaadinServletService service;

    if(ApplicationUtil.isPushEnabled()) {

        service = new VaadinServletService(this, deploymentConfiguration) {

            @Override

            protected List<RequestHandler> createRequestHandlers() throws ServiceException {

                ArrayList<RequestHandler> handlers = new ArrayList<RequestHandler>();

                handlers.add(new SessionRequestHandler());

                handlers.add(new PublishedFileHandler());

                handlers.add(new HeartbeatHandler());

                handlers.add(new FileUploadHandler());

                handlers.add(new UidlRequestHandler());

                handlers.add(new UnsupportedBrowserHandler());

                handlers.add(new ConnectorResourceHandler());

                handlers.add(0, new ServletBootstrapHandler());

                handlers.add(new ServletUIInitHandler());

                if (isAtmosphereAvailable()) {

                    try {

                        handlers.add(new ShiroPushRequestHandler(this));

                    } catch (ServiceException e) {

                        // Atmosphere init failed. Push won't work but we don't throw a

                        // service exception as we don't want to prevent non-push

                        // applications from working

                        logger.error("Could not initialize push");

                    }

                }

                return handlers;

            }

        };

        service.init();

        

        initializePlugin(service);

    } else {

        service = super.createServletService(deploymentConfiguration);

    }

    

    return service;

}

The custom ShiroPushRequestHandler class :

[code]
public class ShiroPushRequestHandler extends PushRequestHandler {

public ShiroPushRequestHandler(VaadinServletService service) throws ServiceException {
    super(service);
}


protected PushHandler createPushHandler(VaadinServletService service) {
    return new ShiroPushHandler(service);
}

}
[/code]The ShiroPushHandler class :

public class ShiroPushHandler extends PushHandler {

    private static Logger logger = LoggerFactory.getLogger(ShiroPushHandler.class);    

    public ShiroPushHandler(VaadinServletService service) {
        super(service);
        // Retrieve here anything from the servlet context (Spring factory....)
    }

    @Override
    void onConnect(AtmosphereResource resource) {
        // This subject has been previously set on the Atmosphere request by the ShiroInterceptor
        Subject subject = (Subject) resource.getRequest().getAttribute(FrameworkConfig.SECURITY_SUBJECT);

        logger.info("Executig shiroPushHandler.onConnect with subject {}", subject.getPrincipal());

        subject.execute(new Callable() {

            public Object call() throws Exception {
                if (subject.isAuthenticated()) { 
                    // You can call your services to initialize Locale, MDC or anything you did in the HTTP filters
                } else {
                   // Handle error
                }

                try {
                    ShiroPushHandler.super.onConnect(resource);
                } finally {

                     // Clear MDC, and any ThreadLocal related stuff you initialized...
                }
                return null;
            }
        });
    }




    @Override
    void onMessage(AtmosphereResource resource) {
        // This subject has been previously set on the Atmosphere request by the ShiroInterceptor
        Subject subject = (Subject) resource.getRequest().getAttribute(FrameworkConfig.SECURITY_SUBJECT);

        logger.info("Executig shiroPushHandler.onConnect with subject {}", subject.getPrincipal());

        subject.execute(new Callable() {

            public Object call() throws Exception {
                if (subject.isAuthenticated()) { 
                    // You can call your services to initialize Locale, MDC or anything you did in the HTTP filters
                } else {
                   // Handle error
                }

                try {
                    ShiroPushHandler.super.onMessage(resource);
                } finally {

                     // Clear MDC, and any ThreadLocal related stuff you initialized...
                }
                return null;
            }
        });
    }
}