1

我正在尝试实现一个登录模块,以便我可以在我的网络应用程序上执行“记住我”功能,并使用 bcrypt 对我的密码进行哈希处理。为了构建我使用本教程的类。但是,在实施此操作后我没有设法连接。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();
        }
    }
}

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

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)

同样在我的页面上,我曾经有一个注册和一个登录按钮,只有当用户为空时才会显示,否则我的用户名在顶部。现在我实现了这个,就像用户连接为“匿名”(所以在页面顶部有“你作为匿名连接”。为了防止这种情况,我做了一个临时修复:

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

我试过了 :

isUserInGroup("ANONYMOUS"); 

但是没有用户,所以我得到了一个 npe。我也不确定该怎么做。

4

1 回答 1

3

您可以在此处采用两种替代方法。

第一个——我个人赞成——将完全忘记专有的 AS 提供的 JAAS (LM),并在你的 (SAM)方法LoginModule中自己实现整个身份验证过程。这为您提供了关于应用程序如何保存凭据(散列/加盐、DB 模式)以及 SAM 如何执行客户端提供的凭据验证的最终选择自由。因此,在这种情况下,SAM 负责以线程安全和高效的方式连接到 DB;这将是这种方法唯一潜在的棘手方面。ServerAuthModulevalidateRequest

或者,您的 SAM 可以将凭证验证委托给 AS 提供的 LM(或您创作的 LM,前提是它适用,即供应商特定的扩展)。我想这就是你一直在努力实现的目标。SAM 必须同时遵守 JASPIC 的Servlet Container Profile及其LoginModule Bridge Profile(参见规范的第 6 章);简而言之,后者要求:

  1. SAM 的initialize方法查询javax.security.auth.login.LoginContext封装在运行时提供的密钥的值options。它还构造了一个附加 CallbackHandler的支持NameCallbackPasswordValidationCallbackLoginContext最后,它使用上述实例(分别是namecallbackHandler参数)实例化一个特定于请求的(LC)。
  2. validateRequest对 LC 的代表login;然后将PrincipalLC 内的基础 LM 建立的任何(s)Subject传送到 AS(通过CallerPrincipalCallbackGroupPrincipalCallback)。
  3. cleanSubject代表 LC 的logout

显然,除非您自己编写 LM,否则没有可用的更改凭证验证过程的干净方法。

一些结束语:

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

也可以看看:

于 2015-07-09T10:28:34.087 回答