First time user, please be kind!
I have a bit of a problem configuring Shiro to filter Vaadin-generated pages using Guice.
I have looked online on various websites including the Apache Shiro's guides and etc. Problem is that most websites tend to do it the 'old' fashion way, i.e. using Shiro 1.1 (which doesn't have native Guice support).
So here is the problem. My pages don't get filtered through Shiro. I have tried a zillion different things including using AOP for method authentication, setting filters up manually in the web.xml. Even setting up a shiro.ini file (which I do NOT want to do under any circumstances).
So here is the list of things I am using:
- Shiro 1.2.0-SNAPSHOT
- Guice 3.0
- Vaadin 6.7.4
Here is my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>App</display-name>
<context-param>
<description>Vaadin production mode</description>
<param-name>productionMode</param-name>
<param-value>false</param-value>
</context-param>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.app.GuiceServletInjector</listener-class>
</listener>
</web-app>
Here is the Servlet Injector:
public class GuiceServletInjector extends GuiceServletContextListener {
private ServletContext servletContext;
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
servletContext = servletContextEvent.getServletContext();
super.contextInitialized(servletContextEvent);
}
#Override
protected Injector getInjector() {
return Guice.createInjector(new GuiceServletModule(), new ShiroConfigurationModule(servletContext));
}
Which then creates a ServletModule, which passes the request to the Vaadin app:
protected void configureServlets() {
bind(Application.class).to(VaadinMainWindow.class).in(ServletScopes.SESSION);
bind(BasicHttpAuthenticationFilter.class).in(Singleton.class);
filter("/*").through(BasicHttpAuthenticationFilter.class);
serve("/*", "*").with(VaadinApp.class);
}
Also during the injector stage, please notice that I create a ShiroConfigurationModule, which takes care of the realms and etc:
public class ShiroConfigurationModule extends ShiroWebModule {
#Inject
public ShiroConfigurationModule(ServletContext servletContext) {
super(servletContext);
}
#Override
protected void configureShiroWeb() {
bindRealm().to(ShiroBaseRealm.class).in(Singleton.class);
bind(Realm.class).to(ShiroBaseRealm.class).in(Singleton.class);
processMethodInterceptors();
}
private void processMethodInterceptors() {
MethodInterceptor interceptor = new AopAllianceAnnotationsAuthorizingMethodInterceptor();
bindInterceptor(any(), annotatedWith(RequiresRoles.class), interceptor);
bindInterceptor(any(), annotatedWith(RequiresPermissions.class), interceptor);
bindInterceptor(any(), annotatedWith(RequiresAuthentication.class), interceptor);
bindInterceptor(any(), annotatedWith(RequiresUser.class), interceptor);
bindInterceptor(any(), annotatedWith(RequiresGuest.class), interceptor);
}
}
The realm class returns 'true' for the supports(), but returns 'null' for everything, simulating that the user doesn't exists.
The chances of doing something wrong, or missing a step is very high. Can someone please care to explain what I'm missing so I can at least get a basic HTTP auth up?
Thanks a lot!
Mo.
Naturally the blog link has expired, and the site that is now redirected to contains no trace of that blog article whatsoever.
A copy of the article can be found here.
http://web.archive.org/web/20120413052117/http://www.mofirouz.com/wordpress/2012/01/guice-shiro-1-2-and-vaadingwt/
The crux of the answer: if you are using guice then you MUST include
filter("/*").through(GuiceShiroFilter.class)
in your ServletModule, or else NONE of the related shiro filters will ever get hit.
Right so after a lot of testing and messing about with Shiro (as well as finally using the 1.2-release), I got mine to work.
I've written a detailed answer on my site (primarily because it is easier to write!). Have a look:
http://www.mofirouz.com/wordpress/2012/01/guice-shiro-1-2-and-vaadingwt/
Good luck whoever out there!
Related
I'm writing a simple game with JavaEE Websocket technology. Using JSR356, my server-side socket class looks like following:
#ServerEndpoint(
value = "/sock",
decoders = { SocketDecoder.class }
)
public class CardsSocket {
....
#OnMessage
public void onMessage(final SocketInput message, final Session session) {
...
}
...
}
It works perfectly fine, and has no issues. But then I decided to create also some web page for info and stuff. So, without changing anything on previous class, I have created a new one:
#ServerEndpoint(value = "/cards")
public class CardsWebPage extends HttpServlet {
#Override
public void doGet(...) {
...
}
}
And configured web.xml file in WEB-INF directory.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
<servlet>
<servlet-name>CardsWebPage</servlet-name>
<servlet-class>server.CardsWebPage</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CardsWebPage</servlet-name>
<url-pattern>/cards</url-pattern>
</servlet-mapping>
</web-app>
And there began troubles. My servelet works - browser shows page on localhost:8080/cards, but client-side socket class can no longer initiate - it falls with Exception:
"javax.websocket.DeploymentException: The HTTP response from the server [HTTP/1.1 404 Not Found] did not permit the HTTP upgrade to WebSocket"
, and nothing seems to fix it. Have I missed some documentation? Is it impossible for a single project to contain both servlets and websocket classes? Because, if I delete web.xml file, then sockets are starting to work like before. Server startup logs containing no warnings or errors in both cases.
Yeah, perhaps sparks is right, and I should simply deploy multiple projects.
Hi why decorating CardsWebPage with #ServerEndpoint? Nothing or if you can to get rid of web.xml #WebServlet should be fine.
Intro
Jersey: 2.9
This part of Jersey documentation describes how to provide authorization for REST services. There are two ways to do that:
standard Servlet way, using configuration in web.xml
much better solution using JSR 250 annotations
The First approach works fine, but I cannot make the second work.
Case 1 (using web.xml):
This example works. It is for informational purpose. If you want just jump do the second one, which does not work.
Resource is very simple:
#Path("/HelloWorld")
public class HelloWorldResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayHelloWorld(){
return "Hello World!!!";
}
}
Security configuration is placed in web.xml file, which looks like that:
<web-app ...>
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<!-- SECURITY -->
<security-constraint>
<web-resource-collection>
<url-pattern>/rest/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>boss</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>DefaultRealm</realm-name>
</login-config>
</web-app>
This example works fine. When I try to access http://{myhost}:8080/{war_name}/rest/HelloWorld I have to provide username and password. This means that Realm and configuration in database is just fine. So there is no need to show it here.
Case 2 (JSR 250 annotations):
This example does not work. The resource is almost the same as in the first example, just some annotations are added:
#Path("/HelloWorld")
#PermitAll
public class HelloWorldResource {
#RolesAllowed("boss")
#GET
#Produces(MediaType.TEXT_PLAIN)
public String sayHelloWorld(){
return "Hello World!!!";
}
}
As you see, two annotations are added. It is the same security policy like in the first example, but defined using annotations insted of web.xml.
Now web.xml looks like that:
<?xml version="1.0" encoding="UTF-8"?>
<web-app ...>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>DefaultRealm</realm-name>
</login-config>
</web-app>
In addition new class is added (like described in documentation):
#ApplicationPath("rest")
public class MyApplication extends ResourceConfig {
public MyApplication() {
super(HelloWorldResource.class);
register(RolesAllowedDynamicFeature.class);
}
}
This class is important. As you see required RolesAllowedDynamicFeature is registered.
So two main steps:
- adding annotations to resource
- registering RolesAllowedDynamicFeature
are done.
Problem:
Second example does not work. Popup to provide username and password never shows up. Every time response is 403 Forbidden. This is not a problem with Realm and database configuration since this configuration works fine with the first example.
So the question is: what is wrong with my second implementation?
Tnaks in advance.
Instead of the ResourceConfig sub-class, try add this to you web.xml block
<init-param>
<param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.RolesAllowedResourcFilterFactory</param-value>
</init-param>
So we're almost at our goal to test-drive our spring web-mvc application. We're using spring security to secure URLs or methods and we want to use Spring-Web-Mvc to test the controllers. The problem is: Spring security relies on a FilterChain defined in the web.xml:
<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>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
While spring-web-mvc even with a custom context loader can only load the usual application context stuff:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = WebContextLoader.class, locations = {
"file:src/main/webapp/WEB-INF/servlet-context.xml", "file:src/main/webapp/WEB-INF/dw-manager-context.xml" })
public class MvcTest {
#Inject
protected MockMvc mockMvc;
#Test
public void loginRequiredTest() throws Exception {
mockMvc.perform(get("/home")).andExpect(status().isOk()).andExpect(content().type("text/html;charset=ISO-8859-1"))
.andExpect(content().string(containsString("Please Login")));
}
}
As one can see, we set up our web app so that when calling /home an annonymus user will be prompted to log in. This is working fine with the web.xml in place but we have no idea how to integrate this into our tests.
Is there any way to add a filter to spring-test-mvc?
Right now I'm thinking I might have to start at the GenericWebContextLoader, dig into the MockRequestDipatcher which implements RequestDispatcher, add a filter there somehow and then tell the GenericWebContextLoader to use my implementation... But I'm quite unfamiliar with the whole web stack and would prefer an easier solution that helps me avoid digging into that stuff obviously...
Any ideas / solutions out there?
How would I add a filter by hand anyway? web.xml can't be the only place to do it...
Thanks!
Maybe you could mock the filter chain. There is a class in Spring for doing that.
The code would look something like:
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath("/foo/secure/super/somefile.html");
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain(true);
filterChainProxy.doFilter(request, response, chain);
Then you can add the org.springframework.web.filter.DelegatingFilterProxy instance to the
chain in your code.
Something like:
See also this forum thread.
I'd like to intercept the OPTIONS request with my controller using Spring MVC, but it is catched by the DispatcherServlet. How can I manage that?
I added some more detail to the Bozho answer for beginners.
Sometimes it is useful to let the Spring Controller manage the OPTIONS request (for example to set the correct "Access-Control-Allow-*" header to serve an AJAX call).
However, if you try the common practice
#Controller
public class MyController {
#RequestMapping(method = RequestMethod.OPTIONS, value="/**")
public void manageOptions(HttpServletResponse response)
{
//do things
}
}
It won't work since the DispatcherServlet will intercept the client's OPTIONS request.
The workaround is very simple:
You have to... configure the DispatcherServlet from your web.xml file as follows:
...
<servlet>
<servlet-name>yourServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
...
Adding the "dispatchOptionsRequest" parameter and setting it to true.
Now the DispatcherServlet will delegate the OPTIONS handling to your controller and the manageOption() method will execute.
Hope this helps.
PS. to be honest, I see that the DispatcherServlet append the list of allowed method to the response. In my case this wasn't important and I let the thing go. Maybe further examinations are needed.
#RequestMapping(value="/youroptions", method=RequestMethod.OPTIONS)
public View getOptions() {
}
You should configure the dispatcherServlet by setting its dispatchOptionsRequest to true
As a quick supplement to the above 2 answers, here's how to enable dispatchOptionsRequest in a servlet 3 (no web.xml) environment as it took me a while to work out how to apply the answers above in a non-xml setup.
In a spring 3.2 / servlet 3 environment, you will have some variety of DispatcherServlet initializer class which is the java equivalent of web.xml; in my case it's the AbstractAnnotationConfigDispatcherServletInitializer. Adding the following code will enable dispatchOptionsRequest:
#Override
protected void customizeRegistration(Dynamic registration) {
registration.setInitParameter("dispatchOptionsRequest", "true");
}
I took the following approach:
Using Maven (or manually) pull in this dependancy:
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>1.3.2</version>
</dependency>
This has an implementation to capture all the inbound OPTIONS requests. Into the web.xml file add the following config:
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>Content-Type,Accept,Origin</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The problem I've seen with the /** approach is a more specific Controller implementation will override this.
For Spring without web.xml file, and based on Paul Adamson answer, I just set the parameter dispatchOptionsRequest to true into the dispatcher, to process the Options method calls.
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(applicationContext));
dispatcher.setInitParameter("dispatchOptionsRequest", "true");
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
Could you help to check why doFilter not getting called
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<filter>
<filter-name>roseFilter</filter-name>
<filter-class>net.paoding.rose.RoseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>roseFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
</web-app>
class signature:
import org.springframework.web.filter.GenericFilterBean;
public class RoseFilter extends GenericFilterBean {
404 is returned while call http://localhost:8080/hello/world, I set the breakpoints at
doFilter, it seems doFilter not called?(I tried tomcat 6.0.18, 6.0.29, jdk1.6)
The filter won't be invoked when:
The filter class is missing in the classpath and/or is not loadable or instantiable. You should however have noticed it in the server's startup logs. Solution is to be found based on the interpretation of the exceptions/errors found in the server logs.
There's another filter running before in the chain which isn't calling FilterChain#doFilter(), but rather RequestDispatcher#forward() or include() which caused the subsequent filters in the chain being completely skipped (when they do not listen on FORWARD or INCLUDE dispatchers; they by default only listens on REQUEST dispatcher). Solution is either to fix the wrong filter, or to add <dispatcher>FORWARD</dispatcher> etc accordingly, or to rearrange the filter declarations in web.xml so that your new filter comes before the another filter (you in turn only need to ensure that your new filter is using the FilterChain#doFilter() properly :) ).
The request URL is plain wrong. You used http://localhost:8080/hello/world. With a filter listening on /*, this means that the webapp context should be ROOT or at least /hello. Verify your webapp context. I'd just retry with an URL which points to a valid JSP/Servlet inside the same webapp which generates a non-404 response. Does the filter then get called as well?
What's the web request look like? Can you try changing your url-pattern to *.jsp instead of / * ? If you are using something other than pure JSP then change it to whatever the request ending extension is (like for struts it is usually *.do).