雅加达EE - 自定义服务器身份验证模块(登录模块),在哪里散列密码?贾斯皮克



我正在尝试实现一个登录模块,以便我可以在我的Web应用程序上执行"记住我"功能,并使用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。我也不确定该怎么做。

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

第一个 - 我个人赞成 - 是完全忘记专有的AS提供的JAAS LoginModule(LM(,并在ServerAuthModule(SAM(validateRequest方法中自己完整地实现身份验证过程。这为您提供了选择应用程序如何持久化凭据(哈希/加盐、数据库架构(以及 SAM 应如何执行客户端提供的凭据验证的终极自由选择。因此,在这种情况下,SAM 负责以线程安全和高效的方式连接到数据库;这将是这种方法唯一可能棘手的方面。

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

  1. SAM 的 initialize 方法查询封装在运行时提供的options中的javax.security.auth.login.LoginContext密钥的值。它还构造了一个额外的CallbackHandler,支持 NameCallbackPasswordValidationCallback 。最后,它使用上述实例(分别为namecallbackHandler参数(实例化特定于请求的LoginContext (LC(。
  2. 它的validateRequest委托给信用证的login;然后将底层LM在法郎Subject中建立的任何Principal传送给AS(通过CallerPrincipalCallbackGroupPrincipalCallback(。
  3. cleanSubject代表参加LC的logout

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

一些结束语:

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

另请参阅:

  • JSR-196 1.1 MR 2
  • JASPIC的登录模块桥接配置文件的示例使用

相关内容

  • 没有找到相关文章

最新更新