4

我的 Web 应用程序使用内部 Web API(来自浏览器的简单 AJAX 请求,因为它是主要客户端)最终应该向外部公开给第 3 方。由于 API 已经并且必须使用 中的安全约束进行保护,因此必须对web.xml用户或客户端进行身份验证。目前,实现了一个自定义表单身份验证器,它执行一些额外的检查和操作,然后简单地将进一步的身份验证处理委托给FormAuthenticator自定义身份验证器派生的类。这真的很好用,因为用户只是被迫登录并通过身份验证,唯一的客户端是 Web 浏览器。

但是 FORM 身份验证不太适合其他类型的客户端:比方说 Android 客户端、各种 3rd 方客户端等。太通过表单身份验证它们都必须模拟我在这个问题中寻找的行为:我如何模拟使用 JMeter 在 Tomcat 中进行表单身份验证?. 在对 Tomcat 源代码进行了一些调查之后,我有了一个想法,即应该可以扩展AuthenticatorBase该类以实现自己的身份验证方式(喜欢FormAuthenticatorBasicAuthenticator.java做)。我试图做的是:

public final class SimpleAuthenticator extends AuthenticatorBase {

    private static final String USERNAME_PARAMETER = "username";
    private static final String PASSWORD_PARAMETER = "password";

    @Override
    protected boolean authenticate(Request request, Response response, LoginConfig config) throws IOException {
        final Principal principal = request.getUserPrincipal();
        final String ssoId = (String) request.getNote(REQ_SSOID_NOTE);
        if ( principal != null ) {
            if ( ssoId != null ) {
                associate(ssoId, request.getSessionInternal(true));
            }
            return true;
        }
        if ( ssoId != null && reauthenticateFromSSO(ssoId, request) ) {
            return true;
        }
        final String contextPath = request.getContextPath();
        final String requestURI = request.getDecodedRequestURI();
        final boolean login = requestURI.equals(contextPath + "/authenticate");
        if ( !login ) {
            response.sendError(SC_FORBIDDEN);
            return false;
        }
        final String username = request.getParameter(USERNAME_PARAMETER);
        final String password = request.getParameter(PASSWORD_PARAMETER);
        final Realm realm = context.getRealm();
        final Principal authenticatedUserPrincipal = realm.authenticate(username, password);
        if ( authenticatedUserPrincipal == null ) {
            response.sendError(SC_UNAUTHORIZED);
            return false;
        }
        register(request, response, authenticatedUserPrincipal, "SIMPLE", username, password);
        return true;
    }

}

简单地说,我想使用类似的东西/%CONTEXT_PATH%/authenticate?username=%USERNAME%&password=%PASSWORD%来验证我的自定义 non-form 用户SimpleAuthenticator。不太确定在BasicAuthentication这种情况下是否会更好,但我在上面的示例中遇到了以下问题:

  • Tomcat 允许Valvecontext.xml. 如果将另一个身份验证器添加到context.xml文件中,则每个安全资源都会与每个身份验证器一起处理。(原则上,我理解它为什么会发生,但是它们可以为不同的资源分开吗?)
  • /%CONTEXT_PATH%/authenticate不可访问(HTTP 404)。(尚不清楚,但不知道这/j_security_check是以某种方式模拟的。)
  • 我找不到在 Tomcat 中指定多个身份验证方案的方法,因此“旧”Web 浏览器客户端仍然可以使用FormAuthenticator(就像今天一样),但“轻量级”客户端可以使用我尝试实现的简化身份验证SimpleAuthenticator. (甚至不知道这是否可能——这是核心)
  • 据我了解 servlet 规范,login-config整个 web 应用程序只允许一个。(好吧,我应该使用另一个 Web 应用程序来提供 API 吗?)
  • 我看到一些提到通过 实现自定义身份验证Filter,但如果可能的话——我想将身份验证器模块单独保存在一个地方(就像它已经存在并通过Tomcat 确认:Web 应用程序中的自定义表单身份验证器,而不是一个独立的 JAR 模块。可能吗?)。但是,我可以从头开始回顾我使用的一般概念。

我怀疑我在做一件非常错误的事情并且完全理解,但我不相信没有办法在 Tomcat 中实现多个身份验证方案。

有没有办法提供多种身份验证方案来提取身份验证FormAuthetication(对于轻量级客户端)?您的帮助将非常感激。提前致谢。

4

1 回答 1

2

底线:您不能在 Tomcat 中为同一个 Web 应用程序配置多个身份验证方案。您可以配置一种身份验证方法,用于在以下位置进行身份验证web.xml

<login-config>
    <auth-method>CLIENT-CERT</auth-method>
</login-config>

Tomcat 在身份验证器上映射 Web 应用程序身份验证。例如,对于CLIENT-CERT它将使用org.apache.catalina.authenticator.SSLAuthenticator 映射在文件中org/apache/catalina/startup/Authenticators.properties

出于您在问题中描述的目的,您可以使用常规的 Tomcat Form Authenticator ( <auth-method>FORM</auth-method>)

以下代码将对用户进行身份验证:

String url = "j_security_check?j_username=" + username + "&j_password=" + password;
String redirectUrl = response.encodeRedirectURL(url);
response.sendRedirect(redirectUrl);

当您第一次访问应用程序时,通过上面的执行 URL 对用户进行身份验证。此外,您可以对“旧”代码使用 Form Authenticator :) 请注意,上面的代码使用GET方法。POST我建议在方法中提交相同的参数j_security_check(你可以在 AJAX 中完成)。

于 2013-07-16T12:06:29.560 回答