自定义ServerAuthModule(loginModule),在哪里哈希密码? JASPIC

时间:2015-06-27 15:07:40

标签: java-ee glassfish jaspic

我试图实现一个loginmodule,这样我就可以执行一个"记住我"我的网络应用程序上的功能以及使用bcrypt哈希我的密码。为了构建我使用this tutorial的类。但是在实施之后我没有设法连接。 db中的密码目前通过SHA-256进行哈希处理,我怀疑这就是原因。

  public class TestAuthModule implements
        javax.security.auth.message.module.ServerAuthModule {

    @SuppressWarnings("rawtypes")
    protected static final Class[] supportedMessageTypes = new Class[] {
            HttpServletRequest.class, HttpServletResponse.class };

    private CallbackHandler handler;

    public void initialize(MessagePolicy requestPolicy,
            MessagePolicy responsePolicy, CallbackHandler handler,
            @SuppressWarnings("rawtypes") Map options) throws AuthException {
        System.out.println("initialize called.");
        this.handler = handler;
    }

    @SuppressWarnings("rawtypes")
    public Class[] getSupportedMessageTypes() {
        return supportedMessageTypes;
    }

    public AuthStatus validateRequest(MessageInfo messageInfo,
            Subject clientSubject, Subject serverSubject) throws AuthException {
        HttpServletRequest request = (HttpServletRequest) messageInfo
                .getRequestMessage();

        String user = request.getParameter("user");
        String group = request.getParameter("group");

        System.out.println("validateRequest called.");
        System.out.println("User = " + user);
        System.out.println("Group = " + group);

        authenticateUser(user, group, clientSubject, serverSubject);

        return AuthStatus.SUCCESS;
    }

    public AuthStatus secureResponse(MessageInfo msgInfo, Subject service)
            throws AuthException {
        System.out.println("secureResponse called.");
        return AuthStatus.SEND_SUCCESS;
    }

    public void cleanSubject(MessageInfo msgInfo, Subject subject)
            throws AuthException {
        if (subject != null) {
            subject.getPrincipals().clear();
        }
    }

    private void authenticateUser(String user, String group,
            Subject clientSubject, Subject serverSubject) {
        System.out
                .println("Authenticating user " + user + " in group " + group);

        CallerPrincipalCallback callerPrincipalCallback = new CallerPrincipalCallback(
                clientSubject, user);

        GroupPrincipalCallback groupPrincipalCallback = new GroupPrincipalCallback(
                clientSubject, new String[] { group });

        try {
            handler.handle(new Callback[] { callerPrincipalCallback,
                    groupPrincipalCallback });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我这样登录(在实现自定义loginmodule之前确实有效):

private String username;
private Password password;
//....

for (int i = 0; i < x -1 ; i++) {
    this.password = PasswordEncoder
                       .toHex(PasswordEncoder
                            .hash512(this.password + salt));
    }
   // x is the number of time I hashed the password before storing it in db.
   // x-1 because glassfish authentication does it once for me.

   //...
try {
    request.login(username, password + salt);
    } catch (ServletException e)

同样在我的网页上,我曾经有一个注册表和一个登录按钮,只有当用户为空时才会显示,如果没有我在顶部的用户名。现在我实现了它,就像用户连接为&#34; ANONYMOUS&#34; (所以有#34;你在页面顶部连接为ANONYMOUS&#34; 为了防止这种情况,我做了一个临时修复:

    if (username == null || username.equals("ANONYMOUS")) {
        this.isUserConnected = false;
    } else {
        this.isUserConnected = true;
    }

我试过了:

isUserInGroup("ANONYMOUS"); 

但是没有用户,所以我得到了一个npe。我也不确定如何解决这个问题。

1 个答案:

答案 0 :(得分:2)

您可以在这里使用两种替代方法。

第一个 - 我个人赞成 - 将完全忘记专有的AS提供的JAAS LoginModule(LM),并在您{{1}内完成自己的身份验证过程。 }(SAM的)ServerAuthModule方法。对于应用程序如何持久化凭据(散列/腌制,数据库模式)以及SAM如何执行客户端提供的凭据验证,这使您可以自由选择。因此,在这种情况下,SAM负责以线程安全和有效的方式连接到DB;这将是这种方法唯一可能棘手的方面。

或者,您的SAM可以将凭据验证委派给AS提供的LM(或者您编写的LM,只要它适用,即特定于供应商的扩展)。我猜想,这就是你一直在努力实现的目标。然后,SAM必须符合JASPIC的 Servlet容器配置文件及其 LoginModule Bridge Profile (参见规范的第6章);简而言之,后者要求:

  1. SAM的validateRequest方法参考了运行时提供的initialize中封装的javax.security.auth.login.LoginContext密钥的值。它还构建了支持optionsCallbackHandler附加 NameCallback。最后,它使用上述实例(分别为PasswordValidationCallbackLoginContext参数)实例化特定于请求的name(LC)。
  2. callbackHandler代表加入了LC的validateRequest;然后将LC login内的基础LM建立的任何Principal传达给AS(通过SubjectCallerPrincipalCallback)。
  3. GroupPrincipalCallback代表加入了LC的cleanSubject
  4. 显然,除非你自己编写LM,否则没有改变凭证验证过程的简洁方法。

    一些结束语:

      当JASPIC配置为与您的应用程序一起使用时,
    • logout将始终引发异常(请参阅规范的第3.9.2节)。请改用HttpServletRequest.login
    • 您的SAM HttpServletRequest.authenticate应返回正确的validateRequest;无论认证结果如何,当登录成功或认证可选(即所请求的资源不受保护)时,即AuthStatus;将用户重定向到登录页面时SUCCESS;登录失败并且身份验证必需时(SEND_CONTINUE(或抛出SEND_FAILURE)(即请求的资源受到保护或用户明确请求登录)。
    • 未经身份验证的用户的身份应由您的SAM AuthException传达给AS,方法是向validateRequest / CallerPrincipalCallback构造函数提供GroupPrincipalCallback 主体< / em>或 groups 参数。然后,兼容的Servlet容器的null实现应该返回HttpServletRequest.getUserPrincipal。请注意null行为不同,返回AS特定类型的实例以表示未经身份验证的用户(可能是您所见过的)。在任何情况下,与授权相关的应用程序级代码都不应该依赖于匿名EJBContext.getCallerPrincipal的非标准名称。解决 is-guest 问题的另一种方法可能涉及在成功进行身份验证时从SAM设置Principal回调属性(请参阅规范的第3.8.4节)。然后,调用javax.servlet.http.authType不仅会断言用户是否经过身份验证,还会断言您的SAM,另一个SAM或AS特定的LM是否执行了身份验证(考虑使用多种身份验证类型的应用程序)。另一种处理方法可能是将自定义HttpServletRequest.getAuthType实施传递给Principal;然后对通过CallerPrincipalCallback公开的instanceof执行Principal(但请注意,仍然存在问题,例如在GlassFish上需要EJB来实现相同的结果。)< / LI>

    另见: