JavaEE身份验证:如何捕获登录事件?
给定为Java web应用定义的FORM类型的身份验证机制,在重定向到请求的资源之前,如何捕获登录执行的事件?有没有什么监听器可以让我在用户登录时执行代码JavaEE身份验证:如何捕获登录事件?,java,authentication,jakarta-ee,Java,Authentication,Jakarta Ee,给定为Java web应用定义的FORM类型的身份验证机制,在重定向到请求的资源之前,如何捕获登录执行的事件?有没有什么监听器可以让我在用户登录时执行代码 我觉得定义过滤器不是最好的解决方案,因为过滤器链接到资源,即使用户已经通过身份验证并请求资源,也会被调用。我想知道是否有一些类/方法仅由登录事件触发。您可以在j\u安全检查URI上使用Servlet过滤器。此筛选器不会在每个请求上调用,而仅在登录请求上调用 检查以下页面——这在WebSphereAppServer和WebSphereLiber
我觉得定义过滤器不是最好的解决方案,因为过滤器链接到资源,即使用户已经通过身份验证并请求资源,也会被调用。我想知道是否有一些类/方法仅由登录事件触发。您可以在
j\u安全检查
URI上使用Servlet过滤器。此筛选器不会在每个请求上调用,而仅在登录请求上调用
检查以下页面——这在WebSphereAppServer和WebSphereLiberty概要文件中都可以使用
具有这种过滤器的:
@WebFilter("/j_security_check")
public class LoginFilter implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Filter called 1: " +((HttpServletRequest)request).getUserPrincipal());
chain.doFilter(request, response);
System.out.println("Filter called 2: " + ((HttpServletRequest)request).getUserPrincipal());
}
提供以下输出:
// on incorrect login
Filter called 1: null
[AUDIT ] CWWKS1100A: Authentication did not succeed for user ID user1. An invalid user ID or password was specified.
Filter called 2: null
// on correct login
Filter called 1: null
Filter called 2: WSPrincipal:user1
更新
另一种可能的方法是使用您自己的servlet进行登录,将登录页面中的操作更改为该servlet,然后使用request.login()
方法。这是ServletAPI,所以即使在Wildfly中也应该可以工作,并且您可以完全控制登录。您只需要了解wildfly如何传递最初请求的资源URL(WebSphere通过cookie进行传递)
Servlet伪代码:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user = request.getParameter("j_username");
String password = request.getParameter("j_password");
try {
request.login(user, password);
// redirect to requested resource
} catch (Exception e) {
// login failed - redirect to error login page
}
JavaEE中没有这样的事件。然而作为的一部分,容器管理的安全性将完全重新设计,因为它当前跨不同的容器实现,并且不跨容器兼容。本演示文稿对此进行了概述 已经有一个安全API的参考实现在进行中,由我的同事Arjan Tijms开发。有了新的安全API,CDI将用于触发身份验证事件,您只需
@观察。关于规范的讨论在年进行。它还没有在索特里亚具体实施
在此之前,假设基于表单
的身份验证将用户主体内部存储在会话中,最好的办法是在HTTP会话中不存在登录用户的表示时,手动检查servlet过滤器中是否存在用户主体
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
String username = request.getRemoteUser();
if (username != null && request.getSession().getAttribute("user") == null) {
// First-time login. You can do your thing here.
User user = yourUserService.find(username);
request.getSession().setAttribute("user", user);
}
chain.doFilter(req, res);
}
请注意,在/j_security_check
上注册一个过滤器并不能保证其正常工作,因为在第一个过滤器被点击之前,一个体面的容器会在内部处理它,这是出于明显的安全原因(用户提供的过滤器可能会以错误的方式处理请求,可能是意外的,也可能是意外的)
但是,如果您碰巧使用了JavaEE服务器,并且使用了servletcontainer,例如,那么有一种更干净的方法来钩住其内部通知事件,然后触发自定义CDI事件。这在Arjan Tijms中得到了充实。如博客所示,您最终可以得到如下CDIBean:
@SessionScoped
public class SessionAuthListener implements Serializable {
private static final long serialVersionUID = 1L;
public void onAuthenticated(@Observes AuthenticatedEvent event) {
String username = event.getUserPrincipal().getName();
// Do something with name, e.g. audit,
// load User instance into session, etc
}
public void onLoggedOut(@Observes LoggedOutEvent event) {
// take some action, e.g. audit, null out User, etc
}
}
这听起来是个好主意,理论上应该行得通,但对我来说不行,我也不知道为什么。筛选器似乎不是由j\u security\u check
调用触发的。例如,为每个未受保护的资源调用url模式设置为*
的筛选器,但不会为密码错误的登录执行该筛选器。或在成功登录时仅执行一次(对于资源URI,而不是j_security_check
URI)。这里是Wildfly 8@LuigiCortese我使用了以下servlet映射@WebFilter(“/j_security_check”)
,只有在提交登录表单时才会调用filter。它在WebSphere中工作得非常好Liberty@BalusC您的评论不正确,或格式不正确。我的过滤器在j_安全检查中被正确调用。所以我真的不明白你所说的“内部”是什么意思,那么这就是Liberty特有的特性/怪癖。这绝对不应该跨容器依赖。这项技术不可移植,因为许多servlet容器实现在调用任何筛选器之前拦截j_security_check
。我讨厌这样,因为我没有多次向上投票的机会!尽管如此,Undertow解决方案将使您的Web应用程序不完全可移植,这是我现在试图避免的。在这一点上,为一系列URL添加一个过滤器可能是我将要应用的解决方案,我只是有点担心如此广泛应用的过滤器可能会带来的开销,但在一天结束时,大多数情况下,它只会导致一个未经验证的if条件。。。你认为这是一个“可接受的开销”吗?:)的概念也是如此。此外,一些用户的个人资料上有一个愿望列表或捐赠链接。至于开销,这实际上可以忽略不计。也许几纳秒吧。在成熟的web应用程序中,有许多更好的东西需要优化。