Java 使用ContainerRequestFilter阻止JAX-RS方法的基本身份验证头的Wildfly web.xml安全约束

Java 使用ContainerRequestFilter阻止JAX-RS方法的基本身份验证头的Wildfly web.xml安全约束,java,servlets,web.xml,wildfly,security-constraint,Java,Servlets,Web.xml,Wildfly,Security Constraint,我正在开发的web应用程序包括一些servlet和JAX-RS web服务。到目前为止,我一直在使用ContainerRequestFilter对REST方法调用进行身份验证,但现在我还需要保护servlet,因此我决定使用web.xml来定义安全约束。My web.xml如下所示: 休息 /休息/* 受保护的 /保护/* 管理 管理 用户 基本的 禁区 如果我正确理解了web.xml的语法,那么我定义的内容就意味着对/rest/*(我所有的JAX-RS方法都在其中)的访问就LoginMod

我正在开发的web应用程序包括一些servlet和JAX-RS web服务。到目前为止,我一直在使用ContainerRequestFilter对REST方法调用进行身份验证,但现在我还需要保护servlet,因此我决定使用web.xml来定义安全约束。My web.xml如下所示:


休息
/休息/*
受保护的
/保护/*
管理
管理
用户
基本的
禁区
如果我正确理解了web.xml的语法,那么我定义的内容就意味着对/rest/*(我所有的JAX-RS方法都在其中)的访问就LoginModule而言是不受限制的,并且对/protected/*路径(我保存安全servlet的地方)的所有访问都需要基本授权

当我尝试打开一个安全servlet时,例如/protected/test,我会在浏览器中看到基本的auth-login对话框,并且行为是正确的-如果我为“admin”用户输入凭据,我就可以访问。否则,我会收到一条“禁止”信息

另外,当我试图访问/rest/path上的任何内容时,我没有看到基本的auth对话框,这正是我所期望的。但是,我在ContainerRequestFilter中获得的授权头不是我在REST请求中发送的授权头,而是我以前用于进入/protected/servlet的授权头

下面是谜题的其他部分:

standalone.xml(安全域部分)


jboss-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <security-domain>PaloSecurityDomain</security-domain>
</jboss-web>

孢粉安全域
paologinmodule.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);
  }

}
package com.palo.security;
导入java.security.acl.Group;
导入java.util.Set;
导入javax.inject.inject;
导入javax.naming.NamingException;
导入javax.security.auth.login.LoginException;
导入org.apache.log4j.Logger;
导入org.jboss.security.SimpleGroup;
导入org.jboss.security.SimplePrincipal;
导入org.jboss.security.auth.spi.UsernamePasswordLoginModule;
导入com.palo.PaloRealmRole;
导入com.palo.model.PaloRealmUser;
导入com.palo.utils.CdiHelper;
导入com.palo.utils.PasswordHandler;
公共类PaloRealmLoginModule扩展UsernamePasswordLoginModule{
专用静态记录器=记录器
.getLogger(PaloRealmLoginModule.class);
@注入
私有PaloRealmLogic realmLogic;
@凌驾
受保护的字符串getUsersPassword()引发LoginException{
if(null==realmLogic){
试一试{
CdiHelper.programmaticInjection(PaloRealmLoginModule.class、,
这),;
}捕获(NamingE例外){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}
debug(“获取用户密码”+super.getUsername());
PaloRealmUser user=realmLogic.getUserByName(super.getUsername());
if(null==用户){
logger.错误(“未找到用户”);
抛出新的LoginException(“用户”+super.getUsername()
+“未找到”);
}
debug(“Found”+user.getPassword());
返回user.getPassword();
}
@凌驾
受保护组[]getRoleSets()引发LoginException{
debug(“为用户获取角色”+super.getUsername());
if(null==realmLogic){
试一试{
CdiHelper.programmaticInjection(PaloRealmLoginModule.class、,
这),;
}捕获(NamingE例外){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}
PaloRealmUser user=realmLogic.getUserByName(super.getUsername());
if(null==用户){
抛出新的LoginException(“用户”+super.getUsername()
+“未找到”);
}
Set roles=user.getRoles();
组[]组={new SimpleGroup(“角色”)};
for(PaloRealmRole角色:角色){
debug(“找到的角色”+role.getRole());
SimplePrincipal prole=新的SimplePrincipal(role.getRole());
组[0]。添加成员(prole);
}
返回组;
}
@凌驾
受保护的布尔validatePassword(字符串输入密码,
字符串(预期密码){
logger.debug(“验证密码”+输入密码+”|”
+预期密码);
返回PasswordHandler.getInstance().verifyPassword(inputPassword,
预期密码);
}
}
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;
  }
}
package com.palo.web.rest;
导入java.io.IOException;
导入java.lang.reflect.Method;
导入java.util.List;
导入java.util.StringTokenizer;
导入javax.annotation.security.DenyAll;
导入javax.annotation.security.PermitAll;
导入javax.inject.inject;
导入javax.json.JsonObjectBuilder;
导入javax.ws.rs.container.ContainerRequestContext;
导入javax.ws.rs.container.ContainerRequestFilter;
导入javax.ws.rs.container.ContainerResponseContext;
导入javax.ws.rs.container.ContainerResponseFilter;
导入javax.ws.rs.core.MultivaluedMap;
导入javax.ws.rs.core.Response;
导入javax.ws.rs.ext.Provider;
导入org.apache.log4j.Logger;
导入org.jboss.resteasy.annotations.interception.ServerInterceptor;
导入org.jboss.resteasy.core.Headers;
导入org.jboss.resteasy.core.ResourceMethodInvoker;
导入org.jboss.resteasy.core.ServerResponse;
导入com.palo.analytics.GoogleAnalyticsEvent;
导入com.palo.logic.UserLogic;
导入com.palo.web.utils.HttpUtils;
@提供者
@服务器拦截器
公共类SecurityInterceptor实现ContainerRequestFilter{
私有静态记录器=Logger.getLogger(SecurityInterceptor.class);
私有静态最终字符串授权\u PROPERTY=“AUTHORIZATION”;
私有静态最终服务器响应访问被拒绝=新服务器响应(
“此资源的访问被拒绝”,401,新标题();
私有静态最终服务器响应访问\u被拒绝\u用户=新服务器响应(
“用户未授权”,401,新标题();
专用静态最终服务器响应访问_
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;
  }
}
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>rest</web-resource-name>
            <url-pattern>/rest/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>anonymous</role-name>
            <role-name>admin</role-name>
        </auth-constraint>
    </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-constraint>
        <web-resource-collection>
            <web-resource-name>rest</web-resource-name>
            <url-pattern>/rest/*</url-pattern>
        </web-resource-collection>
    </security-constraint>