I have already a login functional screen built using JSF 2.2 and PrimeFaces 5.3. However, I have a problem. Say the user enters username and password but one of them is incorrect. When that happens, I display an error message to the user: "Username and/or password is incorrect". The problem is that at that moment I am storing the session. I ONLY want to store a session once the user successfully is logged in. My Login form looks like this:
<f:metadata>
<f:viewParam name="tipo" value="#{loginTipoController.tipo.tipo}" />
</f:metadata>
<h:form id="login">
<p:focus context="login" />
<p:graphicImage url="img/logog.JPG" width="448" height="119" />
<p>Digite su usuario y password.</p>
<p:outputLabel for="usuario" value="Usuario:" />
<p:inputText id="usuario" value="#{loginController.login.username}" />
<p:outputLabel for="password" value="Password:" />
<p:password id="password" value="#{loginController.login.password}" />
<p:messages autoUpdate="true" for="usuarioPassword" />
<p:commandButton value="Login" action="#{loginController.login()}" />
<p:button value="Regresar" outcome="index.jsf" />
</h:form>
The controller class (loginController), looks like this:
#ManagedBean
#SessionScoped
public class LoginController {
private Login login;
private LoginTipo loginTipo;
private LoginService service;
public Login getLogin() {
return login;
}
public void setLogin(Login login) {
this.login = login;
}
public LoginService getService() {
return service;
}
public void setService(LoginService service) {
this.service = service;
}
#PostConstruct
public void init()
{
login = new Login();
service = new LoginService();
}
public String login()
{
FacesContext facesCont = FacesContext.getCurrentInstance();
loginTipo = facesCont.getApplication().evaluateExpressionGet(facesCont,
"#{loginTipoController.tipo}", LoginTipo.class);
login = service.login(login.getUsername(), login.getPassword(), loginTipo.getTipo());
if(login.getMensaje_id() != 0)
{
FacesContext.getCurrentInstance().addMessage("usuarioPassword",
new FacesMessage(login.getMensaje()));
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.invalidateSession();
return "";
}
else
{
return login.getMensaje();
}
}
//logout method
public void logout()
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.invalidateSession();
try
{
ec.redirect(ec.getRequestContextPath() + "/index.jsf");
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I am not putting the LoginService() class code because it is simply going to the database and checking if the user exists or not. I just want to know what do I need to do so that the session is only stored when the user successfully logs in.
Notice that on the login() method of the controller class, when the user fails to log in, I display an error message. After that, I have the following piece of code:
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.invalidateSession();
I am destroying the session. I want to avoid to have to do that!
I understand that you basically want to clear out the login form on login fail. You could just clear out the bean properties representing form data.
login = new Login();
loginTipo = null;
The bean itself does not necessarily need to be session scoped. It can be request scoped and you could manually put the user object in session. I understand Login represents the user. In that case, once the login is successful, do as follows:
ec.invalidateSession();
ec.getSessionMap().put("login", login);
return "/userhome?faces-redirect=true";
The logged-in user will be available in the session scope as #{login}.
Explicitly invalidating the session right before login is a good security practice to avoid session fixation attacks. Invalidating it on every login fail is indeed unnecessary.
Related
I'm new in JSF. Can i use this way of coding instead of using EL in JSF view? and correct me if there is something wrong in my coding or should i use better way.
#Named
#RequestScoped
public class RegistrationBacking extends Root {
#EJB
private UserManagerLocal userManager;
public String register(){
Map<String, Object> parameterMap = getRequestMap();
User user = new User();
user.setUserName((String) parameterMap.get("userName"));
user.setPassword((String) parameterMap.get("password"));
user.setEmail((String) parameterMap.get("email"));
try{
userManager.registerUser(user);
} catch(UserExistsException ex) {
Logger.getLogger(RegistrationBacking.class.getName()).log(Level.SEVERE, null, ex);
getContext().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, getBundle().getString("loginExist"), ex.getMessage()));
return null;
} catch(Exception ex) {
Logger.getLogger(RegistrationBacking.class.getName()).log(Level.SEVERE, null, ex);
getContext().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, getBundle().getString("loginError"), ex.getMessage()));
return null;
}
return "index";
}
}
No. You're basically manually grabbing the submitted values from the request parameter map instead of binding the input values to the model. You're manually filling the model in the controller's action method. You won't be able to perform JSF-managed Conversion and Bean Validation on those inputs.
The right way is the following:
<h:form>
<h:inputText value="#{registrationBacking.user.userName}" />
<h:inputSecret value="#{registrationBacking.user.password}" />
<h:inputText value="#{registrationBacking.user.email}" />
<h:commandButton value="Register" action="#{registrationBacking.register}" />
</h:form>
And then in the backing bean:
private User user;
#PostConstruct
public void init() {
user = new User();
}
public String register {
try {
// ...
}
}
See also:
Passing a JSF2 managed pojo bean into EJB or putting what is required into a transfer object
Creating master-detail pages for entities, how to link them and which bean scope to choose
JSF Service Layer
I am using a session scoped managed bean for handling login in a Java EE application. After I authenticate the user, the user object is saved in this session bean. However, after I refresh the page, the session bean values are gone.
I was debugging the code and it results that the constructor of the session scoped managed bean is called again on page refresh, therefore initializing the user object with a new user. I guess this is not a normal behavior since it should be preserved on the session shouldn't it?
I am posting some parts of the login managed bean including the parameters and the login method. Basically the enteredEmail and enteredPassword stand for the entered data on the login form. If the authentication succeeds, the loggedIn boolean is turned to true and the logged in user object is stored in the checkedUser variable.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class LoginController implements Serializable {
#EJB
private LoginSessionBean loginSessionBean;
#EJB
private LecturerFacade lecturerFacade;
private Lecturer checkedUser;
private String enteredEmail;
private String enteredPassword;
private boolean loggedIn;
/** Creates a new instance of loginController */
public LoginController() {
loggedIn = false;
checkedUser = new Lecturer();
}
public String login(){
RequestContext context = RequestContext.getCurrentInstance();
FacesMessage msg = null;
this.setCheckedUser(lecturerFacade.findLecturerByEmail(enteredEmail));
if(loginSessionBean.checkPassword(checkedUser, enteredPassword))
{
loggedIn = true;
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Welcome", checkedUser.getFirstName()+ " " + checkedUser.getLastName());
FacesContext.getCurrentInstance().addMessage(null, msg);
context.addCallbackParam("loggedIn", loggedIn);
}
return "Index";
I am also posting the two EJBs that the above managed bean uses. The lecturerFacade retrieves the user object with the entered email, while the loginSessionBean checks the password.
#Stateless
public class LecturerFacade extends AbstractFacade<Lecturer> {
#PersistenceContext(unitName = "EffectinetWebPU")
private EntityManager em;
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
protected EntityManager getEntityManager() {
return em;
}
public LecturerFacade() {
super(Lecturer.class);
}
public Lecturer findLecturerByEmail(String email) {
try {
return (Lecturer) this.getEntityManager().createQuery("SELECT l FROM Lecturer l WHERE l.email = :email").setParameter("email", email).getSingleResult();
} catch (NoResultException e) {
System.err.println("Caught NOResultException: "+ e.getMessage());
return null;
} catch (NonUniqueResultException e) {
System.err.println("Caught NonUniqueResultException: "+ e.getMessage());
return null;
} catch (IllegalStateException e) {
System.err.println("Caught IllegalStateException: "+ e.getMessage());
return null;
}
}
_
#Stateless
public class LoginSessionBean {
// Add business logic below. (Right-click in editor and choose
// "Insert Code > Add Business Method")
#PersistenceContext(unitName = "EffectinetWebPU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public void setEntityManager(EntityManager em) {
this.em = em;
}
public boolean checkPassword(Lecturer user, final String enteredPassword) {
if (user.getPassword().equals(enteredPassword)) {
return true;
} else {
return false;
}
}
}
Please if someone has any suggestion of what is going wrong, please tell me
Im using glassfish 3.1 as application server and Primefaces as JSF library. Also, I have checked and the imported the sessionScoped annotation from the right package and not from javax.enterprise...
Your problem is thus here:
<p:menuitem value="Logout" ... onclick="#{loginController.logout()}"/>
The onclick attribute should represent a JavaScript handler function which is to be executed in the webbrowser when the enduser clicks the element. Something like
onclick="alert('You have clicked this element!')"
The onclick attribute also accepts a ValueExpression, so you can even let JSF/EL autogenerate its value accordingly:
onclick="#{bean.onclickFunction}"
with
public String getOnclickFunction() {
return "alert('You have clicked this element!')";
}
All the EL is thus evaluated when the page is rendered. In your particular case, the logout() method is called everytime the EL is evaluated and thus you're invalidating the session everytime the page is rendered!
You need to bind it to an attribute which takes a MethodExpression like <h:commandLink action>, <h:commandButton action> and in this particular case <p:menuitem action>.
<p:menuitem value="Logout" ... action="#{loginController.logout()}"/>
This can be understood by understanding basic HTML and JavaScript concepts and keeping in mind that JSF ultimately produces HTML/CSS/JS. Open the JSF page in webbrowser, rightclick and View Source to realize it.
Well I managed to solve it today. This was the problem, although I cannot explain why:
I was using Primefaces 3.2 as JSF library so this was the main menu of the index page.
<h:form>
<p:menubar >
<p:menuitem id="registerLink" value="Register" rendered="#{!loginController.loggedIn}" onclick="registerDialog.show()" />
<p:menuitem id="loginLink" value="Login" rendered="#{!loginController.loggedIn}" onclick="loginDialog.show()" />
<p:submenu label="Units" rendered="true">
<p:menuitem id="addNew" value="Add New" onclick="createUnitDialog.show()" />
<p:menuitem id="myUnits" value="My Units" onclick="" />
</p:submenu>
<p:menuitem id="results" value="Results/Statistics" rendered="#{loginController.loggedIn}" onclick=""/>
<p:menuitem id="profile" value="My Profile" rendered="#{loginController.loggedIn}" onclick=""/>
<p:menuitem id="logout" value="Logout" rendered="#{loginController.loggedIn}" onclick="#{loginController.logout()}"/>
</p:menubar>
</h:form>
After setting breakpoints to the whole code I discovered that the logout() method, which is supposed to destroy the managed bean, was called on every page refresh. I don't know why this happened as it should be called when the logout menuitem was clicked.
However, after changing the onclick="#{loginController.logout()} with action="#{loginController.logout()} the problem was solved.
I checked the documentation of Primefaces but nowhere this behavior was explained
I have a JSF page in which I show the details of a given user:
<h:form>
<p>
<h:outputLabel value="User Name" for="userName" />
<h:outputText id="userName" value="#{userController.user.name}" />
</p>
<p>
<h:outputLabel value="Email" for="email" />
<h:outputText id="email" value="#{userController.user.email}" />
</p>
<p>
<h:commandLink value="Edit" action="#{userController.edit(userController.user.id)}" />
</p>
</h:form>
This works fine with the exception of the Edit commandLink. When I click it, I expect userController.edit to be passed the ID of the user that's currently being rendered on the page.
However looks like as userController is a request scoped bean that has a #PostConstruct method that assigns a new User to the user field, always 0 is being passed to the action method as the ID of a new instance of User is null which apparently gets converted to 0.
How can I fix this controller/page, without changing the scope of the controller, to pass the correct ID to the edit action? Here's the code for the controller:
#Model
public class UserController {
#Inject
#UserRepository
private EntityManager entityManager;
#Inject
private UserTransaction tx;
private User user;
public String edit(Long id) {
System.out.println("id = " + id);
// fetch the user with id from the db
return "edit";
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#PostConstruct
private void init() {
user = new User();
}
}
The approach is weird but I can imagine when you need this. (e.g. you don't wand to write to session and flash scope don't work in distributed environment etc.). BTW if you can't use session scope consider using flash scope.
Apart from correctness of the approach, try adding aditional field to the bean:
protected Long currentId;
public Long getCurrentId() {
return currentId == null ? user.getId() : currentId;//or simply return it, I don't know how you play with the user field
}
public void setCurrentId(Long currentId) {
this.currentId = currentId;
}
Remove parameter from edit action and use currentId field, and to your view add param (hidden field will not work here):
<f:viewParam name="id" value="#{userController.currentId}" />
Then in action you can get user id from the previous view simply accessing this.currentId.
And BTW, try load your User from another place, not int #PostConstruct. If you create user in #PostConstruct then when you are in edit method the currentId will be from previous view, and user.id will be from #PostConstruct. So if you don't want to store user in session scope, but use only request scope you have to persist it immediately after creation (e.g. in database). In edit method you have to get it back using currentId.
I have a logout link in my JSF app that invalidates the session to log the user out. It works but it doesn't redirect the user to the logon page. It stays on the same page. If I try to access the same page again it does direct back to the logon. I want this to happen immediately.
logout link:
<h:form>
<h:panelGroup id="loginout">
<h:outputText value="#{todoController.loggedInUser}" />
<h:commandLink value="logout" action="#{todoController.logout}" />
</h:panelGroup>
</h:form>
logout code:
public String logout()
{
System.out.println("testing logout");
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
final HttpServletRequest r = (HttpServletRequest)ec.getRequest();
r.getSession( false ).invalidate();
return "../login.html?faces-redirect=true";
}
This can happen if the outcome is invalid. login.html doesn't seem to be a JSF page, so JSF navigation will simply fail.
You want to use ExternalContext#redirect() instead.
public void logout() throws IOException {
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.invalidateSession();
ec.redirect("../login.html");
}
Note that the above also demonstrates a more JSF-ish way to invalidate the session. Whenever you need to haul the raw javax.servlet.* API from under the JSF hoods, you should always ask yourself twice: "Is there really not a JSF-provided API for this?"
I'm wondering what the current approach is regarding user authentication for a web application making use of JSF 2.0 (and if any components do exist) and Java EE 6 core mechanisms (login/check permissions/logouts) with user information hold in a JPA entity. The Oracle Java EE tutorial is a bit sparse on this (only handles servlets).
This is without making use of a whole other framework, like Spring-Security (acegi), or Seam, but trying to stick hopefully with the new Java EE 6 platform (web profile) if possible.
I suppose you want form based authentication using deployment descriptors and j_security_check.
You can also do this in JSF by just using the same predefinied field names j_username and j_password as demonstrated in the tutorial.
E.g.
<form action="j_security_check" method="post">
<h:outputLabel for="j_username" value="Username" />
<h:inputText id="j_username" />
<br />
<h:outputLabel for="j_password" value="Password" />
<h:inputSecret id="j_password" />
<br />
<h:commandButton value="Login" />
</form>
You could do lazy loading in the User getter to check if the User is already logged in and if not, then check if the Principal is present in the request and if so, then get the User associated with j_username.
package com.stackoverflow.q2206911;
import java.io.IOException;
import java.security.Principal;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#SessionScoped
public class Auth {
private User user; // The JPA entity.
#EJB
private UserService userService;
public User getUser() {
if (user == null) {
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
if (principal != null) {
user = userService.find(principal.getName()); // Find User by j_username.
}
}
return user;
}
}
The User is obviously accessible in JSF EL by #{auth.user}.
To logout do a HttpServletRequest#logout() (and set User to null!). You can get a handle of the HttpServletRequest in JSF by ExternalContext#getRequest(). You can also just invalidate the session altogether.
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "login?faces-redirect=true";
}
For the remnant (defining users, roles and constraints in deployment descriptor and realm), just follow the Java EE 6 tutorial and the servletcontainer documentation the usual way.
Update: you can also use the new Servlet 3.0 HttpServletRequest#login() to do a programmatic login instead of using j_security_check which may not per-se be reachable by a dispatcher in some servletcontainers. In this case you can use a fullworthy JSF form and a bean with username and password properties and a login method which look like this:
<h:form>
<h:outputLabel for="username" value="Username" />
<h:inputText id="username" value="#{auth.username}" required="true" />
<h:message for="username" />
<br />
<h:outputLabel for="password" value="Password" />
<h:inputSecret id="password" value="#{auth.password}" required="true" />
<h:message for="password" />
<br />
<h:commandButton value="Login" action="#{auth.login}" />
<h:messages globalOnly="true" />
</h:form>
And this view scoped managed bean which also remembers the initially requested page:
#ManagedBean
#ViewScoped
public class Auth {
private String username;
private String password;
private String originalURL;
#PostConstruct
public void init() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
originalURL = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
if (originalURL == null) {
originalURL = externalContext.getRequestContextPath() + "/home.xhtml";
} else {
String originalQuery = (String) externalContext.getRequestMap().get(RequestDispatcher.FORWARD_QUERY_STRING);
if (originalQuery != null) {
originalURL += "?" + originalQuery;
}
}
}
#EJB
private UserService userService;
public void login() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
try {
request.login(username, password);
User user = userService.find(username, password);
externalContext.getSessionMap().put("user", user);
externalContext.redirect(originalURL);
} catch (ServletException e) {
// Handle unknown username/password in request.login().
context.addMessage(null, new FacesMessage("Unknown login"));
}
}
public void logout() throws IOException {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
externalContext.invalidateSession();
externalContext.redirect(externalContext.getRequestContextPath() + "/login.xhtml");
}
// Getters/setters for username and password.
}
This way the User is accessible in JSF EL by #{user}.
After searching the Web and trying many different ways, here's what I'd suggest for Java EE 6 authentication:
Set up the security realm:
In my case, I had the users in the database. So I followed this blog post to create a JDBC Realm that could authenticate users based on username and MD5-hashed passwords in my database table:
http://blog.gamatam.com/2009/11/jdbc-realm-setup-with-glassfish-v3.html
Note: the post talks about a user and a group table in the database. I had a User class with a UserType enum attribute mapped via javax.persistence annotations to the database. I configured the realm with the same table for users and groups, using the userType column as the group column and it worked fine.
Use form authentication:
Still following the above blog post, configure your web.xml and sun-web.xml, but instead of using BASIC authentication, use FORM (actually, it doesn't matter which one you use, but I ended up using FORM). Use the standard HTML , not the JSF .
Then use BalusC's tip above on lazy initializing the user information from the database. He suggested doing it in a managed bean getting the principal from the faces context. I used, instead, a stateful session bean to store session information for each user, so I injected the session context:
#Resource
private SessionContext sessionContext;
With the principal, I can check the username and, using the EJB Entity Manager, get the User information from the database and store in my SessionInformation EJB.
Logout:
I also looked around for the best way to logout. The best one that I've found is using a Servlet:
#WebServlet(name = "LogoutServlet", urlPatterns = {"/logout"})
public class LogoutServlet extends HttpServlet {
#Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(false);
// Destroys the session for this user.
if (session != null)
session.invalidate();
// Redirects back to the initial page.
response.sendRedirect(request.getContextPath());
}
}
Although my answer is really late considering the date of the question, I hope this helps other people that end up here from Google, just like I did.
Ciao,
VĂtor Souza
It should be mentioned that it is an option to completely leave authentication issues to the front controller, e.g. an Apache Webserver and evaluate the HttpServletRequest.getRemoteUser() instead, which is the JAVA representation for the REMOTE_USER environment variable. This allows also sophisticated log in designs such as Shibboleth authentication. Filtering Requests to a servlet container through a web server is a good design for production environments, often mod_jk is used to do so.
The issue HttpServletRequest.login does not set authentication state in session has been fixed in 3.0.1. Update glassfish to the latest version and you're done.
Updating is quite straightforward:
glassfishv3/bin/pkg set-authority -P dev.glassfish.org
glassfishv3/bin/pkg image-update