There are cases where we need to generate reports for a users last log-in in AEM.
I have seen many help blogs but none of them worked for me. Below given an approach which worked for me.
Approach
Utilising 'AuthenticationInfoPostProcessor' service in combination with ACS commons, its going to be easy to generate such reports. This is tested on AEM 6.5 version.
Step1: Deploy below Java code which will capture lastLogin information.
Java class which captures the users last login and update the user node
Note: Modify the conditions as per your project requirement.
--Java class START ---
package yourpackage.core.services;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.jcr.Session;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.auth.core.spi.AuthenticationInfo;
import org.apache.sling.auth.core.spi.AuthenticationInfoPostProcessor;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(name = "UserProfileService", service = AuthenticationInfoPostProcessor.class, immediate = true)
public class UserProfileService implements AuthenticationInfoPostProcessor {
/**
* This class generate Last login property of any user profile
*
* @param authenticationinfo
* @param servletrequest
* @param servletresponse
*/
private static final Logger LOGGER = LoggerFactory.getLogger(UserProfileService.class);
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Override
public void postProcess(AuthenticationInfo info, HttpServletRequest request, HttpServletResponse response)
throws LoginException {
/**
* Users last logged in will be his last active time in AEM
* Executed only when it is a logout operation to ensure the last active time is captured
* Ensure to update the code with relevant condition
*/
if ((info != null && info.getAuthType() == null) || (request != null && request.getServletPath() != null
&& (*Your condition 1*))) {
LOGGER.debug("AuthenticationInfo is null. " + "we can skip post processing this request.");
return;
}
ResourceResolver resourceResolver = null;
Session session = null;
UserManager userManager = null;
Authorizable auth = null;
try {
resourceResolver = resourceResolverFactory.getResourceResolver(info);
session = resourceResolver.adaptTo(Session.class);
userManager = resourceResolver.adaptTo(UserManager.class);
auth = userManager.getAuthorizable(session.getUserID());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
//Anonymous users need not be checked
if (auth.getID() != null && (*Your condition 2*)) {
LOGGER.info("Logged in Users log in");
//Profile will have a new property
auth.setProperty("profile/lastLoggedIn", session.getValueFactory().createValue(sdf.format(new Date())));
session.save();
session.logout();
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
--Java class END---
In my case I had used below conditions.
(*Your condition 1*) - !request.getServletPath().equals("/system/sling/logout.html")
(*Your condition 2*) - !auth.getID().equals("anonymous")
Step 2: ACS Commons Report
Now in ACS common reports create a new report with query of type JCRSQL2
SELECT * FROM [rep:User] as nodes WHERE ISDESCENDANTNODE("/home/users")
AND nodes.[profile/lastLoggedIn] IS NOT NULL
AND NOT ISDESCENDANTNODE([/home/users/community])
AND NOT ISDESCENDANTNODE([/home/users/mac])
AND NOT ISDESCENDANTNODE([/home/users/rep:policy])
AND NOT ISDESCENDANTNODE([/home/users/screens])
AND NOT ISDESCENDANTNODE([/home/users/system])
And configure the report column as below.
Now you will be able to Generate the final report as shown below.
Demo Video