JavaEE身份验证:如何捕获登录事件?

JavaEE身份验证:如何捕获登录事件?,java,authentication,jakarta-ee,Java,Authentication,Jakarta Ee,给定为Java web应用定义的FORM类型的身份验证机制,在重定向到请求的资源之前,如何捕获登录执行的事件?有没有什么监听器可以让我在用户登录时执行代码 我觉得定义过滤器不是最好的解决方案,因为过滤器链接到资源,即使用户已经通过身份验证并请求资源,也会被调用。我想知道是否有一些类/方法仅由登录事件触发。您可以在j\u安全检查URI上使用Servlet过滤器。此筛选器不会在每个请求上调用,而仅在登录请求上调用 检查以下页面——这在WebSphereAppServer和WebSphereLiber

给定为Java web应用定义的FORM类型的身份验证机制,在重定向到请求的资源之前,如何捕获登录执行的事件?有没有什么监听器可以让我在用户登录时执行代码


我觉得定义过滤器不是最好的解决方案,因为过滤器链接到资源,即使用户已经通过身份验证并请求资源,也会被调用。我想知道是否有一些类/方法仅由登录事件触发。

您可以在
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应用程序中,有许多更好的东西需要优化。