Following the very simple example line, as in the “login example with Rails“, now we’ll see a very simple login example with Java Server Faces.
I’m writing this sequence of short howto posts because in the forums I read one of the recurrent question from the beginners is “how to implement a login with X”, so, let’s go to the JSF login example.
Of course there are many ways to implement a login in a JSF aplication, you can use JAAS, you can use a Servlet Filter, but this one I think is one of the best approaches, of course, in a real application, I usually combine it with some AOP and annotations, but AOP and annotations are out of the scope of this tutorial …
First, write a web.xml for your application, with the faces servlet in it …
<?xml version="1.0" encoding="UTF-8"?> <web-app 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_2_5.xsd" version="2.5"> <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> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.jsf</url-pattern> </servlet-mapping> <error-page> <exception-type>java.lang.SecurityException</exception-type> <location>/login.jsf</location> </error-page> </web-app>
In this web.xml we are using the servlets 2.5 specification, and I’m using JSF 1.2, for this example you can use other versions without problem.
The servlet container is configured to show a login page if at any time the application throws a java.lang.SecurityException, this is a important point for the example.
And now a backing bean for the application, here is where we’ll validate the user’s login, in this example there is no need for database access, but in a real application you’ll search this data in your user repository (database, ldap, …)
package br.com.urubatan.jsfjpasec; public class Login { private boolean loginOk; private String userName; private String password; public boolean isLoginOk() { return loginOk; } 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; } public String validateLogin(){ if(userName!=null && password!=null && !userName.equalsIgnoreCase(password)){ loginOk = true; return "secpage"; }else return "login"; } }
It is a very simple backing bean, with only 3 properties:
name and password for the user, and a property telling the application that a user has already logged in.
probably this last property will be replaced by a list of groups filled by the validateLogin method …
Now a bean with some static data for the example:
package br.com.urubatan.jsfjpasec; import java.util.List; import java.util.ArrayList; public class SomeData { private List<String> data = new ArrayList<String>(); private List<String> securedData = new ArrayList<String>(); private boolean loginOk; public SomeData() { for(int i=0;i<10;i++){ data.add("Simple data " + i); securedData.add("Secure data " + i); } } public void setLoginOk(boolean loginOk) { this.loginOk = loginOk; } public List<String> getSecuredData() { if(!loginOk) throw new SecurityException(); return securedData; } public List<String> getData() { return data; } }
This one has only the getters for two properties, and in the secureData property, if there is no logged in user, the application throws a java.lang.SecurityException, this will redirect the user to the login page.
Now some XML tricks in the faces-config.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <?xml version='1.0' encoding='UTF-8'?> <faces-config 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-facesconfig_1_2.xsd" version="1.2"> <managed-bean> <managed-bean-name>login</managed-bean-name> <managed-bean-class>br.com.urubatan.jsfjpasec.Login</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>mdata</managed-bean-name> <managed-bean-class>br.com.urubatan.jsfjpasec.SomeData</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>loginOk</property-name> <property-class>java.lang.Boolean</property-class> <value>#{login.loginOk}</value> </managed-property> </managed-bean> <navigation-rule> <from-view-id>/login.jsp</from-view-id> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/login.jsp</to-view-id> <redirect/> </navigation-case> <navigation-case> <from-outcome>secpage</from-outcome> <to-view-id>/secureView.jsp</to-view-id> <redirect/> </navigation-case> </navigation-rule> </faces-config> |
In this file we have two navigation cases, if “login” is returned from a backing bean, it shows the login page, if “secpage” is returned, the secureView.jsp page is shown …
The first lines are used to declare the backing beans, pay attention to the line 15, where we are reading the property “loginOk” from the login bean.
The login bean is session scoped, and the mdata is request scoped.
With this written we have all the needed logic for this application, the only missing part is the “view”, or the JSP files …
So, let’s write them …
login.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <html> <head><title>System Login</title></head> <body> <f:view> <h:form> <h:panelGrid columns="2"> <h:outputLabel value="User Name" for="un"/> <h:inputText id="un" value="#{login.userName}"/> <h:outputLabel value="Password" for="pw"/> <h:inputText id="pw" value="#{login.password}"/> </h:panelGrid> <h:commandButton value="Login" action="#{login.validateLogin}"/> </h:form> </f:view> </body> </html>
This is only a simple JSF page with two fields and a commandButton …
dataView.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <html> <head><title>Unsecured Data Page</title></head> <body> <f:view> <h:dataTable value="#{mdata.data}" var="v"> <h:column> <f:facet name="header"> <h:outputText value="Data List"/> </f:facet> <h:outputText value="#{v}"/> </h:column> </h:dataTable> <h:panelGrid columns="3"> <h:outputLink value="dataView.jsf"> <h:outputText value="Data that every one can access"/> </h:outputLink> <h:outputLink value="secureView.jsf"> <h:outputText value="Data that you can view after login"/> </h:outputLink> <h:outputLink value="login.jsf"> <h:outputText value="Login"/> </h:outputLink> </h:panelGrid> </f:view> </body> </html>
This is simple page with a dataTable rendering the “data” property from “mdata” bean and two links.
secureView.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <html> <head><title>Secured Data Page</title></head> <body> <f:view> <h:dataTable value="#{mdata.securedData}" var="v"> <h:column> <f:facet name="header"> <h:outputText value="Data List"/> </f:facet> <h:outputText value="#{v}"/> </h:column> </h:dataTable> <h:panelGrid columns="3"> <h:outputLink value="dataView.jsf"> <h:outputText value="Data that every one can access"/> </h:outputLink> <h:outputLink value="secureView.jsf"> <h:outputText value="Data that you can view after login"/> </h:outputLink> <h:outputLink value="login.jsf"> <h:outputText value="Login"/> </h:outputLink> </h:panelGrid> </f:view> </body> </html>
And this is almost a copy from the previous page, but now reading the “secureData” property from “mdata” bean.
And just to avoid the “404″ error when running the application, an “index.jsp” with a link to “dataView.jsf”
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Entry Page</title></head> <body>Entry Page, this could redirect to the JSF Index, but for now, click here: <a href="dataView.jsf">JSF Index</a> </body> </html>
That is all folks!
to run this application you need the following jar files: jstl.jar, jsf-api.jar e jsf-impl.jar
They can be found in the JSF-RI distribution, and are already present in containers with JSF support.
And now, some questions:
If you enjoyed this post, make sure you subscribe to my RSS feed!
This is just a little step by step on how to create a simple login with Rails, of course you can use a plugin to do it for you, but this way you can understand what the plugin is doing.
Let’s start creating a new Rails application:
$rails applicationNameThen we create a controller with a view for the login:
$script/generate controller login loginThe code for the login_controller.rb will be the following:
class LoginController < ApplicationController def index render :action => 'login' end def login end def do_login username = params[:username] password = params[:password] if username.nil? || password.nil? || username==password redirect_to :action => "login" flash[:notice] = 'Unknown user or invalid password' else session["user_id"] = username redirect_to :controller => "secure", :action => "index" end end end
Basically the controller has to implement only the do_login method, this is the one that will authenticate the user, in this example it only validates if the user filled the username and password fields, but later you can search the database for it.
The login view will have the following code:
<% form_tag :action => 'do_login' do %> <table class="loginForm" align="center"><tbody> <tr> <td>Login</td> <td><input name="username" type="text" /></td> </tr> <tr> <td>Password</td> <td><input name="password" type="password" /></td> </tr> <tr> <td colspan="2"><%= submit_tag "Login" %></td> </tr> </tbody></table> <table><% end %>
It is a simple view, with only two fields and since we do not have a user model I did not used the rails form helpers …
Ok, perfect until now, but how do I use it?
let’s change the application.rb file, the code will be the following:
class ApplicationController < ActionController::Base # Pick a unique cookie name to distinguish our session data from others' session :session_key => '_untitled6_session_id' before_filter :authorize protected # Override in controller classes that should require authentication def secure? false end private def authorize if secure? && session["user_id"].nil? session["return_to"] = request.request_uri redirect_to :controller => "login", :action => "login" return false end end end
The code above adds a before filter to every method of every controller in your application (since all controllers extends ApplicationController), this method will intercept the calls and if the controller is “secure” and the user did not authenticated yet, the user will be redirected to the login page, otherwise the filter will do nothing. To secure a controller you just need to override the “secure?” method and return true.
Now let’s create a simple controller that will need authentication …
$script/generate controller secure indexThis line above creates a new controller with only an index action.
Now let’s edit the secure_controller.rb file
class SegureController < ApplicationController def index end protected def secure? true end end
Overriding the “secure?” method to return true we tell the application that all actions in this controller need authentication.
But if we and only a few actions to need authentication? Than we can use some thing like the code bellow!
protected def secure? ["secureMethod","anotherSecureMethod"].include?(action_name) end
This way only the “secureMethod” and the “anotherSecureMethod” will need authentication.
Every thing ready, now we have a login implemented in a rails application …
Try starting the server (ruby script/server from the application’s directory) and access http://localhost:3000/secure and you will see the login screen instead of the index.rhtml contents.
Of course we can improve this example in many ways, for example:
And you, how would you improve the example above?
Do you see any problem in this example?
If you enjoyed this post, make sure you subscribe to my RSS feed!