sábado, 9 de agosto de 2008

Login com JSF

A primeira abordagem é uma implementação manual, que nesta versão só verifica se o usuário ja esta logado, mas é possível estende-la facilmente para utilizar grupos.


Primeiro vamos começar com a configuração do web.xml …




<?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>



Como podem ver, eu criei uma aplicação WEB com servlets 2.5, estou utilizando JSF 1.2, mas para este exemplo a versão não faz muita diferença, e configurei o servlet container para que no caso de uma java.lang.SecurityException ele redireccione para a página de login da aplicação.


Depois disto, vamos começar a escrever alguns beans, vou utilizar dois beans simples, um para gerenciar o login:



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";

}

}



É uma classe bastante simples, ele tem 3 propriedades, o nome e senha do usuário, e uma propriedade dizendo que o login esta OK, este é o ponto de extensão para adicionar suporte a grupos mais tarde se quiserem.


E este bean que vai conter todos os dados da aplicação (neste exemplo, dados estáticos), ele tem duas propriedades que possuem dados estáticos apenas para este exemplo, se quiserem utilizar JPA basta misturar com este exemplo ou com um destes.



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("Dados Simples " + i);

securedData.add("Dados Seguros " + 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;

}

}


Como podemos ver, ele possui apenas os getters dos dados, e o método de leitura dos dados seguros, verifica a propriedade loginOk que deve ter sido fornecida pelo bean de login, vamos configurar isto no faces-config.xml a seguir.


Caso o usuário não tenha se identificado ainda, apenas geramos uma SecurityException e o container cuida de mandar isto para a página de login, como foi configurado no web.xml.


Agora um pouco mais de XML, vamos configurar os navigation cases necessários para a aplicação e os relacionamentos entre os beans ja criados, no caso, configurar a propriedade loginOk do bean de dados com a mesma propriedade do bean de login.



<?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>



Os casos de navegação nesta aplicação bastante complexa são utilizados apenas pelo bean de login, no método que valida o login (baita validação, apenas verifica se foram informados um nome e uma senha e se os dois não são iguais, mas este é outro problema :D )


Com isto toda a lógica necessária para a aplicação esta pronta, falta apenas criarmos as páginas utilizadas, então segue o código delas:


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>



Como podemos ver é apenas uma página JSF simples com um formulário e dois campos, chamando o método vaidateLogin de um bean de nome login.


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>



Como podemos ver também é uma página bem simples, com apenas uma dataTable listando os dados da propriedade data de um bean de nome mdata.




<%@ 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>



Esta também é uma página simples, praticamente igual a anterior, mas desta vez, lendo a propriedade securedData do bean mdata, nesta propriedade o bean verifica se o usuário tem acesso, caso contrário gera uma excessão.

por último temos a página index.jsp que apenas possui um link para a página dataView.jsp:



<%@ 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>



com isto temos uma implementação bem simples de um login em uma aplicação JSF, bom para quem esta começando a estudar a tecnologia.


Como melhorias poderíamos mudar a propriedade loginOk para um Map<String,Boolean> tendo como chave os nomes dos possíveis grupos e como valores veradeiro se o usuário pertence aquele grupo.



Outra melhoria interessante seria utilizar um h:inputSecret para a senha em vez de um inputText como estou utilizando no exemplo.


Outra possível melhoria seria utilizar uma combinação de anotações e AOP como esta implementado no Spring-Annotations. (se quiserem posso postar outro dia um exemplo de implementação de segurança com o SA).


Uma outra possível alteração seria utilizar JAAS para a segurança declarativa e deixar isto por conta do container.


Agora algumas perguntas para vocês:



  1. Quais outras possíveis melhorias vocês vêem neste exemplo?

  2. Quais problemas vocês vêem nesta implementação?

  3. Vocês gostariam de outros exemplos deste estilo aqui no blog?


  4. Vocês já estão utilizando JSF?


Acho que era isto, espero que o exemplo seja útil para alguém :D


PS.: para os que forem rodar a aplicação, são necessários apenas os jars: jstl.jar, jsf-api.jar e jsf-impl.jar (se forem utilizar a JSF-RI como eu).



fonte:http://www.urubatan.com.br/implementando-login-com-jsf-exemplo-simples/

Nenhum comentário: