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.
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 ?
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;
}
});
}
}