java - 如何使用 Java EE Security 从 HttpSessionListener 访问登录的主体?
问题描述
我有一个应用程序@CustomFormAuthenticationMechanismDefinition
,我想在登录和注销时记录用户名、会话 ID、IP 地址等。与此注释一起应用的 HttpAuthMechanism 将给定会话与主体相关联,我可以通过SecurityContext
. 使用直接注销,我没有问题记录,但我也想在会话超时时记录。所以我创建了一个 HttpSessionListener 并在其sessionDestroyed()
方法中尝试通过 访问登录的用户SecurityContext
,但它返回一个空集,可能是因为 securityContext 已经失效。
我想到的一种解决方案是将用户主体存储在会话参数中(这可能在HttpAuthMechanism
实现中发生)并从HttpSessionEvent
对象从那里访问它,但这并不是最干净的解决方案。我可以使用另一个侦听器或其他解决方案吗?
解决方案
我使用了自定义的 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
}
}
推荐阅读
- styles - stylegan 编码器打印图像太小
- c# - 使用窗口标题或进程名称
- python - 允许在 Sphinx 文档字符串的简短摘要中使用句号
- linux - 如何将两个 CSV 文件与 Linux 列合并?
- javascript - 使用 JavaScript 将元素中的某些文本转换为元素本身
- elasticsearch - 如何使用 Kibana 脚本字段在嵌套字段中获取值?
- sql - “不同”导致复杂查询
- google-colaboratory - mkdir 和 joinpath 在 Google 中不起作用
- android - 通过 Android 后台服务 Kotlin 轮询在线打印机队列
- c# - 为什么 Task.Run 给出错误?