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

时间:2016-03-14 17:55:12

标签: java authentication java-ee

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

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

2 个答案:

答案 0 :(得分:7)

Java EE中没有此类事件。然而。作为JSR375的一部分,容器管理的安全性将完全重做,因为它在不同的容器实现中目前是scattered,并且不是跨容器兼容的。这在Java EE 8 Security API演示文稿中有所概述。

已经有一个正在进行的安全API的参考实现,Soteria,由我的同伴Arjan Tijms开发。使用新的安全API,CDI将用于触发您只能@Observes的身份验证事件。关于规范的讨论发生在this mailing list thread。它还没有在Soteria中具体实施。

在此之前,假设基于FORM的身份验证(其中用户主体在内部存储在会话中),最好的办法是手动检查servlet过滤器,如果请求中存在用户主体,则表示已记录-in用户在HTTP会话中不存在。

@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上注册过滤器并不能保证工作,因为一个像样的容器会在第一个过滤器被命中之前在内部处理它,这是出于明显的安全原因(用户提供的过滤器可能会操纵请求糟糕的方式,无论是意外还是意识到。)

如果您碰巧使用Java EE服务器使用Undertow servletcontainer,例如WildFly,那么有一种更简洁的方法可以挂钩其内部通知事件,然后触发自定义CDI事件。这是在Arjan Tijms this blog中充实的。如博客所示,您最终可以得到像这样的CDI bean:

@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
    }
}

答案 1 :(得分:0)

您可以在j_security_check URI上使用Servlet过滤器。不会在每个请求上调用此过滤器,而只会在登录请求上调用。

检查以下页面 - Developing servlet filters for form login processing - 这在WebSphere App Server和WebSphere Liberty概要文件中有效。

有这样的过滤器:

@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()方法。这是servlet API,因此即使在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
    }