JSF navigation redirect to previous page - jsf

Once the user successful login to the system, the system will redirect the user to the homepage. Now my problem is, if the user clicks on the view account page without login to the system the system will redirect user to the login page. if user login to the system now the system will redirect the user to the homepage, in this case any method can redirect user to the previous page that is view account page instead of homepage?
i tried using session
String url = (String)session.getAttribute("url");
if(url != null)
response.sendRedirect(url);
else
response.sendRedirect("homepage.faces");
i put this code under public void doBtnAction(){} if the user login successful then redirect to the url. But i got this error
java.lang.IllegalStateException: Cannot forward after response has been committed
com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:322)
com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:130)
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:87)
com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:200)
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:117)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:198)

This exception is caused because you called response.sendRedirect() and didn't block JSF from rendering the normal response. You need to inform JSF that it doesn't need to handle normal response by adding
FacesContext.getCurrentInstance().responseComplete();
to the action method.
Or, even better, just don't get the HttpServletResponse from under the JSF's hoods, but instead do:
FacesContext.getCurrentInstance().getExternalContext().redirect(url);
This will automatically invoke responseComplete() and it also keeps your code clean from unnecessary Servlet API stuff. Also see the ExternalContext API.

Not sure, but try doing this through ExternalContext facilities:
Something like this:
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.redirect(externalContext.encodeResourceURL(externalContext.getRequestContextPath()+getUrl()));

You haven't described any framework facilities you use for authentication so I assume that you perform authentication in own code. Probably, the simplest approach would be to save initial destination in session before redirecting to the login page, and then after successful login you can check this attribute and redirect to right place.
I particular for JSF you can do this in action method that processes login.
Actually, I'd suggest to use some framework for authentication, for example Spring Security, because it does such things out of box, and it will be much easier to extend your security system in case you need some additional features, though it needs some additional configuration

Logic described (login-redirect) should be implemented through Filter mechanics. Also you can try standard jsf navigation rules(if it can fit you case). And if you still want to send redirect to a custom url and don't want to use Filters, do it in your servlet in render phase, not in your jsf bean.

java.lang.IllegalStateException: Cannot forward after response has been committed
The exception is fairly clear:
The response has been committed.
You cannot forward after the response has been committed.
What doesn't make sense?

You're too late and that is giving you the exception. Do it in a phaselistener before the render response phase.

Placing the below syntax in web.xml should work:
<context-param>
<param-name>com.sun.faces.writeStateAtFormEnd</param-name>
<param-value>false</param-value>
</context-param>

Related

JSF redirect doesn't stop page rendering

We're using JSF in a very simple way. All we're doing is implementing tags that contain a little Java code.
I have implemented a "security" tag that sends a 302 redirect back to the login page whenever the user isn't logged in:
// make them log in
ctx.getExternalContext().redirect("login.xhtml");
ctx.responseComplete();
The trouble is that the redirect() method doesn't stop the rest of the page being rendered. Tags that are further down the page are getting executed. This is a problem because non-logged-in users could see things they shouldn't if they had their browser ignore redirects.
How do I get responseComplete() to do what I thought it was supposed to do?
Its always better to implement the login related logic in a servlet filter, like below:
Implement a filter for the URL patterns that you want to secure
In the filter, check if the user is logged in (may be by just checking if Username/UserId is present in user session)
If the user is not logged in, redirect the user to a HTML based login page.
If the user is logged in, let the user access the resources.
There are a lot of ways (may be better than this) to implement this, but this is the most basic one.
Maybe you could use a flag to verify if the user is logged in.
Then, you can use the render=#{managedBean.logged} property in the tags you don't want to render.
This is just a workaround... can't really help too much with that amount of information you gave.
Try it!
ctx.getExternalContext().dispatch("login.xhtml");
ctx.responseComplete();

socialauth - can you use same session across a redirect?

I'm trying to use socialauth to login with google, facebook et al (I'll assume google here) and have a question about how it works. I'm using JSF 2 without Seam. The basic idea is that you:
make a few API calls indicating that you want to login with google.
make another API call which returns a URL for google.
supply a result URL which will be used by google to redirect back to your site.
redirect to the google URL.
then google will either immediately redirect back to your site or first ask for login details.
My confusion is about linking together the data from the outbound and inbound sides. In the getting started page (linked above) they suggest this:
Outbound
SocialAuthManager manager = new SocialAuthManager();
String successUrl = "http://my.domain.com/socialauthd/successAction.xhtml";
String url = manager.getAuthenticationUrl(id, successUrl);
// Store in session
session.setAttribute("authManager", manager);
Inbound
// get the auth provider manager from session
SocialAuthManager manager = (SocialAuthManager)session.getAttribute("authManager");
The problem I have is that I don't see how this can work, and it doesn't in testing. They suggest storing a reference to an instance of SocialAuthManager in the session, however when the request is received from google a new session is created. It doesn't have the JSESSIONID cookie and so isn't part of the session that sent the request to google in the first place.
To work around this I got a unique per-request id from the socialauth api (openid.assoc_handle - it's sent as a query param), put it in a concurrentHashMap in an app scoped bean, and retrieve the reference in a preRenderView listener in the completion page (successUrl - badly named in the example as it is called either way).
This all seems like a lot of hassle for something that isn't included in the documentation. I've tried this with #RequestScoped CDI beans, although I usually use CODI #ViewAccessScoped. With CODI I've tried adding the windowId to the success URL, and also adding the JSESSIONID cookie to the redirect, but neither approaches work. I don't think the bean scope is relevant but the more information the better.
I could dive into the spring, seam and struts examples but for a pure EE 6 developer it's a lot of overhead, and with a better understanding of this issue I can produce a simple, working, JSF only example which I will make available to the socialauth team for use on google code.
Am I missing something obvious or does this just have to be complicated? and if so why did they document an approach that simply doesn't work?
Edit: I think that the successUrl may be named more appropriately than I thought, because in testing with Yahoo I realise that you won't be redirected back to your own site unless correct login details are provided. I expect this is the same for all providers. I have added some comments regarding this solution to the socialauth site, and also to an issue I logged about this problem (neither of which have received any response from anyone involved in the socialauth project).
Include the jsessionid path parameter in the callback URL.
String successUrl = "http://my.domain.com/socialauthd/successAction.xhtml"
+ ";jsessionid=" + session.getId();
Note that this is not specific to JSF API, but to Servlet API (chapter 7.1.3, URL rewriting).

j_security_check not redirecting to welcome page - successful login event listener?

For ages I've been puzzled about why after login I sometimes don't directed to the application welcome page. I've finally figured it out (years after everyone else):
I login successfully via j_security_check and go to the welcome page
wait for session timeout
click on h:link which sends a GET request
because it's a GET and not a POST my custom ViewExpiredException
handler doesn't kick in
container security redirects to the login page because the session
has timed out. Because of the session timeout+container security the
get request (from h:link) isn't seen by the application, in either a phase listener
or filter.
I successfully login again
j_security_check redirects me to the page which triggered the
authentication, in this case the target of the GET request.
The last bit I'd not understood, I assumed it would always go to the welcome page.
My problem is that my current design requires that after login I always show the welcome page. The welcome page has a preRenderView event which sets up some context information in a session scoped bean after login and increments a few counters etc...
This context information is required by backing bean code for other pages, and presently if I don't go through the welcome page first there'll be an exception.
In terms of fixing it I've looked at the following options:
Ideally there'd be an #PostLogin method that could be called, which would cleanly solve all my problems. I use JSF (Mojarra) with Myfaces CODI but I don't see anything which does what I want.
I could add some more code to my filter, but I need to persist some data (i.e. login count), it doesn't look like a nice option. Maybe I'm wrong.
I make all the preRenderView methods of potential targets of j_security_check (pages called with GET) handle the case where they are called directly from j_seecurity_check. I can see this being what I have to do but it seems like a lot of hassle.
Write a Server Authentication Module for glassfish to override j_security_check behavior.
How is this normally handled? I've started hitting this problem after moving to GETs for simple navigation cases after years of abusing POSTs, and the custom exception handler doesn't work. If anyone has any guidance on this issue I'd appreciate it, at least I know what's going on now. Hopefully I've missed something obvious!
Thanks
O/S
Ideally there'd be an #PostLogin method that could be called, which would cleanly solve all my problems. I use JSF (Mojarra) with Myfaces CODI but I don't see anything which does what I want.
There is indeed no such thing.
I could add some more code to my filter, but I need to persist some data (i.e. login count), it doesn't look like a nice option. Maybe I'm wrong.
That would indeed be the "easiest" way. Basically:
UserPrincipal user = request.getUserPrincipal();
HttpSession session = request.getSession();
if (user != null && session.getAttribute("user") == null) {
session.setAttribute("user", user);
// First-time login. You can do your intercepting thing here.
response.sendRedirect(request.getContextPath() + "/welcome.xhtml");
}
I make all the preRenderView methods of potential targets of j_security_check (pages called with GET) handle the case where they are called directly from j_seecurity_check. I can see this being what I have to do but it seems like a lot of hassle.
That's not DRY.
Write a Server Authentication Module for glassfish to override j_security_check behavior.
Can't answer that as I've never done that.

JSF authentication logout

I know that this question seems to be answered by a lot of other threads, but I can't find a solution with JSF 2.0 with Glassfish 3.0.1 for logout an user.
I tried either with a BASIC authentication and FORM authentication using j_security_check as action.
But for the logout method I can't find any of them that works.
I tried using a servlet with session.invalidate(), i used a managed bean tring to invalide the session, but nothing happened. I also tried with j_security_logout without success.
Does someone know what I can do for logout an user?
Calling session.invalidate() should work.
Your problem is probably that you used the browser back button to view a restricted page to test if logout really succeeded, but that page was actually served from the browser cache instead of straight from the webserver over a real HTTP connection.
In that case, you need to instruct the webbrowser to not cache the restricted pages. This way the browser will always request the page straight from the webserver. You can do this with help of a Filter. You can find an example in this question: Prevent user from seeing previously visited secured page after logout

Best way to implement user login page in JSF

What's best way to implement login page in JSF 1.2? If session is timeout, use will be redirected to login in page. i found 2 ways to do it on internet.
use PhaseListener ->
http://www.jsfcentral.com/listings/A92000?link
use filter ->
What is the correct way to implement login with redirect using JSF 2.0?.
also i want user go through an agreement page right after login page. user need to click on "agree" button to continue to use the system.
can anyone tell me which option is better or if there is a better way to implement this.
thank you,
What's best way to implement login page in JSF 1.2?
Just use a JSP/Facelet page with a <h:form> and appropriate input elements and a backing bean which puts the user in an injected session scoped bean.
If session is timeout, use will be redirected to login in page. i found 2 ways to do it on internet.
Definitely use a Filter. A PhaseListener has too much overhead for this simple use case. You're not interested in filtering/modifying the JSF lifecycle, but just on filtering/modifying HTTP requests.
also i want user go through an agreement page right after login page. user need to click on "agree" button to continue to use the system.
Well, then just develop such a page? If you stucks, press Ask Question on the right top with the actual problem described in detail.
Filter is always better is meant for this, and more suitable also.
Now for your requirement ,
On successful Login
- Add managedBean holding user info to session
On acceptance of terms
- update this bean's field
and check for both the condition for rest of the pages.
no check on login page
check of bean for term page
In JSF2 it can be done using System events. In your template page put
< f:event type="preRenderView" listener="#{loginBean.checkLogin}"/>
and in loginBean ( make it session scoped ) you can do like this
public void checkLogin(ComponentSystemEvent event) {
// loggedIn is a boolean variable when sucessfully logged in make it true and at logout make it false.
if (!loggedIn) {
FacesContext context = FacesContext.getCurrentInstance();
ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler)
context.getApplication().getNavigationHandler();
handler.performNavigation("login");
}
}

Resources