The web application I'm developing consists of some servlets as well as JAX-RS web services. So far I have used the ContainerRequestFilter to authenticate calls to the REST method, but now I also need to protect the servlets, so I decided to use web.xml to determine the security restrictions. My web.xml looks like this:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <security-constraint> <web-resource-collection> <web-resource-name>rest</web-resource-name> <url-pattern>/rest/*</url-pattern> </web-resource-collection> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>protected</web-resource-name> <url-pattern>/protected/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>user</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> <realm-name>Restricted Zone</realm-name> </login-config> </web-app>
If I understand the syntax of web.xml correctly, then what I defined means that access to / rest / * (where all my JAX-RS methods are) is unlimited with LoginModules, and all access to the path / protected / * (where I keep my protected servlets) requires basic authorization.
When I try to open one of the safe servlets, for example. / protected / test, I get the basic login dialog in the browser, and the behavior is correct - if I enter the credentials for the user โadminโ, Iโm allowed access. Otherwise, I get a Forbidden message.
Also, when I try to access something on the / rest / path, I don't get any basic auth dialog, which I expect. However, the authorization header that I get in the ContainerRequestFilter is not the one I send in the REST request, but the one I used earlier to get into / protected / servlet.
The following are other parts of the puzzle:
standalone.xml (secure domains section)
<security-domain name="PaloSecurityDomain" cache-type="default"> <authentication> <login-module code="com.palo.security.PaloLoginModule" flag="required"/> </authentication> </security-domain>
Jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?> <jboss-web> <security-domain>PaloSecurityDomain</security-domain> </jboss-web>
PaloLoginModule.java
package com.palo.security; import java.security.acl.Group; import java.util.Set; import javax.inject.Inject; import javax.naming.NamingException; import javax.security.auth.login.LoginException; import org.apache.log4j.Logger; import org.jboss.security.SimpleGroup; import org.jboss.security.SimplePrincipal; import org.jboss.security.auth.spi.UsernamePasswordLoginModule; import com.palo.PaloRealmRole; import com.palo.model.PaloRealmUser; import com.palo.utils.CdiHelper; import com.palo.utils.PasswordHandler; public class PaloRealmLoginModule extends UsernamePasswordLoginModule { private static Logger logger = Logger .getLogger(PaloRealmLoginModule.class); @Inject private PaloRealmLogic realmLogic; @Override protected String getUsersPassword() throws LoginException { if (null == realmLogic) { try { CdiHelper.programmaticInjection(PaloRealmLoginModule.class, this); } catch (NamingException e) {
SecurityInterceptor.java
package com.palo.web.rest; import java.io.IOException; import java.lang.reflect.Method; import java.util.List; import java.util.StringTokenizer; import javax.annotation.security.DenyAll; import javax.annotation.security.PermitAll; import javax.inject.Inject; import javax.json.JsonObjectBuilder; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerResponseContext; import javax.ws.rs.container.ContainerResponseFilter; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.ext.Provider; import org.apache.log4j.Logger; import org.jboss.resteasy.annotations.interception.ServerInterceptor; import org.jboss.resteasy.core.Headers; import org.jboss.resteasy.core.ResourceMethodInvoker; import org.jboss.resteasy.core.ServerResponse; import com.palo.analytics.GoogleAnalyticsEvent; import com.palo.logic.UserLogic; import com.palo.web.utils.HttpUtils; @Provider @ServerInterceptor public class SecurityInterceptor implements ContainerRequestFilter { private static Logger logger = Logger.getLogger(SecurityInterceptor.class); private static final String AUTHORIZATION_PROPERTY = "Authorization"; private static final ServerResponse ACCESS_DENIED = new ServerResponse( "Access denied for this resource", 401, new Headers<Object>()); private static final ServerResponse ACCESS_DENIED_FOR_USER = new ServerResponse( "User not authorized", 401, new Headers<Object>()); private static final ServerResponse ACCESS_FORBIDDEN = new ServerResponse( "Nobody can access this resource", 403, new Headers<Object>()); @Inject private UserLogic ul; @Override public void filter(ContainerRequestContext requestContext) throws IOException { logger.debug("------------- request filter ------------"); ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker) requestContext .getProperty("org.jboss.resteasy.core.ResourceMethodInvoker"); Method method = methodInvoker.getMethod(); String methodName = method.getName(); String uri = requestContext.getUriInfo().getPath(); logger.debug("Accessing method " + methodName + " via URI " + uri); for (String str : requestContext.getPropertyNames()) { logger.debug(str); }
I use RESTClient for Firefox to send REST requests to JAX-RS methods. Since I am registering all the headers, I can clearly see what is happening with the filter and the value does not change between calls, even if I change it in RESTClient. Moreover, the value still exists, even if I do not use the authorization header in RESTClient.
My question is: why is the authorization header blocked and it is not redirected to my filter? If I delete the web.xml file, I get the correct authorization header in the ContainerRequestFilter. Is there a way to move / rest part of the application to a zone that is not affected by login-config in web.xml?
Any help is much appreciated!