Here is my use cases.
I have a login page which is /public/login.xhtml
. All my other pages are required to log-in before reaching them. They are in /pages/
directory.
I want that :
http://host/myapp/pages/*
it redirects him first to the login page, and then, to the URL he has firstly entered.http://host/myapp/
, it redirects him first to the login page, and then, to /pages/home.xhtml
.http://host/myapp/public/login.xhtml
, it redirects him first to the login page, and then, to /pages/home.xhtml
.http://host/myapp/public/login.xhtml
and is already logged in, it redirects to /pages/home.xhtml
.What is working currently?
With Seam 3 (v3.1.0.Final) and the Security + Faces module, my use case n°1 is automagically working with :
@ViewConfig
public interface PagesConfig {
static enum Pages {
@ViewPattern("/pages/*")
@LoginView("/public/login.xhtml")
@LoggedIn
LOGGED_IN_PAGES,
}
}
My problem is that I don't understand how Seam's working to do that redirection to the "capture view".
With Seam 2, it was easy to understand, in components.xml
we had
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}" />
</event>
<event type="org.jboss.seam.security.loginSuccessful">
<action execute="#{redirect.returnToCapturedView}" />
</event>
So we captured the events notLoggedIn
and loginSuccessful
to handle that with a redirect
component.
In Seam 3, I didn't found that configuration : nothing seems to @Observes LoggedInEvent
, and there is no Redirect
class...
The point n°2 is achieved with that /index.htm
file :
<html><head>
<meta http-equiv="Refresh" content="0; URL=pages/home.xhtml">
</head></html>
But for my point n°3, I've tried solutions which don't fully work.
First I tried that in login.xhtml
:
<f:metadata>
<s:viewAction action="#{loginAction.redirectToHome}" if="#{identity.loggedIn}" immediate="true" />
</f:metadata>
And with or without onPostback="true"
, after I login, I'm still in the login page with that error message (twice) : "Unable to find matching navigation case with from-view-id «/public/login.xhtml» for action «#{identity.login}» with outcome «success».". It's only if I now re-access to http://host/myapp/public/login.xhtml
that my viewAction
redirects me to the home.
I also tried that navigation-rule in faces-config.xml
:
<navigation-rule>
<from-view-id>/public/login.xhtml</from-view-id>
<navigation-case>
<if>#{identity.loggedIn}</if>
<to-view-id>/pages/home.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
But then, my use case n°1 was disabled : every time I logged-in, I was redirected to the home.
Finally, for my point n°4, the s:viewAction
does the job.
So does somebody knows the best practices in order to correctly handle those 4 use cases (which I think are common use cases), especially the point n°3?
Finally here is what I did.
<f:metadata>
<s:viewAction action="#{loginAction.redirectToHome}" immediate="true" />
</f:metadata>
So I removed the if="#{identity.loggedIn}"
in order to call my redirectToHome
method which redirects to
the /pages/home.xhtml
.
@ViewConfig
Here is the loginAction
:
public void redirectToHome() throws IOException {
externalContext.redirect(externalContext.encodeActionURL(externalContext.getRequestContextPath()+"/pages/home.xhtml"));
}
The problem I faced then was when I logged out.
Here is my logout action :
<h:commandLink action="/public/login" actionListener="#{securityAction.logout()}" value="Disconnect" immediate="true" />
And the securityAction.logout()
method :
public void logout() {
identity.logout();
if (!conversation.isTransient()) {
conversation.end();
}
}
The problem is that I was redirected to the login page (thanks to the @ViewConfig I think), but no PreLoginEvent
were thrown, so the Seam LoginListener.observePreLoginEvent
wasn't called, and so my previous URL wasn't put in session. So when I logged in (immediatly after logout), I was stuck on the login page, but was logged in.
Thanks to Brian Leathem and he's previous answer, here is what I did : in my authenticate
method of my BaseAuthenticator
, I called that method after authentication :
private void overrideRedirectToLogin() {
final String PRE_LOGIN_URL = LoginListener.class.getName() + "_PRE_LOGIN_URL";
final ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
final Map<String, Object> sessionMap = externalContext.getSessionMap();
String redirectURL = (String) sessionMap.get(PRE_LOGIN_URL);
if (redirectURL == null) {
final HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
redirectURL = request.getRequestURL().toString();
}
sessionMap.put(PRE_LOGIN_URL, redirectURL.replace("/public/login.xhtml", "/pages/home.xhtml"));
}
With that solution, my previous URL wasn't set in session, but at least, my user is redirected to the home page.