I´m new at JSF programming and got a problem with my login/session which gets lost after the login.
I want to implement an easy login where a user can type in username and password. So I wrote a LoginController:
#ManagedBean
#SessionScoped
public class LoginController extends AbstractController{
#PostConstruct
public void initialiseSession() {
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
private String username = "null";
private String password;
private boolean loggedIn = false;
#Inject
private EmployeeService employeeService;
public static final String employeeSessionKey = "user";
public LoginController() {
}
public String login() {
//check username and password and if true redirect to "/"
}
My login.xhtml looks like:
<h:form id="loginForm">
<h:outputLabel style="font-size:24px" value="Bitte melden Sie sich an!"/>
<p:panelGrid columns="2">
<p:outputLabel id="userOutput" for="userInput" value="Benutzername"/>
<p:inputText id="userInput" value="#{loginController.username}"></p:inputText>
<p:outputLabel id="passwordOutput" for="passwordInput" value="Passwort"/>
<p:inputText id="passwordInput" type="password" value="#{loginController.password}"></p:inputText>
<h:outputText value="Logindaten merken?" id="outputRememberLogin">
<p:selectBooleanCheckbox id="loginCheckbox">
</p:selectBooleanCheckbox>
<p:spacer width="10" id="loginFormLittleSpacer"></p:spacer>
</h:outputText>
<p:commandButton id="loginButton" value="Anmelden" action="#{loginController.login()}" ajax="false" >
</p:commandButton>
</p:panelGrid>
</h:form>
So when I login the redirect works. But when I go to another .xhtml page the session gets lost.
To test this, I put
<p:outputLabel value="#{loginController.username}"/>
on my pages. After the login, the username becomes "null".
I´m going crazy on this problem.
Any ideas?
thanks before.
Your LoginController bean looks like its annotations are okay and the code looks like it should work. However, there's a couple other things that you may want to check. Some of this may be obvious but your question is missing a few details so I’m not sure what level of experience you may have and where to start in this answer. Therefore, I’m starting from the beginning (almost)…
1. Domain Name Configuration
To use sessions, you must use a qualified domain name. Sending a request to an IP address will not allow sessions to work since client browsers only send session information to a fully qualified domain name (http://example.com/). If you’re calling your web app with an IP (such as ‘http://127.0.0.1:8080/MyApp’), the session data will never be sent to your web app and you will have a new session created with each request. Make sure you’re using a fully qualified domain name and path with each request to your application, for example ‘http://localhost:8080/MyApp’.
2. Application Configuration
Check that your web application’s <session-config> configuration is setup correctly. The default config should allow your code to work without having to add anything specific, so if you didn’t add anything, don’t worry about this. However, you may want to make sure there’s nothing that may be preventing the sessions from being reused.
Session cookies should be enabled (<tracking-mode>COOKIE</tracking-mode>)
Timeout should be long enough to not expire before the second request (<session-timeout>60</session-timeout>)
The cookie path should be correctly set for your use (<path>/</path>)
The following is a common session config that I use…
<session-config>
<session-timeout>60</session-timeout>
<tracking-mode>COOKIE</tracking-mode>
<cookie-config>
<path>/</path>
<http-only>true</http-only>
<secure>false</secure>
</cookie-config>
</session-config>
3. Session Cookie Tracking on the Client
If the above points don’t resolve the issue, you can really start diving into the session tracking by monitoring the request and response traffic between your app and the client browser. The session info is passed back and forth by a cookie (or query string parameter if cookies are disabled) named ‘JSESSIONID’. Its value will be the ID of the unique session and must be the same for each request to ensure that your web application tracks the same session. The below highlights some of the things to look for…
Make sure the ‘JSESSIONID’ session cookie is sent to the client browser correctly and is being sent back with each subsequent request. You can do this with Chrome or Safari's web developer tools (under the 'Network' tab) or with a separate utility, such as Wireshark.
Make sure the 'JSESSIONID' cookie exists in the response from the first request… this will let you know that your web app is at least creating the session and response cookie.
Make sure the second request from the client browser is passing the 'JSESSIONID' cookie back... this is the only way your web app knows which existing session to use.
Make sure the 'JESSIONID' cookie has a path of '/' (which may display as 'N/A' in the browser) or the path of your web app ('/MyApp', for example)… the client browser will only send cookies to the domain(s) and path associated with each. For example, if your login page is 'http://example.com/MyApp/login', the 'JSESSIONID' cookie may have a path of '/MyApp' (by default), which will not be returned if the following request is made to 'http://example.com/' (without the '/MyApp' path). The default path is the name of your web app (‘/MyApp’) and can be changed using the <path> config item indicated above.
4. Session Management within the Bean (Additional Information)
In your initialiseSession() method, the session object returned by FacesContext.getCurrentInstance().getExternalContext().getSession(true); isn't being saved and this line is essentially doing nothing. The #SessionScoped annotation will have already created a session and added the ‘JSESSIONID’ cookie to the response before initialiseSession() is called. Therefore, the only reason to call getSession(true) is if you want to save the session to a private object within the bean, which would be like this...
#ManagedBean
#SessionScoped
public class LoginController extends AbstractController{
// Create a global, private member for storing the session data...
private HttpSession session;
#PostConstruct
public void initialiseSession() {
// Assign the session to the global member…
session = FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
…
Again, the call to the getSession(true) isn’t necessary in your example since the SessionScoped bean will have already created the session. The above code is only necessary if you intended to update or use the session object, for example add an attribute or modify a setting.
In summary
Double-check the above points 1 – 3. My assumption would be that the ‘JSESSIONID’ cookie isn’t being sent back in subsequent requests. If you can confirm that the ‘JSESSIONID’ cookie (with the same value) is being included in each request then the problem isn't related to the session and may be related to the code in your login() method.
i used cookies for repair this kind of problems, in my application. inside Faceutils doesn't work before servlet.
Related
Since PrimeFaces push is going to be discontinued, I started to migrate to OmniFaces push. For notification purposes it's all working as expected, but I have a chat on my application where I'm missing two things in OmniFaces:
1) Change the user of the socket on demand. I require this for private conversations. On PrimeFaces I create a channel for each conversation based on the logged users id, and I pass it to the connect method of their socket component. With OmniFaces I tried to place the socket component inside a PrimeFaces output panel, bind the user property to a view scoped bean, update the panel on a ajax request and on the oncomplete event call OmniFaces.Push.open('channelName'), but I can see on the SocketObserver class(from the showcase) that the user has not been updated. As a workaround I created the channel using the logged user id as user, and if someone different from the person that he is talking at the moment sends a message, I ignore it at the JavaScript callback instead of appending it.
2) When I don't specify the user on PushContext.send, I'd like that the message was sent to everyone connected on the channel, similar to "/channel/*" on PrimeFaces. My requirement with this is to update the list off online/offline users off this chat application. A a workaround I could create a separate channel for these notifications, but as the application don't require this to work, I preferred to check here first.
I'll add code if necessary, but first I'd like to check if these concepts are present in OmniFaces push.
Thanks
It was indeed not possible to change the <o:socket user="#{...}"> value while staying in the same JSF view (ajax updates, etc). As per issue 472, it has been improved in OmniFaces 3.2-SNAPSHOT.
Any dynamic change in value of <o:socket user="#{...}"> during any ajax request in the same JSF view will now be reflected in push behavior.
In other words, below construct will now be possible:
<h:form>
<h:selectOneMenu value="#{bean.chat}">
<f:selectItems value="#{bean.chats}" />
<f:ajax render="#form" />
<h:selectOneMenu>
...
<o:socket channel="chat" user="#{bean.chat.id}" />
</h:form>
If you start by using the user attribute on the o:socket like in the example
<o:socket channel="sess" scope="session" user="#{pushTestUser}" />
1) seems possible by using the 'user id' as can be seen in the showcase push test page
From http://showcase.omnifaces.org/push/socket:
#Inject #Push
private PushContext someChannel;
public void sendMessage(Object message, User recipientUser) {
Long recipientUserId = recipientUser.getId();
someChannel.send(message, recipientUserId);
}
In this example the pass the User which is an 'example' object that could be your own or whatever. In the showcase the user is passed on by binding the id to an input field, but that could be done server-side as well. Up to you
2) seems possible with sending messages to the generic channel or even groups as can be seen in http://showcase.omnifaces.org/push/socket.
#Inject #Push
private PushContext someChannel;
public void sendMessage(Object message, Group recipientGroup) {
Collection<Long> recipientUserIds = recipientGroup.getUserIds();
someChannel.send(message, recipientUserIds);
}
The group here is an example Object that could be your own, it could be passed from the 'frontend' or just read in the backend somewhere. It just needs to contain id's of users (can be mapped guids, does not need to be internal user id's) that are subscribed to the channel. All are just example methods.
So both seem possible in my opinion.
our customer doesn't want to have session handling with cookies and it also will cause problems with an Apache/mod_rewrite gateway, so i tried to use
<tracking-mode>URL</tracking-mode>
in our web.xml. That should be all with Glassfish3/Servlet 3.0. However now i get ViewExpiredExceptions when trying to log in(it's not an AJAX request):
<p:commandButton id="submit"
value="${msg['Login.submit.label']}"
action="#{loginBean.login}"
ajax="false"/>
I also tried to save the session on the client side, than i can see the JSESSIONID in the URL but that throws NotSerializableExceptions for my #EJBs. Any ideas? Do i miss something? It used to work fine with the cookies.
UPDATE: LoginBean.login returns "Home.xhtml?faces-redirect=true", expected behaviour when clicking the commandButton: POST on Login.xhtml, my login page, redirect and GET on Home.xhtml.
SECOND UPDATE:
Looks like my action never gets called, i'm directly getting the ViewExpiredException and a HTTP 500 error code.
THIRD UPDATE:
Looks like the HttpSession is always null with tracking mode set to URL, with cookies the HttpSession is correctly created. Shouldn't the FacesServlet create a session and append the JSESSIONID in the URL if there is no session?
ANOTHER UPDATE:
With
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
the session will be created on postback. But than i'm running into
java.io.NotSerializableException
.
The other option is to set restore view compability to true.
Edit your web.xml and add following code and try.
<context-param>
<param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
<param-value>true</param-value>
</context-param>
Updated:
Reference
com.sun.faces.enableRestoreView11Compatibility is a JSF 1.2 setting that tells JSF 1.2 to behave like JSF 1.1.
com.sun.faces.enableRestoreView11Compatibility == true means "do not throw a ViewExpiredException; instead, just create a new view if the old one has expired."
The IBM notes on the JSF 1.1 behaviour say:
This can have adverse behaviors because it is a new view, and items that are usually in the view, such as state, are no longer be there.
The default JSF 1.2 behaviour is defined in the spec as this:
If the request is a postback, call ViewHandler.restoreView(), passing the FacesContext instance for the current request and the view identifier, and returning a UIViewRoot for the restored view. If the return from ViewHandler.restoreView() is null, throw a ViewExpiredException with an appropriate error message. javax.faces.application.ViewExpiredException is a FacesException` that must be thrown to signal to the application that the expected view was not returned for the view identifier. An application may choose to perform some action based on this exception.
To have a ViewExpiredException thrown when the view expires, remove the com.sun.faces.enableRestoreView11Compatibility parameter or set it to false.
The com.sun namespace suggests that the parameter is a Sun/Mojarra and derived implementation-specific setting, so it probably will not work with all JSF implementations.
Fixed by updating Mojarra. My Glassfish 3.1.2.2 came with Mojarra 2.1.6 and this bug:
https://java.net/jira/browse/JAVASERVERFACES-2143
Updated to 2.1.22 and everything works.
I have been experimenting with the substance of this question (JSF / Java EE login without requiring a protected resource).
If I set up a sample application using BASIC authentication, with one public page (/public.xhtml), and one protected page (/protected/private.xhtml), and I have a link from the first page to the second (as shown below), everything works perfectly.
<h:commandButton value="Go Private" action="/protected/private?faces-redirect=true" />
However, if I remove the login-config and replace the above button with:
<h:commandButton value="Go Private" action="#{mybean.login}" />
...and #{mybean.login} looks something like this...
public String login() {
HttpServletRequest request = ...
try {
request.login("known username", "known password");
} catch (Exception e) {
// handle unknown credentials
}
return "/protected/private?faces-redirect=true";
}
In this case, the login succeeds (no exception from request.login()), but the browser shows a "forbidden resource" page.
Can anyone shed any light on the difference between the two scenarios?
The HttpServletRequest#login() programmatic login works only with FORM based authentication configuration. Removing the <login-config> would make it to default to BASIC and thus the login() will never work. The login() basically sets the user in the session, however the BASIC authentication basically checks the Authenticate HTTP request header, not the session.
Put that <login-config> back and set it to FORM if you want to utilize login().
Where it is:
<h:commandButton value="Go Private" action="#{mybean.login}" />
it should be:
<h:commandButton value="Go Private" action="#{mybean.login()}" />
if you want to use the login methode.
But maybe your mybean also has
String private login;
public String getLogin(){ return this.login ;}
and you've loaded the var with the desired response.
In order to create a "Remember me" login in JSF, I am trying to understand how Cookies work. I have created a brand new Web Application using JSF, with this bean that creates a Cookie expiring with the session:
CookieBean class
#ManagedBean
#ViewScoped
public class CookieBean implements Serializable {
public void create() {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.addResponseCookie("MyTestCookie", "Hello Cookie", null);
}
}
and index.xhtml has this body:
<h:form>
<h:commandButton value="Create Cookie!" action="#{cookieBean.create()}" >
<f:ajax render="#form" />
</h:commandButton>
<p></p>
<h:outputText value="Cookie value: #{cookie['MyTestCookie'].value}" />
</h:form>
As a result, when the page first loads, there is no cookie, correctly, because it's the first time the application runs, and no cookie is there.
After clicking the button once, no cookie is displayed. Why? The button invokes the cookieBean#create() method, and the ajax tag should force a revaluation of the outputText component. And this should generate an HttpSerlvetRequest with the cookie... or not? The cookie shows only after I press the button again!.
More surprisingly, when I press the refresh button of the browser, the cookie is not shown, although I'd expect to see it, because the older session is still alive.
It's like if (re)loading the page doesn't send an HttpServletRequest to the server...
The #{cookie} refers to the cookies of the current HTTP request. If you add a new cookie, then it appears only on the HTTP response, but the HTTP request which is associated with this HTTP response does of course not have the cookie yet. It's only present in the subsequent request, depending on the age of the cookie.
You basically need to send a redirect afterwards to make the cookie available during rendering the HTTP response for the HTTP request.
As to the refresh matter, the request was most likely re-executed in the browser cache.
I'm new at Spring Security. I'm using jsf2 with spring security 3. Three questions:
How can I access, from a session managed bean, the user info (name,password,roles) of the user currently logged in?
In order to use it in a view, for example for rendering elements depending on the roles of the user.
How can I know if a user is logged in? In order to show in a view a "login link" if the user is not logged in, or a "logout link" if the user is logged in. Which property of Spring Security do I have to use in my managed bean to store this info and use it in the view?
The "login link" is just a GET request to the URL of the login page. But how can I show "logout link"? Do it have to be a POST request and use "h:commandLink" like this?:
<h:commandLink value="Logout" action="#{request.contextPath}/j_spring_security_logout" />
Or can it be a GET request?:
<h:link value="Logout" outcome="#{request.contextPath}/j_spring_security_logout" />
Thank you very much in advanced.
The object authentication is who save this properties, you can obtain with next line in your managedBean:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
A user is logged if his Authentication is not a instace of AnonymousAuthenticationToken, in your spring-security-context.xml you must define the urls intercepted by Spring.
The first interceptor is not analyzed by Spring. In this case the Authentication object is an instance of AnonymousAuthenticationToken.
The second interceptor is analyzed by Spring and the user is redirected to login page declared in spring-security-context.xml
/* This is a example for to obtain the rol name for example for generate automatic menu */
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String namePrincipalRol = null;
if (auth instanceof AnonymousAuthenticationToken) {
namePrincipalRol = "ROLE_ANONYMOUS";
} else {
namePrincipalRol = auth.getAuthorities().iterator().next().getAuthority();
}
Good question, I am not sure but I think I remember having read that it must be POST, would be interesting to try. I use h:outputLink
Kind regards.