I have following two methods in my backing bean -
public String validateUser() {
FacesContext facesCtx = FacesContext.getCurrentInstance();
if(userName.equals("user1") && password.equals("pass1")) {
User user = new User();
user.setUserName(userName);
HttpSession session = (HttpSession) facesCtx.getExternalContext().getSession(false);
session.setAttribute(User.SESSION_ATTRIBUTE, user);
return "secured/home.jsf?faces-redirect=true";
}
if(!userName.equals(LoginBean.USERNAME)) {
FacesMessage msgForUserName = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Username did not match.", null);
facesCtx.addMessage("loginForm:userName", msgForUserName);
}
if(!password.equals(LoginBean.PASSWORD)) {
FacesMessage msgForPassword = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Password did not match.", null);
facesCtx.addMessage("loginForm:password", msgForPassword);
}
return null;
}
public String logout() {
logger.info("Logging out .........................................");
FacesContext facesCtx = FacesContext.getCurrentInstance();
HttpSession session = (HttpSession) facesCtx.getExternalContext().getSession(false);
session.invalidate();
return "login.jsf?faces-redirect=true";
}
I don't know why the redirection is working in the first method (i.e. validateUser()), but it's not working in the second method (i.e. logout()).
The code inside the logout method is actually executed, the session also gets invalidated,but somehow the browser stays on the same page.
And, I am using PrimeFaces p:commandButton and the ajax is enabled on both of them.
Any one, any idea?
Thank you.
but somehow the browser stays on the same page. And, I am using PrimeFaces p:commandButton and the ajax is enabled on both of them
I wouldn't expect it to fail. I suspect that this has something to do with the invalidated session. Try it with ajax="false" on the <p:commandButton>.
Unrelated to the problem, you should try to minimize the javax.servlet imports in your JSF managed beans. They often indicate that you're doing things in the wrong place or the clumsy way. In pure JSF2, you can invalidate the session as follows:
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
You can get/set objects in the session by the session Map.
Map<String, Object> sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
// ...
Or just make it a managed bean (property).
See also:
How can I create a new session with a new User login on the application?
Related
I have already set the session time in web.Config file, after the session time out I want my page automatically to get refreshed and redirect to Login page, but its not happening, kindly find my code as mentioned below :- Pleaes help me , I have been trying since almost 1 week, but no progress.
HttpContext ctx = HttpContext.Current;
if (ctx.Session != null)
{
// check if a new session id was generated
if (ctx.Session.IsNewSession)
{
HttpContext ctx1 = HttpContext.Current;
// check sessions here
if (ctx.Session["UserName"] == null || !filterContext.HttpContext.Request.IsAuthenticated)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
FormsAuthentication.SignOut();
filterContext.Result = new RedirectResult("~/Authentication/Logon");
base.OnActionExecuting(filterContext);
}
}
}
}
The session times out only on the server. Your page will never gets refreshed automatically. You need to implement a polling mechanism from the browser to check for session availability and refresh accordingly.
I think I have come across a bug in spring-session but I just want to ask here if it really is a bug. Before I forget
https://github.com/paranoiabla/spring-session-issue.git
here's a github repository that reproduces the problem. Basically I have a 2 controllers and 2 jsps, so the flow goes like this:
User opens http://localhost:8080/ and the flow goes through HomepageController, which puts 1 attribute in the spring-session and returns the homepage.jsp which renders the session id and the number of attributes (1)
The homepage.jsp has this line inside it:
${pageContext.include("/include")}
which calls the IncludeController to be invoked.
The IncludeController finds the session from the session repository and LOGs the number of attributes (now absolutely weird they are logged as 0) and returns the include.jsp which renders both the session id and the number of session attributes (0).
The session id in both jsps is the same, but somehow after the pageContext.include call the attributes were reset to an empty map!!!
Can someone please confirm if this is a bug.
Thank you.
Problem
The problem is that when using MapSessionRepository the SessionRepositoryFilter will automatically sync the HttpSession to the Spring Session which overrides explicit use of the APIs. Specifically the following is happening:
SessionRepositoryFilter is obtaining the current Spring Session. It caches it in the HttpServletRequest to ensure that every invocation of HttpServletRequest.getSession() does not make a database call. This cached version of the Spring Session has no attributes associated with it.
The HomepageController obtains its own copy of Spring Session, modifies it, and then saves it.
The JSP flushes the response which commits the HttpServletResponse. This means we must write out the session cookie just prior to the flush being set. We also need to ensure that the session is persisted at this point because immediately afterwards the client may have access to the session id and be able to make another request. This means that the Spring Session from #1 is saved with no attributes which overrides the session saved in #2.
The IncludeController obtains the Spring Session that was saved from #3 (which has no attributes)
Solution
There are two options I see to solving this.
Use HttpSession APIs
So how would I solve this. The easiest approach is to stop using the Spring Session APIs directly. This is preferred anyways since we do not want to tie ourselves to the Spring Session APIs if possible. For example, instead of using the following:
#Controller
public class HomepageController {
#Resource(name = "sessionRepository")
private SessionRepository<ExpiringSession> sessionRepository;
#Resource(name = "sessionStrategy")
private HttpSessionStrategy sessionStrategy;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(final Model model) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
final String sessionIds = sessionStrategy.getRequestedSessionId(request);
if (sessionIds != null) {
final ExpiringSession session = sessionRepository.getSession(sessionIds);
if (session != null) {
session.setAttribute("attr", "value");
sessionRepository.save(session);
model.addAttribute("session", session);
}
}
return "homepage";
}
}
#Controller
public class IncludeController {
private final static Logger LOG = LogManager.getLogger(IncludeController.class);
#Resource(name = "sessionRepository")
private SessionRepository<ExpiringSession> sessionRepository;
#Resource(name = "sessionStrategy")
private HttpSessionStrategy sessionStrategy;
#RequestMapping(value = "/include", method = RequestMethod.GET)
public String home(final Model model) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
final String sessionIds = sessionStrategy.getRequestedSessionId(request);
if (sessionIds != null) {
final ExpiringSession session = sessionRepository.getSession(sessionIds);
if (session != null) {
LOG.error(session.getAttributeNames().size());
model.addAttribute("session", session);
}
}
return "include";
}
}
You can simplify it using the following:
#Controller
public class HomepageController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletRequest request, Model model) {
String sessionIds = request.getRequestedSessionId();
if (sessionIds != null) {
final HttpSession session = request.getSession(false);
if (session != null) {
session.setAttribute("attr", "value");
model.addAttribute("session", session);
}
}
return "homepage";
}
}
#Controller
public class IncludeController {
#RequestMapping(value = "/include", method = RequestMethod.GET)
public String home(HttpServletRequest request, final Model model) {
final String sessionIds = request.getRequestedSessionId();
if (sessionIds != null) {
final HttpSession session = request.getSession(false);
if (session != null) {
model.addAttribute("session", session);
}
}
return "include";
}
}
Use RedisOperationsSessionRepository
Of course this may be problematic in the event that we cannot use the HttpSession API directly. To handle this, you need to use a different implementation of SessionRepository. For example, another fix is to use the RedisOperationsSessionRepository. This works because it is smart enough to only update attributes that have been changed.
This means in step #3 from above, the Redis implementation will only update the last accessed time since no other attributes were updated. When the IncludeController requests the Spring Session it will still see the attribute saved in HomepageController.
So why doesn't MapSessionRepository do this? Because MapSessionRepository is based on a Map which is an all or nothing thing. When the value is placed in the map it is a single put (we cannot break that up into multiple operations).
I don't redirect or forward my user to another page. So when the my SessionExpiredExceptionHandler (extends ExceptionHandlerWrapper) handles the ViewExireException. I want the user to stay on the same page and display a PrimeFaces Dialog. For notifying that the session has expired and that the user needs to login again (dialog based). I am use Servlet 3.1 functions to login/logout user and Basic/file for auth-method to map the users to different system roles.
What is happening now is that the View/page get refreshed after 2 min, but the session doesn't get invalidated. That only happens the second time when the page refreshes, after 4 min.
<session-config>
<session-timeout>2</session-timeout>
</session-config>
Edit:
Which is refreshed by the meta tag:
<meta http-equiv="refresh" content="#{session.maxInactiveInterval}" />
How can I make SessionExpiredExceptionHandlerinvalidate the session object (Servlet logout) when the Exceptions occur the first time, and how can I invoke a JavaScript (expireDlg.show()) on the client to display a PrimeFaces dialog ?
I have looked at some other threads but not found a viable solution.
Session time-out
SessionExpiredExceptionHandler
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof ViewExpiredException) {
ViewExpiredException vee = (ViewExpiredException) t;
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
NavigationHandler nav = fc.getApplication().getNavigationHandler();
try {
requestMap.put("currentViewId", vee.getViewId());
nav.handleNavigation(fc, null, "Home");
fc.renderResponse();
} finally {
i.remove();
}
}
}
// At this point, the queue will not contain any ViewExpiredEvents.
// Therefore, let the parent handle them.
getWrapped().handle();
}
web.xml
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/home.xhtml</location>
</error-page>
How can I make SessionExpiredExceptionHandler invalidate the session object (Servlet logout) when the Exceptions occur the first time
The session is supposedly to be already invalidated/expired (otherwise a ViewExpiredException wouldn't be thrown at all), so I don't see how it's useful to manually invalidate/expire it yourself. But for the case that, you can invalidate it as follows:
externalContext.invalidateSession();
and how can I invoke a JavaScript (expireDlg.show()) on the client to display a PrimeFaces dialog ?
You can use the PrimeFaces RequestContext API to programmatically instruct PrimeFaces to execute some JS code on complete of ajax response.
RequestContext.getCurrentInstance().execute("expireDlg.show()");
Don't forget to remove the navigation handler block from the exception handler if you actually don't want to navigate.
This solution worked for my case. It seams that Primefaces (3.3) is swallowing the ExceptionQueuedEvent. There are no Exception to handle when my ViewExceptionHandler gets called. So instead I used the p:idleMonitor component with event listner. I also removed the meta refresh tag.
<p:idleMonitor timeout="#{(session.maxInactiveInterval-60)*1000}">
<p:ajax event="idle" process="#this" update="sessionMsg" listener="#{userController.userIdleSession()}" />
<p:ajax event="active" process="#this" update="sessionMsg" listener="#{userController.userActiveSession()}"/>
</p:idleMonitor>
One weird thing is if the timeoutis excatly the same as the web.xmlsession time-out parameter, the listener won't be invoked.
Bean functions
public void userIdleSession() {
if (!userIdleMsgVisable) {
userIdleMsgVisable = true;
JsfUtil.addWarningMessage(JsfUtil.getResourceMessage("session_expire_title"), JsfUtil.getResourceMessage("session_expire_content"));
}
}
public void userActiveSession() {
if (!userSessionDlgVisable) {
userSessionDlgVisable = true;
RequestContext.getCurrentInstance().execute("sessionExipreDlg.show()");
}
}
The dialog (sessionExipreDlg) called the redirect instead of using navigation handler to get new scope and refresh the page.
public void userInactiveRedirect() {
FacesContext fc = FacesContext.getCurrentInstance();
userIdleMsgVisable = false;
userSessionDlgVisable = false;
sessionUser = null;
HttpServletRequest request = (HttpServletRequest) fc.getExternalContext().getRequest();
JsfUtil.findBean("homeController", HomeController.class).clearCurrentValues();
try {
fc.getExternalContext().redirect(JsfUtil.getApplicationPath(request, false, null));
} catch (IOException ex) {
BeanUtil.severe(ex.getLocalizedMessage());
}
}
I am currently creating a primfaces portlet. From my view.xhtml when I am calling Submit method in my bean class. I want to redirect the view base on input.
Below is my code snippet of Submit method:
Submit(){
try {
FacesContext.getCurrentInstance().getExternalContext().redirect("/views/Success.xhtml");
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
It simply adds to the local host URL as http://localhost:8081/views/Success.xhtml. I guess I am missing something critical probably. Should implement some render phase method if yes how do i go about it so that it created a render url for that page.
Why aren't you using normal JSF navigation for this? No need to bother with portlet URLs in that case, because it will be handled by the JSF bridge for you.
public String submit() {
// do stuff;
return "/views/Success";
}
You can omit the .xhtml extension.
You can create the url using the following code
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
PortletRequest portletRequest = (PortletRequest) externalContext
.getRequest();
ThemeDisplay themeDisplay = (ThemeDisplay) req.getAttribute(WebKeys.THEME_DISPLAY);
PortletURL url = PortletURLFactoryUtil.create(req,
PortalUtil.getPortletId(req),
themeDisplay.getLayout().getPlid(),
PortletRequest.ACTION_PHASE);
url.setParameter("_facesViewIdRender", "/views/Success.xhtml");
url.setWindowState(WindowState.NORMAL);
url.setPortletMode(PortletMode.VIEW);
FacesContext.getCurrentInstance().getExternalContext().redirect(url.toString());
In my ASP .NET MVC 2 - application, there are several controllers, that need the session state. However, one of my controllers in some cases runs very long and the client should be able to stop it.
Here is the long running controller:
[SessionExpireFilter]
[NoAsyncTimeout]
public void ComputeAsync(...) //needs the session
{
}
public ActionResult ComputeCompleted(...)
{
}
This is the controller to stop the request:
public ActionResult Stop()
{
...
}
Unfortunately, in ASP .NET MVC 2 concurrent requests are not possible for one and the same user, so my Stop-Request has to wait until the long running operation has completed. Therefore I have tried the trick described in this article and added the following handler to Global.asax.cs:
protected void Application_BeginRequest()
{
if (Request.Url.AbsoluteUri.Contains("Stop") && Request.Cookies["ASP.NET_SessionId"] != null)
{
var session_id = Request.Cookies["ASP.NET_SessionId"].Value;
Request.Cookies.Remove("ASP.NET_SessionId");
...
}
}
This simply removes the session-id from the Stop-Request. At the first glance this works well - the Stop-Request comes through and the operation is stopped. However, after that, it seems that the session of the user with the long running request has been killed.
I use my own SessionExpireFilter in order to recognize session timeouts:
public class SessionExpireFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContext ctx = HttpContext.Current;
// check if session is supported
if (ctx.Session != null)
{
// check if a new session id was generated
if (ctx.Session.IsNewSession)
{
// If it says it is a new session, but an existing cookie exists, then it must
// have timed out
string sessionCookie = ctx.Request.Headers["Cookie"];
if ((null != sessionCookie) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0))
{
filterContext.Result = new JsonResult() { Data = new { success = false, timeout = true }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
}
base.OnActionExecuting(filterContext);
}
}
ctx.Session.IsNewSession is always true after the Stop-Request has been called, but I don't know why. Does anyone know why the session is lost? Is there any mistake in the implementation of the Stop-Controller?
The session is lost because you removed the session cookie. I'm not sure why that seems illogical. Each new page request supplies the cookie to asp.net, and if there is no cookie it generates a new one.
One option you could use to use cookieless sessions, which will add a token to the querystring. All you need to do is generate a new session for each login, or similar.
But this is one of the reasons why session variables are discouraged. Can you change the code to use an in-page variable, or store the variable in a database?