Java EE authentication: how to capture login event?

后端 未结 2 1732
北荒
北荒 2020-12-01 17:37

Given an authentication mechanism of type FORM defined for a Java web app, how do you capture the login performed event before being redirected to requested resourc

相关标签:
2条回答
  • 2020-12-01 17:59

    You can use Servlet filter on the j_security_check URI. This filter will not be invoke on every request, but only on the login request.

    Check the following page - Developing servlet filters for form login processing - this works in WebSphere App Server, and WebSphere Liberty profile.

    Having such filter:

    @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());
    
    }
    

    gives the following output:

    // 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
    

    UPDATE

    Other possible way to do it is to use your own servlet for login, change the action in your login page to that servlet and use request.login() method. This is servlet API so should work even in Wildfly and you have full control over login. You just need to find out how wildfly passes the originally requested resource URL (WebSphere does it via cookie).

    Servlet pseudo code:

    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
        }
    
    0 讨论(0)
  • 2020-12-01 18:02

    There's no such event in Java EE. Yet. As part of JSR375, container managed security will be totally reworked as it's currently scattered across different container implemantations and is not cross-container compatible. This is outlined in this Java EE 8 Security API presentation.

    There's already a reference implementation of Security API in progress, Soteria, developed by among others my fellow Arjan Tijms. With the new Security API, CDI will be used to fire authentication events which you can just @Observes. Discussion on the specification took place in this mailing list thread. It's not yet concretely implemented in Soteria.

    Until then, assuming FORM based authentication whereby the user principal is internally stored in the session, your best bet is manually checking in a servlet filter if there's an user principal present in the request while your representation of the logged-in user is absent in the HTTP session.

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

    Do note that registering a filter on /j_security_check is not guaranteed to work as a decent container will handle it internally before the first filters are hit, for obvious security reasons (user-provided filters could manipulate the request in a bad way, either accidentally or awarely).

    If you however happen to use a Java EE server uses the Undertow servletcontainer, such as WildFly, then there's a more clean way to hook on its internal notification events and then fire custom CDI events. This is fleshed out in this blog of Arjan Tijms. As shown in the blog, you can ultimately end up with a CDI bean like this:

    @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
        }
    }
    
    0 讨论(0)
提交回复
热议问题