Wildfly web.xml security-constraint blockieren basic-auth-header für die JAX-RS-Methoden mit ContainerRequestFilter

Die web-Anwendung entwickle ich aus einigen servlets und auch JAX-RS webservices. Bis jetzt war ich mit einem ContainerRequestFilter zu authentifizieren, die REST-Methode ruft aber jetzt habe ich auch sichern müssen die servlets so entschied ich mich für web.xml um die Sicherheit zu definieren Einschränkungen. Meine web.xml sieht wie folgt aus:

<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>
    <!-- Configure login to be HTTP Basic -->
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Restricted Zone</realm-name>
    </login-config>
</web-app>

Wenn ich die syntax verstehen web.xml korrekt, was für mich bedeutet, dass der Zugriff auf /rest/* (wo alle meine JAX-RS-Methoden sind) ist unbeschränkt, soweit die LoginModules betroffen sind, und alle Zugang zu der /protected/* Pfad (wo ich immer meine sichere servlets) erfordert Grundlegende Berechtigung.

Wenn ich versuche zu öffnen eines der sichere servlets, z.B. /protected/Tests, erhalte ich die basic-auth login-dialog im browser und das Verhalten ist richtig, wenn ich geben Sie die Anmeldeinformationen für einen "admin" - Benutzer, ich bin der Zugriff erlaubt. Ansonsten bekomme ich eine 'Forbidden' - Meldung.

Auch, wenn ich versuche, Zugriff auf alles auf der /rest/Pfad bekomme ich keine basic auth-dialog, das ist, was ich erwarten würde. Aber der Authorization-header bekomme ich in der ContainerRequestFilter ist nicht die, die ich bin, Versand in die REST-Anforderung, aber es ist die, die ich zuvor verwendet, um in zu erhalten /geschützt/servlet.

Unten sind andere Teile des puzzle:

standalone.xml (security-Bereich Domänen)

<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) {
        //TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    logger.debug("Getting password for user " + super.getUsername());
    PaloRealmUser user = realmLogic.getUserByName(super.getUsername());
    if (null == user) {
      logger.error("User not found");
      throw new LoginException("User " + super.getUsername()
          + " not found");
    }
    logger.debug("Found " + user.getPassword());
    return user.getPassword();
  }

  @Override
  protected Group[] getRoleSets() throws LoginException {
    logger.debug("Getting roles for user " + super.getUsername());
    if (null == realmLogic) {
      try {
        CdiHelper.programmaticInjection(PaloRealmLoginModule.class,
            this);
      } catch (NamingException e) {
        //TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    PaloRealmUser user = realmLogic.getUserByName(super.getUsername());
    if (null == user) {
      throw new LoginException("User " + super.getUsername()
          + " not found");
    }
    Set<PaloRealmRole> roles = user.getRoles();
    Group[] groups = { new SimpleGroup("Roles") };
    for (PaloRealmRole role : roles) {
      logger.debug("Found role " + role.getRole());
      SimplePrincipal prole = new SimplePrincipal(role.getRole());
      groups[0].addMember(prole);
    }

    return groups;
  }

  @Override
  protected boolean validatePassword(String inputPassword,
      String expectedPassword) {
    logger.debug("Validating password " + inputPassword + "|"
        + expectedPassword);
    return PasswordHandler.getInstance().verifyPassword(inputPassword,
        expectedPassword);
  }

}

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
  /**
   * The request filter is called automatically called for each incoming request. It checks which method is being called by the client and, based on that method's annotations, restricts access, verifies the identity of the caller, checks the validity of the session token, etc.
   */
  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);
    }

    //Get request headers
    final MultivaluedMap<String, String> headers = requestContext
        .getHeaders();
    for (String key : headers.keySet()) {
      for (String value : headers.get(key)) {
        logger.debug(key + " - " + value);
      }
    }

    //Access allowed for all
    if (method.isAnnotationPresent(PermitAll.class)) {
      return;
    }
    //Access denied for all
    if (method.isAnnotationPresent(DenyAll.class)) {
      requestContext.abortWith(ACCESS_FORBIDDEN);
      return;
    }

    //Fetch authorization header
    final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);

    //If no authorization information present; block access
    if (null == authorization || authorization.isEmpty()) {
      requestContext.abortWith(ACCESS_DENIED);
      return;
    }

    final String username = HttpUtils.getUsernameFromAuthorizationHeader(
        authorization, HttpUtils.AUTHENTICATION_SCHEME_BASIC);
    final String password = HttpUtils.getPasswordFromAuthenticationHeader(
        authorization, HttpUtils.AUTHENTICATION_SCHEME_BASIC);

    if (null == username || null == password || username.isEmpty()
        || password.isEmpty()) {
      requestContext.abortWith(ACCESS_DENIED_FOR_USER);
      return;
    }

    boolean authenticated = ul.authenticate(username, password);
    if (false == authenticated) {
      requestContext.abortWith(ACCESS_DENIED);
      return;
    } 
    return;
  }
}

Ich bin mit RESTClient für Firefox senden Sie den REST Anforderungen an die JAX-RS-Methoden. Da bin ich Protokollierung der Header, ich kann deutlich sehen, was kommt in den Filter und der Wert ändert sich nicht zwischen den anrufen, selbst wenn ich es ändern in RESTClient. Was ist mehr, der Wert ist immer noch da, auch wenn ich nicht verwenden, der Authorization-header in RESTClient.

Meine Frage ist, warum ist der Authorization-header blockiert und es nicht an meinen Filter? Wenn ich entfernen Sie die web.xml Datei, bekomme ich die richtige Authorization-header in der ContainerRequestFilter. Gibt es eine Möglichkeit, den /rest-Teil der Anwendung in eine zone, die nicht betroffen ist der login-config in web.xml?

Jede Hilfe wird sehr geschätzt!

InformationsquelleAutor PawelPredki | 2014-08-17
Schreibe einen Kommentar