我有一个带有 @CustomFormAuthenticationMechanismDefinition
的应用程序,我想在登录和注销时记录用户名、 session ID、IP 地址等。应用此注释的 HttpAuthMechanism 将给定 session 与主体相关联,我可以通过 SecurityContext 访问该主体。通过直接注销,我的日志记录没有问题,但我还想在 session 超时时进行日志记录。因此,我创建了一个 HttpSessionListener,并在其 sessionDestroyed()
方法中尝试通过 SecurityContext
访问登录用户,但它返回一个空集,可能是因为 securityContext 已失效已经。
我想到的一个解决方案是将用户主体存储在 session 参数中(这可能发生在 HttpAuthMechanism
实现中),并从 HttpSessionEvent
从那里访问它> 对象,但这感觉不是最干净的解决方案。我可以使用另一个监听器或其他解决方案吗?
最佳答案
我使用了自定义的 HttpAuthenticationMechanism,如果有人需要它的话,这里就是它(尽管我很高兴收到一些关于它是否有任何安全缺陷或改进的反馈) .
在实现 HttpAuthenticationMechanism
的 @ApplicationScoped
类中:
@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
}
}
关于java - 如何使用 Java EE 安全性从 HttpSessionListener 访问登录主体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56853513/