I would like to track user's sessions. I am interested in getting the user logname, context he accessed and the time when he accessed a certain context.
I was thinking of using a class that implements HttpSessionListener (overriding sessionCreated(final HttpSessionEvent se) and sessionDestroyed(final HttpSessionEvent se)) but on these methods I don't get access to the request (from where I could pull the user's logname and context he accessed).
Any suggestions are welcome.
I think a servlet Filter is more suitable for what you want. I suggest to write a custom filter around all the urls you want to track.
In the doFilter() method you have access to the HttpServletRequest as needed. From the request object you can get HttpSession too.
Here is an example:
#WebFilter("/*")
public class TrackingFilter implements Filter {
private FilterConfig filterConfig;
#Override
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpSession session = request.getSession(false);
String loggedInUser = "Unregistered user";
//assuming you have a session attribute named user with the username
if(session != null && session.getAttribute("user") != null) {
loggedInUser = (String) session.getAttribute("user");
}
Date accessedDate = new Date();
filterConfig.getServletContext().log(
String.format("%s accessed context %s on %tF %tT",
loggedInUser, request.getRequestURI() ,
accessedDate, accessedDate)
);
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
}
See also: JavaEE6 tutorial section about filters.
Related
I want to create a "Impersonate" feature in my JSF application. This functionality would provide the Administrator with the ability to access the application authenticated with a low-level user without even knowing the password.
I though it would be a simple setUserPrincipal, similar to what I use to get the current logged in User
FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal(), but I couldn't find any "setUserPrincipal" method inside javax.faces.context.ExternalContext...
In short, what I want is to programmatically change the current logged in user, so the admin can impersonate any other user, without informing the credentials. Is it possible?
Thanks
I strongly advise against playing with authentication/authorization unless you really don't have alternatives.
Anyway, leave out JSF, it comes in the game too late.
The simplest way is to provide a customized request, supplied with a filter:
#WebFilter(filterName = "impersonateFilter", urlPatterns = "/*", asyncSupported = true)
public class ImpersonateFilter implements Filter
{
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
// do nothing
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
ImpersonateRequest impersonateRequest = new ImpersonateRequest(httpRequest);
chain.doFilter(impersonateRequest, response);
}
#Override
public void destroy()
{
// do nothing
}
public static class ImpersonateRequest extends HttpServletRequestWrapper
{
protected Principal principal;
public ImpersonateRequest(HttpServletRequest request)
{
super(request);
HttpSession session = request.getSession(false);
if(session != null)
{
principal = (Principal) session.getAttribute(ImpersonateRequest.class.getName());
}
}
#Override
public Principal getUserPrincipal()
{
if(principal == null)
{
principal = super.getUserPrincipal();
}
return principal;
}
public void setUserPrincipal(Principal principal)
{
this.principal = principal;
getSession().setAttribute(ImpersonateRequest.class.getName(), principal);
}
#Override
public String getRemoteUser()
{
return principal == null ? super.getRemoteUser() : principal.getName();
}
}
}
Something like this should suffice.
*#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("INTERCEPTING---");
if(request !=null && request.getParameter("email")!=null){
System.out.println("session is NOT null------");
session = request.getSession();
session.setAttribute("user", request.getParameter("email"));
}else{
try{
if (session==null){
System.out.println("session lost");
}
}catch(IllegalStateException exception){
System.out.println("session expired");
"redirect:http://localhost:8080/web/authservice/fail";
/this is the redirection that i used, and i tried model and view as well but yet it does not work/
}
}
return true;
}
/*in the above method how should I redirect. Its not redirecting...............
Im unable to redirect the after the session expired it gives me a response commit exception. I ve been trying for hours no solution
Please provide a suitable solution */
Use a servlet filter instead, (place it according to you intercepet requirement in your web.xml).
public class CustomRequestFilter implements Filter {
WebApplicationContext springContext;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
springContext = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
}
#Override
public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain) throws IOException, ServletException, LicenseException {
HttpServletRequest request = (HttpServletRequest) req;
if (XXX) {
//forward or redirect as per requirement
request.getRequestDispatcher("/WEB-INF/jsp/errors/SystemError.jsp").forward(request, response);
}
chain.doFilter(request, response);
}
}
I need to do some checks before every page is loaded to see if there's a need to redirect the user to another page (for security reasons).
When I was using JSF 2.0 I used a phase listener to do this job. Now that I'm using JSF 2.2 and all my beans are not JSF beans anymore, but CDI beans, I think I'm offered better choices to do this (or not?).
I've heard of the viewAction event, but I wouldn't like to be repeating metadata on every page (only if there's no other option).
So what's the best approach to implement this scenario in JSF 2.2 with CDI?
UPDATE (after #skuntsel suggestion)
This is the filter that I'm using for now. I would like to use it only after authentication to simplify its code. By the way, if you can see any mistake in it, I would appreciate if you told me.
#WebFilter("/*")
public class SolicitacoesFilter implements Filter
{
// I can't just use #Inject private User _user, because it needs to be initialized
// only when the user is authenticated. Otherwise an exception is thrown. If this
// filter was called only after the authentication I could use the mentioned code.
private User _user;
#Inject
private Instance<User> _userGetter;
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
if (initializeUser(request))
{
if (_user.isProvisoryPassword())
{
// Redirect to another page...
return;
}
if (_user.getStatus() != Status.ACTIVE)
{
// Redirect to another page...
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy()
{
}
private boolean initializeUser(ServletRequest request)
{
boolean userAuthenticated = ((HttpServletRequest) request).getUserPrincipal() != null;
if (userAuthenticated)
{
if (_user == null)
{
_user = _userGetter.get();
}
}
else
{
_user = null;
}
return _user != null;
}
}
Ok, what are the purposes of your redirection need ?
If it's about checking session User for authentification purposes, use filter:
Let's assume there is login form at : http://www.domain.com/login.jsf.
Once the user fires connect button, we want to redirect him to http://www.domain.com/member/welcome.jsf, and avoid other people not to access the member/welcome.jsf domain, I mean all the pages which are in http://www.domain.com/member/....
Here a simple design:
#WebFilter("/member/*")
public class SecurityCheck implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();
if (session == null || session.getAttribute("User") == null) {
response.sendRedirect(request.getContextPath() + "/index.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
#Override
public void destroy() {
// Cleanup global variables if necessary.
}
Other case, use:
<h:link></h:link>,or <h:commandLink></h:commandLink> // Checking in the managed Beans method
You can also xml file, for redirection.
I was designated to implement a security requierement to mimic "messenger like" authentication, for example: if a user first logs in, then another user tries to log in with the same username, this new user will be prompted to "kick" the previously loged user and the system should invalidate the first user's web session, the web app is running on tomcat 6, i was taking a look at HTTPSessionContext but its now deprecated, is there an alternative or should i go and implement something myself using a HTTPSessionListener ?
HTTPSessionListener might not work because you don't have access to the user principal there. I have done something similar using a filter (without the invalidation of the session). This is a striped down version of what I did with some code that might work for your situation (haven't tested it):
public class ApplicationFilter implements Filter {
private Map<String, HttpSession> sessions = new HashMap<String, HttpSession>();
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
Principal principal = request.getUserPrincipal();
HttpSession session = request.getSession();
if (principal != null && session.getAttribute("THE_PRINCIPAL") == null) {
// update the current session
session.setAttribute("THE_PRINCIPAL", session);
// get the username from the principal
// (assumes you using container based authentication)
String username = principal.getName();
// invalidate previous session if it exists
HttpSession s = sessions.get(username);
if (s != null)
s.invalidate();
// register this session as the one for the user
sessions.put(username, session);
}
chain.doFilter(servletRequest, servletResponse);
}
}
I have a JSF app and would like to have the user auto logout after a period of inactivity. Is there an standard way to do this?
Generally, the server (Tomcat, Glassfish...) that hosts the web application handles a timeout for a session.
For example, in Tomcat, you can define the session timeout for a particular web application by adding the folowing lines in the web.xml file:
<session-config>
<session-timeout>30</session-timeout>
</session-config>
This will set the timeout to 30 minutes.
When a user does not send any request during a time greater that this defined timeout, the session on the server is invalidated. If the user tries to reconnect after the session has been invalidated, he will generally be redirected to another page or to an error page.
You can develop your own JSF Filter that will automatically redirect the user to a timeout.html page. Here is an example of such a filter :
public class TimeoutFilter implements Filter {
private static final String TIMEOUT_PAGE = "timeout.html";
private static final String LOGIN_PAGE = "login.faces";
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
if ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse)) {
HttpServletRequest requestHttp = (HttpServletRequest) request;
HttpServletResponse responseHttp = (HttpServletResponse) response;
if (checkResource(requestHttp)) {
String requestPath = requestHttp.getRequestURI();
if (checkSession(requestHttp)) {
String timeoutUrl = hRequest.getContextPath() + "/" + TIMEOUT_PAGE;
responseHttp.sendRedirect(timeoutUrl);
return;
}
}
filterChain.doFilter(request, response);
}
private boolean checkResource(HttpServletRequest request) {
String requestPath = request.getRequestURI();
return !(requestPath.contains(TIMEOUT_PAGE) || requestPath.contains(LOGIN_PAGE) || requestPath.equals(hRequest.getContextPath() + "/"));
}
private boolean checkSession(HttpServletRequest request) {
return request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
}
public void destroy() {
}
}