I'm trying to add authentication to a JSF 2 based application. I started up by following this tutorial.
Here are exports from my database:
CREATE TABLE `authentication_groups` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(45) NOT NULL,
`description` varchar(255) default NULL,
`create_time` timestamp NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
INSERT INTO `authentication_groups` VALUES (1,'Guest','Anonymous user','2012-07-18 13:54:34'),(2,'Member','Standart user','2012-07-18 13:54:34'),(3,'Admin','Administrator','2012-07-18 13:54:34');
CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`username` varchar(45) NOT NULL,
`password` varchar(128) NOT NULL,
`first_name` varchar(45) default NULL,
`last_name` varchar(45) default NULL,
`create_time` timestamp NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username_UNIQUE` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `users` VALUES (1,'admin','8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918','Samuil','Yanovski','2012-07-24 06:41:56');
CREATE TABLE `users_authentication_groups_link` (
`id` int(11) NOT NULL auto_increment,
`user_id` int(11) NOT NULL,
`authentication_group_id` int(11) NOT NULL,
`create_time` timestamp NULL default CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `uag_link_users_fk` (`user_id`),
KEY `uag_link_authentication_groups_fk` (`authentication_group_id`),
CONSTRAINT `uag_link_authentication_groups_fk` FOREIGN KEY (`authentication_group_id`) REFERENCES `authentication_groups` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `uag_link_users_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `users_authentication_groups_link` VALUES (1,1,3,'2012-07-24 06:42:25');
SELECT `u`.`username` AS `username`,
`u`.`password` AS `password`,
`g`.`name` AS `name`
FROM ( (`observer`.`users_authentication_groups_link` `ug`
JOIN `observer`.`users` `u`
ON(( `u`.`id` = `ug`.`user_id` )))
JOIN `observer`.`authentication_groups` `g`
ON(( `g`.`id` = `ug`.`authentication_group_id` )));
I have one user with username "admin" and password "admin" (encrypted with SHA-256 and Hex encoding).
I have created a JDBC Connection Pool in my Glassfish Admin Console and it is pinging successfully the database. I've assigned this pool to a JDBC Resource named "jdbc/observer". After that in the Configuration -> server-config -> Security -> Realms I created "ObserverRealm" with this configuration:
Here is my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
<login-config>
<auth-method>FORM</auth-method>
<realm-name>jdbcRealm</realm-name>
<form-login-config>
<form-login-page>/faces/login.xhtml</form-login-page>
<form-error-page>/faces/loginError.xhtml</form-error-page>
</form-login-config>
</login-config>
<security-constraint>
<web-resource-collection>
<web-resource-name>Admin</web-resource-name>
<url-pattern>/faces/private/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>Admin</role-name>
</auth-constraint>
</security-constraint>
</web-app>
and glassfish-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
<security-role-mapping>
<role-name>Admin</role-name>
<principal-name>Admin</principal-name>
<group-name>Admin</group-name>
</security-role-mapping>
<class-loader delegate="true"/>
<jsp-config>
<property name="keepgenerated" value="true">
<description>Keep a copy of the generated servlet class' java code.</description>
</property>
</jsp-config>
</glassfish-web-app>
The login.xml is pretty basic:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
template="/templates/master.xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<ui:define name="content">
<p>Login to access secure pages:</p>
<h:messages />
<h:form id="loginForm">
<h:panelGrid columns="2">
<h:outputLabel for="username" value="Username:" />
<h:inputText id="username" value="#{authBackingBean.username}" />
<h:outputLabel for="password" value="Password:" />
<h:inputSecret id="password" value="#{authBackingBean.password}" />
<h:commandButton id="loginButton" value="Login" action="#{authBackingBean.login}" />
</h:panelGrid>
</h:form>
</ui:define>
</ui:composition>
and the AuthBackingBean:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package yanovski.observer.jsf;
import java.security.Principal;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.bean.ManagedBean;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
/**
*
* @author Intuitiv-06
*/
@ManagedBean(name = "authBackingBean")
@RequestScoped
public class AuthBackingBean {
private static final Logger log = Logger.getLogger(AuthBackingBean.class.getName());
private String username;
private String password;
public String login() {
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
try {
request.login(username, password);
} catch (ServletException e) {
context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Login failed!", null));
return "login";
}
//you can fetch user from database for authenticated principal and do some action
Principal principal = request.getUserPrincipal();
log.info("Authenticated user: " + principal.getName());
if (request.isUserInRole("Admin")) {
return "/admins/admins?faces-redirect=true";
} else {
return "/users/users?faces-redirect=true";
}
}
public String logout() {
String result = "/index?faces-redirect=true";
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
try {
request.logout();
} catch (ServletException e) {
log.log(Level.SEVERE, "Failed to logout user!", e);
result = "/loginError?faces-redirect=true";
}
return result;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Could you tell me what am I doing wrong since I always receive "login failed" error when I try to authenticate - using admin/admin for username/password. Please tell me if you need any other details - I could also provide an archive of the project if anyone wants to give it a look.
Thank you in advance and sorry for the long post. :)
We have experienced similar issue on our Lubuntu server with Glassfish v3 and PostgreSQL database. Make sure you have installed PostgreSQL driver in your Glassfish domain.
copy to:
\glassfish\domains\YOURDOMAIN\lib
a JDBC 4 driver which you can find here:
http://jdbc.postgresql.org/download.html
Must help :)