如何在同一Java EE应用程序中配置BASIC和FORM身份验证方法

时间:2014-12-21 10:51:24

标签: java java-ee web.xml basic-authentication form-authentication

我需要根据Java EE应用程序中的Web资源配置BASIC和FORM身份验证方法。这意味着例如对于路径/ app / services我想使用BASIC进行身份验证,而其余的应用程序方法将是FORM。

没有Spring,甚至纯Java EE中是否可能?

4 个答案:

答案 0 :(得分:2)

这是可能的,但您需要创建并安装自己的身份验证模块,而不是使用两个内置的BASIC和FORM。

Java EE有一个名为JASPIC的API / SPI。最重要的是,许多应用程序服务器都有替代的本机API。

答案 1 :(得分:1)

是的,有一种解决方法(我是为Tomcat 7.0.68做的)。

1)配置web.xml以使用FORM auth-method:

<login-config>
 <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>/login.jsp</form-login-page>
    <form-error-page>/loginError.jsp</form-error-page>
  </form-login-config>
</login-config>

2)设置您要验证BASIC方式的url-pattern,而不是auth-constraint

<security-constraint>
  <web-resource-collection>
    <web-resource-name>BASIC auth path</web-resource-name>
      <url-pattern>/app/services/*</url-pattern>
    </web-resource-collection>
  </security-constraint>

3)为该模式配置过滤器:

<filter>
  <filter-name>BasicLoginFilter</filter-name>
  <filter-class>pa.cka.ge.BasicLoginFilter</filter-class>
  <init-param>
    <param-name>role-names-comma-sep</param-name>
    <param-value>role1,andRole2,andRole3</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>BasicLoginFilter</filter-name>
  <url-pattern>/app/services/*</url-pattern>
</filter-mapping>

enter code here

其中role-names-comma-sep是您定义要访问/app/services的角色的自定义参数。这很有用,因为路径/app/services必须没有auth-constraints(参见上文),并且您基本上无法像往常一样定义角色。在我的示例中,实现通过AND检查这些角色(您可以更改它)。

4)在过滤器中手动登录:

package pa.cka.ge;

import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.Role;
import org.apache.catalina.users.MemoryUser;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.codec.binary.Base64;

public class BasicLoginFilter implements Filter {

  /**
   * List of roles the user must have to authenticate
   */
  private final List&lt;String&gt; roleNames = new ArrayList&lt;String&gt;();

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    String roleNamesParam = filterConfig.getInitParameter("role-names-comma-sep");
    if (roleNamesParam != null) {
      for (String roleName: roleNamesParam.split(",")) {
        roleNames.add(roleName);
      }
    }
  }

  private static final String AUTHORIZATION_HEADER = "Authorization";
  private static final String BASIC_PREFIX = "Basic ";

  @Override
  public void doFilter(ServletRequest req, ServletResponse resp,
      FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)resp;

    // get username and password from the Authorization header
    String authHeader = request.getHeader(AUTHORIZATION_HEADER);
    if (authHeader == null || !authHeader.startsWith(BASIC_PREFIX)) {
      throwBasicAuthRequired();
    }

    String userPassBase64 = authHeader.substring(BASIC_PREFIX.length());
    String userPassDecoded = new String(Base64.decodeBase64(userPassBase64), B2CConverter.ISO_8859_1);// decode from base64 any other way, if this won't work for you. Finally userPassDecoded must contain readable "username:password"
    if (!userPassDecoded.contains(":")) {
      throwBasicAuthRequired();
    }

    String authUser = userPassDecoded.substring(0, userPassDecoded.indexOf(':'));
    String authPass = userPassDecoded.substring(userPassDecoded.indexOf(':') + 1);

    // do login manually
    request.login(authUser, authPass);

    // check roles for the user
    final Principal userPrincipal = request.getUserPrincipal();

    // Your Principal will be another class, not MemoryUser. Run in debug mode to see what class you actually have. The role checking will depend on that class.
    MemoryUser user = (MemoryUser)userPrincipal; 

    boolean hasRoles = true;
    for (String role: roleNames) {
      if (role == null) {
        continue;
      }
      boolean hasRole = false;
      Iterator&lt;Role&gt; roles = user.getRoles();
      while (roles.hasNext()) {
        if (role.equals(roles.next().getName())) {
          hasRole = true;
          break;
        }
      }
      if (!hasRole) {
        hasRoles = false;
        break;
      }
    }

    if (hasRoles) {
      // login successful
      chain.doFilter(request, response);
      request.logout();// optional
    } else {
      // login failed
      throwLoginFailed();
    }
  }

  @Override
  public void destroy() {
  }

  public static void throwBasicAuthRequired() throws ServletException {
    throw new ServletException("The /app/services resources require BASIC authentication");
  }

  public static void throwLoginFailed() throws ServletException {
    throw new ServletException("Login failed");
  }
}

完成!现在/app/services支持BASIC身份验证,但其余应用程序支持FORM。

答案 2 :(得分:0)

从Servlet 3.0(Tomcat 7+)开始,您可以以编程方式执行HttpServletRequest.login()

获取用户:传递罗马诺夫的答案并登录。

答案 3 :(得分:-1)

Spring安全性会安装自己的过滤器。您可以编写自己的过滤器/过滤器并安装它们。如果您可以轻松识别哪个URL模式使用一个身份验证,另一个使用两个过滤器则相当简单