首页 > 解决方案 > 如何使用 Java EE Security 从 HttpSessionListener 访问登录的主体?

问题描述

我有一个应用程序@CustomFormAuthenticationMechanismDefinition,我想在登录和注销时记录用户名、会话 ID、IP 地址等。与此注释一起应用的 HttpAuthMechanism 将给定会话与主体相关联,我可以通过SecurityContext. 使用直接注销,我没有问题记录,但我也想在会话超时时记录。所以我创建了一个 HttpSessionListener 并在其sessionDestroyed()方法中尝试通过 访问登录的用户SecurityContext,但它返回一个空集,可能是因为 securityContext 已经失效。

我想到的一种解决方案是将用户主体存储在会话参数中(这可能在HttpAuthMechanism实现中发生)并从HttpSessionEvent对象从那里访问它,但这并不是最干净的解决方案。我可以使用另一个侦听器或其他解决方案吗?

标签: javajakarta-ee

解决方案


我使用了自定义的 HttpAuthenticationMechanism,如果有人需要它,这就是它(尽管我很高兴收到一些关于它是否有任何安全漏洞或改进的反馈)

@ApplicationScoped实现的类中HttpAuthenticationMechanism

@Override
public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {
    if (!httpMessageContext.isProtected()) {
        return httpMessageContext.doNothing();
    }
    HttpSession session = request.getSession(false);

    Credential credential = httpMessageContext.getAuthParameters().getCredential();

    // If we already have a session, we get the user from it, unless it's a new login
    if (session != null && !(credential instanceof UsernamePasswordCredential)) {  
        User user = (User) session.getAttribute("user");
        if (user != null) {
            return httpMessageContext.notifyContainerAboutLogin(user, user.getRoles());
        }
    }

    // If we either don't have a session or it has no user attribute, we redirect/forward to login page
    if (!(credential instanceof UsernamePasswordCredential)) {
        return redirect(request, response, httpMessageContext);
    }

    // Here we have a Credential, so we validate it with the registered IdentityStoreHandler (injected as idStoreHandler)
    CredentialValidationResult validate = idStoreHandler.validate(credential);

    Context context = new Context();
    context.setIp(request.getRemoteAddr());

    if (validate.getStatus() == CredentialValidationResult.Status.VALID) {
        session = request.getSession(true);
        CallerPrincipal callerPrincipal = validate.getCallerPrincipal();
        session.setAttribute("user", callerPrincipal);


        context.setUser(callerPrincipal);
        context.setSessionId(session.getId());

        Logger log = new Logger(logger, "validateRequest", context);

        log.debug("Logged in user: " + callerPrincipal.getName());

        String redirectPage = "whatYouWant.xhtml";

        redirect(request, response, httpMessageContext, redirectPage);

        return httpMessageContext.notifyContainerAboutLogin(validate);
    } else if (validate.getStatus() == CredentialValidationResult.Status.NOT_VALIDATED) {
        return redirect(request, response, httpMessageContext);
    } else {
        // Logging
        return httpMessageContext.responseUnauthorized();
    }
}

并在实施中HttpSessionListener

 @Override
public void sessionDestroyed(HttpSessionEvent se) {
    User user = (User) se.getSession().getAttribute("user");

    if (user != null) {
        // logging
    }
}

推荐阅读