abr 25 2010

Segurança passo a passo com Spring Security 3.0

Category: SpringEdson Gonçalves @ 6:48

Olá, tudo bom? Como vão vocês?

Este artigo é o primeiro de uma série que falaremos sobre segurança em aplicações Java, começando com a primeira parte do artigo sobre Spring Security.  Dúvidas e críticas são bem vindas.

Segurança de dados através do Spring Security

A segurança de áreas restritas em aplicações Web escritas em Java não é uma tarefa das mais triviais.

Sabendo que não era simples criar áreas de segurança, em 2003 surge o Acegi Security System for Spring, um framework extremamente configurável  e complexo. Comum na época, suas configurações eram baseadas em XML e demorava um tempo para que o desenvolvedor o dominasse completamente.

O projeto Acegi evoluiu e, em 2007, foi incorporado aos projetos do Spring Framework, sendo renomeado para Spring Security.

A versão 2.0 foi lançada em 2008 e em 2010 a versão 3.0, a que iremos utilizar neste artigo.

Download do Framework

Para trabalhar com o Spring Security, você deve realizar o download no endereço http://www.springsource.org/download. No momento em que este artigo é escrito, a versão utilizada é a Spring Security 3.0.2. Clique em Download.

Figura 1 – Local de download do Spring Security 3.0.2

Figura 1 – Local de download do Spring Security 3.0.2

Baixe a versão spring-security-3.0.2.RELEASE.zip. Ao baixar, descompacte o arquivo extraindo todos os JARs existentes no diretório lib.

Figura 2 – Download do Spring Security

Figura 2 – Download do Spring Security

Também será preciso baixar o Spring Framework. No momento em que este artigo é escrito, a versão utilizada é a Spring Framework 3.0.2.

Figura 3 – Download do Spring Framework

Figura 3 – Download do Spring Framework

Como o Spring Security trabalha

Da mesma forma que faríamos se estivéssemos utilizando JAAS, o Spring Security trabalha a segurança através de declarações baseadas em papéis (roles). Seja em XML ou Anotações, o Spring Security  não necessita chamar método algum para realizar uma autenticação ou autorização.

Através dos roles definidos, podemos informar ao aplicativo em questão, ao qual está sendo assegurada  uma área, quais recursos podem ser acessados ou restringidos a uma determinada pessoa que acessou a área restrita.

Preparando o ambiente de trabalho

Para este artigo, iremos utilizar a IDE da Spring Source, divisão da VMware,  criada sobre a plataforma Eclipse, chamada de SpringSource Tools Suite.

Para baixar o SpringSource Tools Suite, clique aqui, preencha o formulário e faça o Download. Como a ferramenta possui uma opção de instalador, use-a como facilitador se desejar. Na própria página onde baixar o arquivo, haverá a explicação da instalação em cada plataforma, em Installation Instructions.

Criando um projeto

No SpringSource Tools Suite, clique no menu File>New>Dynamic Web Project. Na caixa de diálogo New Dynamic Web Project, digite ProjSpringSecurity (ou o nome que desejar) em Project name.

O SpringSource Tools Suite possui embutido um servidor de aplicações Java Web baseado no Apache Tomcat 6, só que com algumas modificações. Entretanto, vamos utilizar o Tomcat, que pode ser adicionado como mostro neste artigo.

Confirme a criação do projeto no botão Finish.

Adicionando as bibliotecas ao Projeto

Com o direito do mouse sobre o projeto, na view Project Explorer, vá até Properties. Na  caixa de diálogo das propriedades do projeto, vá até Java EE Module Dependencies. Clique em Add External JARs e adicione os seguintes arquivos:

  1. org.springframework.aop-3.0.2.RELEASE.jar
  2. org.springframework.asm-3.0.2.RELEASE.jar
  3. org.springframework.beans-3.0.2.RELEASE.jar
  4. org.springframework.context-3.0.2.RELEASE.jar
  5. org.springframework.core-3.0.2.RELEASE.jar
  6. org.springframework.expression-3.0.2.RELEASE.jar
  7. org.springframework.transaction-3.0.2.RELEASE.jar
  8. org.springframework.web-3.0.2.RELEASE.jar
  9. spring-security-config-3.0.2.RELEASE.jar
  10. spring-security-core-3.0.2.RELEASE.jar
  11. spring-security-taglibs-3.0.2.RELEASE.jar
  12. spring-security-web-3.0.2.RELEASE.jar
  13. commons-logging-1.1.1.jar

Note que o 13º item é um JAR que não pertence a família do Spring Framework. Você pode baixar o arquivo compactado, contendo a biblioteca commons-logging-1.1.1.jar, clicando aqui.

Figura 4 – Arquivos JARs adicionados ao projeto

Figura 4 – Arquivos JARs adicionados ao projeto

Uma aplicação simples com Spring Security

Para exemplificar como funciona o Spring Security, vamos criar uma aplicação simples com apenas uma área segura. Esta área segura será representada dentro de um diretório, chamado admin.

Teremos duas páginas index.jsp: uma na raiz do aplicativo e outra dentro do diretório admin, como mostra a Figura 5.

Figura 5 – As páginas do Projeto

Figura 5 – As páginas do Projeto

As páginas JSP

A página index.jsp, existente dentro do diretório admin exibe apenas uma mensagem simples, como mostra a Figura 6, com apenas HTML.

O conteúdo é mostrado na Listagem 1.

Figura 6 – Página index.jsp exibida após logar na área admin

Figura 6 – Página index.jsp exibida após logar na área admin

Listagem 1 – O conteúdo HTML da página /admin/index.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01

Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Usuário Logado</title>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

</head>

<body>

<h2>Parabéns, você está logado!</h2>

</body>

</html>

A página index.jsp encontrada na raiz exibe apenas um link que o leva até a área administrativa. Sua aparência é idêntica a Figura 7.

O conteúdo da página index.jsp encontrada na raiz da aplicação é mostrado na Listagem 2.

Figura 7 – Página index.jsp exibida quando acessada a aplicação

Figura 7 – Página index.jsp exibida quando acessada a aplicação

Listagem 2 – O conteúdo HTML da página /index.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<title>Página Inicial</title>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

</head>

<body>

<h2>Uma aplicação simples com Spring Security</h2>

<hr />

<a href="admin">Clique aqui para acessar a área administrativa</a>

</body>

</html>

Configurando o web.xml

Para que o Spring Security, assim como o Spring, precisamos configurar o web.xml. O Spring Security utiliza um filtro HTTP para interceptar as URLs acessadas e verificar as permissões de acesso.

A Listagem 3 exibe a configuração do arquivo web.xml.

Listagem 3 – O arquivo web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

id="WebApp_ID" version="2.5">

<display-name>ProjSpringSecurity</display-name>

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<filter>

<filter-name>springSecurityFilterChain</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

</filter>

<filter-mapping>

<filter-name>springSecurityFilterChain</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

</web-app>

Para configurar o Spring Security, utilizamos o filtro org.springframework.web.filter.DelegatingFilterProxy, devidamente configurado na Listagem 3. O filtro está sendo aplicado em todo o aplicativo, podendo ser visto no elemento <url-pattern />.

Atenção: Um detalhe importante que precisa ser notado é o nome do filtro, colocado no elemento <filter-name/>.  Não o altere, pois o Spring já espera pelo nome springSecurityFilterChain.

Configurando o applicationContext.xml

O Spring Security será configurado no arquivo applicationContext.xml. Este arquivo deverá ser criado dentro do diretório WEB-INF, com o conteúdo mostrado na Listagem 4.

Listagem 4 – O arquivo applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:beans="http://www.springframework.org/schema/beans"

xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

<http auto-config="true">

<intercept-url pattern="/admin/**" access="ROLE_ADMIN" />

</http>

<authentication-manager>

<authentication-provider>

<user-service>

<user name="edson" password="integrator" authorities="ROLE_ADMIN" />

</user-service>

</authentication-provider>

</authentication-manager>

</beans:beans>

No Spring Security, as configurações de autenticação e autorização estão sendo feitas no arquivo de contexto padrão do Spring (applicationContext.xml). Para que este arquivo seja lido, adicionamos no web.xml o elemento <listener />, contendo o listener org.springframework.web.context.ContextLoaderListener.

O listener do Spring faz com que as configurações sejam carregadas na inicialização da aplicação Web.

Ao ser carregado pelo listener, o arquivo da Listagem 4 declara os usuários e suas regras de acesso ao aplicativo.

O controle de acesso é feito pelo elemento <http />, do applicationContext.xml. Este controle é definido no sub-elemento <intercept-url />. O atributo pattern, de <intercept-url />, informa, através de uma expressão, em qual local o filtro deve agir, bem como define a sua regra de acesso, através do atributo access.

Para que possamos definir que qualquer elemento dentro do diretório admin fique acessível somente para os usuários do role ROLE_ADMIN, adicionamos uma expressão comum no Apache Ant.

Caso tenhamos mais de um sub-elemento <intercept-url />, teremos sua interpretação sendo feita por ordem de definição, sendo que, a primeira que atender a regra, será chamada. Na prática, isto significa que, se houver /admin/relatorios/** e /admin/**, o primeiro caso deverá ser lido primeiro, portanto será o primeiro a ser adicionado na ordem em  applicationContext.xml.

O atributo auto-config, com o valor true, indica a configuração automática da aplicação para utilizar um formulário de login. O JSP do formulário é gerado automaticamente pelo Spring Security neste caso. A Figura 8 exibe o formulário gerado pelo Spring Security.

Figura 8 – Formulário de login gerado automaticamente pelo Spring Security

Figura 8 – Formulário de login gerado automaticamente pelo Spring Security

Com o atributo <authentication-manager>, gerenciamos os usuários e seus respectivos roles  que darão permissão ao diretório especificado anteriormente, em <intercept-url />, pelo filtro.

Para facilitar a compreensão do exemplo, adicionamos apenas um usuário, através de <user/>, informando o nome de usuário, a senha e o seu papel de acesso.

Ao logar no aplicativo, o Spring analisará qual role é permitido no diretório e quem possui  tal permissão.

Personalizando o formulário de acesso a área restrita

É interessante ter uma geração automática de formulário no Spring Security, ajuda a testar a codificação, com certeza. Mas não é agradável ao aplicativo como um todo, pois sempre precisamos criar o formulário com as características gerais desenvolvidas no layout das páginas.

Criando a página personalizada de login

Para isso, o Spring Security nos fornece a personalização do formulário.  A Listagem 5 exibe o conteúdo da página /login.jsp que ficará na raiz do seu aplicativo, junto com index.jsp.

Listagem 5 – A página login.jsp

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">

<title>Área Restrita</title>

</head>

<body>

<h2>Área Restrita</h2>

<hr />

<%

if(request.getParameter("error") != null){

if (request.getParameter("error").equals("invalido")){

%>

<p>

<span style="color:red">

Usuário ou Senha inválidos

</span>

</p>

<%

} //fim do if equals

}//fim do if null

%>

<form action="j_spring_security_check" method="post">

Usuário: <input name="j_username" type="text" value="${not empty login_error ? SPRING_SECURITY_LAST_USERNAME : ''}" />

<br />

Senha: <input type="password" name="j_password"><br />

<input type="submit" value="Efetuar Login"><br />

<a href="index.jsp">Retornar para a Página Inicial</a>

</form>

</body>

</html>

Para que o formulário funcione com o Spring Security, as regras mais básicas são:

  • O atributo action deve apontar para j_spring_security_check;
  • O atributo name da caixa de entrada de  texto, do nome de usuário, deve ser j_username;
  • O atributo name da caixa de entrada de senha deve ser j_ password.

Estes princípios básicos farão com que seu formulário funcione com o Spring Security. Entretanto, precisamos mostrar uma mensagem de erro, caso o usuário não tenha colocado as informações necessárias ou, as mesmas não sejam compatíveis com o registrado no sistema para permitir a entrada na área restrita.

É exatamente o papel do if(request.getParameter(“error”) e if (request.getParameter(“error”).equals(“invalido”)),  na página login.jsp, personalizado, com um parâmetro que iremos transmitir, através do Spring Security, caso ocorra um erro na permissão.

A página personalizada pode ser vista na Figura 9.

Figura 9 – Formulário de acesso a área restrita personalizado

Figura 9 – Formulário de acesso a área restrita personalizado

Alterando o arquivo applicationContext.xml

Por fim, mas não menos importante, precisamos alterar o arquivo applicationContext.xml para que o Spring Security passe a trabalhar com a página de login personalizada que criamos. A seguir você tem o trecho, contendo o elemento <form-login/>, que deve ser inserido por entre o elemento <http />:

<form-login login-page=“/login.jsp” authentication-failure-url=“/login.jsp?error=invalido”/>

O elemento <form-login/> é de simples compreensão, pois temos atributos que descrevem bem sua função:

login-page: A página personalizada com o formulário de substituição do padrão existente no framework

authentication-failure-url: URL de retorno caso ocorra um erro. Note que adicionamos,  após “?” , error=invalido. Isto demonstra, claramente, que não é uma regra fixa o que pode ser transmitido, caso ocorra um erro, na entrada de uma determinada área restrita.

Figura 10 – Resultado final do projeto com todos os arquivos criados

Figura 10 – Resultado final do projeto com todos os arquivos criados

No próximo artigo sobre Spring Security

Embora tenhamos conhecido os elementos básicos de utilização do Spring Security, restou colocar o acesso ao banco de dados para fazer uma autenticação verdadeira, como ocorre em sistemas.

No próximo artigo veremos como fazer para trabalhar com Spring Security e o acesso ao banco de dados, com um exemplo completo, passo a passo.

Até o próximo artigo pessoALL.

Tags: , , , , , , ,

28 Responses to “Segurança passo a passo com Spring Security 3.0”

  1. camilo lopes says:

    opa! Edson,

    Realmente excelente post, e bem didático parabens!!

    abracos,

  2. Edson Gonçalves says:

    @Camilo,
    Obrigado amigo!

  3. marcondes says:

    Muito bom! Aguardaremos!

  4. Tweets that mention Segurança de dados através do Spring Security -- Topsy.com says:

    […] This post was mentioned on Twitter by valdir m. s. junior. valdir m. s. junior said: Segurança de dados através do Spring Security http://bit.ly/bePZo1 […]

  5. Rafael says:

    Oi, gostaria de saber se pode me ajudar com o seguinte Problema:

    No meu sistema o usuário e o administrador têm áreas restritas diferentes, gostaria que após o login eles fosse redirecionados para as suas respectivas áreas.

  6. Edson Gonçalves says:

    @Rafael,

    É possível, claro. Mas não é um assunto de rápida explicação. Eu diria que é interessante pesquisar mais sobre o modo de trabalho do Spring Security, para obter todas as características que precisa, de modo que o filtro gerado em sua aplicação direcione seu usuário ao local onde é necessário acessar.

  7. juniorsatanas says:

    Esse desgraçado em 1 post, falou o que eu le em 600 páginas em ingles… filha da mae do cara[…].. bom pra po[…]…

    SUPER PARABÉNS..

    juniorsatanas

  8. juniorsatanas says:

    Vou comprar todos os livros desse cara.. agora sei que vale .. cada real..

    juniorsatanas

  9. Edson Gonçalves says:

    @juniorsatanas

    Desculpe a edição, mas infelizmente não dava para deixar os palavrões, ok?
    Valeu por comentar.

  10. Rauel says:

    Olá Edson…
    você não falou
    “# O atributo name da caixa de entrada de texto, do nome de usuário, deve ser j_username;
    # O atributo name da caixa de entrada de senha deve ser j_ password.”

    No código de exemplo os inputs estão sem os names..
    eu dei um Ctrl c +ctrl v e depois que vi q estava errado.

    Gostei do artigo,
    Pena que o Spring Security 3.0 com banco de dados não deu certo quando eu configuro o
    ..form-login login-page=”/login.jsp” authentication-failure-url=”/login.jsp?error=invalido”/>
    você pode conferir se o código do artigo está correto e depois me da um toque?

    Vlws

  11. Edson Gonçalves says:

    @Raquel,

    Muito bem observado o erro. Ao colar do meu editor de textos para o blog, o editor do WordPress comeu algumas tags e adicionou outras. Na minha edição, não reparei que havia removido estas informações. Obrigado por corrigir o erro e está corrigido no blog também.
    Quanto ao seu erro do Spring Security com banco de dados, verifique se não deixou algo para trás ou que erro é apresentado na saída do seu servidor de aplicações.

    Grato,

  12. Caio Souza says:

    Mto bom o artigo…Parabéns!!!
    Eu fiz o exemplo funcionou certinho…mas tenho uma dúvida
    Edson vc poderia me explicar que código eu devo acrescentar ai no exemplo para que o usuario após efetuar o logout não consiga voltar para a pagina onde estava logado atraves back (voltar do navegador)?
    Pq eu logo no sistema, ai faço o logout….mas qndo eu clico em voltar pelo navegador ele volta para a pagina logada, onde o correto seria direcionar novamente para a pagina de login correto?
    Obrigado

  13. Edson Gonçalves says:

    @Caio,

    O “back” ou “voltar” do navegador é um evento client. Portanto, o código que deve incluir não operaria no servidor.
    Você pode usar desde meta tags até JavaScript para forçar o navegador e ler o estado atual da sessão.
    Entretanto, saiba que, mesmo o usuário retornando no navegador, para um estado anterior, ao começar a clicar nos links, ele será jogado ao login e senha,pois o navegador não possui mais a sessão aberta.

  14. andii.brunetta says:

    Olá Edson! Primeiramente parabéns por suas postagens, muito boas… direto caio no seu blog quando estou procurando alguma coisa de JEE hehehe
    A algumas semanas comecei a mexer com autenticação no meu projeto, segui todos os seus passos e funcionou perfeitamente! Porém, no meu caso eu preciso ter um selectOneMenu de filiais no form de login.. tipo assim, quando o usuario vai acessar o sistema, ele precisa selecionar a qual filial ele pertente(ele pode pertencer a mais de uma filial) … aí vem o detalhe, eu tenho um bean que implementa o Authentication do Spring Security e que faz a autenticação, até aí funciona… mas eu preciso pegar o codigo da filial selecionada nesse bean, mas ele só me retorna null! vc sabe se é possivel fazer isso nem que seja de outra forma? Grata pela atenção! 🙂

  15. Edson Gonçalves says:

    @andii,

    Você pode usar o JSF PhaseListener para armazenar esta filial para que não se perca após a autenticação.

  16. andii.brunetta says:

    Olá Edson… depois de muito ler a doc do Spring Security, acabei descobrindo um metodo na classe UsernamePasswordAuthenticationFilter, aí eu implemento ela e faço a minha autenticação. Obrigada! Agora o meu problema está sendo mandar uma mensagem para a tag h:message … quando faço FacesContext.addMessage(…) dá nullpointer… :S

  17. Fábio says:

    Olá.

    Gostei muito do artigo, estava tendo muitas dificuldades para implementar um sistema de login no meu projeto usando interceptors do struts 2, na verdade eu consegui implementar, mas não definir diferentes níveis de usuário.

    Dai me deparo co Spring que resolve tudo de uma forma muito mais prática. Muito Obrigado mesmo!

    Estou indo agora ler a segunda parte do Tutorial.

  18. Kleiton says:

    Parabéns, Edson, muito bom esse seu passo a passo com Spring Security

  19. Lucas says:

    Parabéns pelo post Edson, bastante esclarecedor pra quem está começando a usar o spring-security… bem tenho uma dúvida sobre como usar os facelets do spring-security.. quero saber como renderizar ou não um menu qualquer de acordo com o tipo de usuário que está fazendo login, Ao mais seu post ja me ajudou bastante!

    Parabéns

  20. Max says:

    Perfeito o Artigo Edson.
    Resta-me algumas pequenas dúvidas.

    1º)
    Você utilizou a IDE Spring Source, mas, posso continuar utilizando o NetBeans? Interfere em algo?

    2º)

    Trabalho em uma empresa que tem o sistema desktop e está sendo reestruturado para uma versão web utilizando jsf. Há alguma restrição na implementação?

    No mais, parabéns e muito sucesso.
    Estarei sempre acompanhando seu blog e aprendendo novas soluções.

  21. Edson Gonçalves says:

    @Max,

    Pode usar qualquer ferramenta, seja o Eclipse ou NetBeans. O importante é seguir as regras de configurações do framework que não terá problemas e nem restrições na implementação.

    Bons códigos!

  22. Edson Gonçalves says:

    @Lucas,

    Caso ainda não tenha resolvido, deixarei aqui em aberto para que algum leitor possa responder, caso tenham tempo.

  23. Thommas says:

    Pessoal, só uma ressalva, eu fiz o exemplo do jeito que está ai,porém, me apareceu um erro de NoClassDefFoundError e consegui resolver adicionando o “jar org.springframework.jdbc-3.1.2.RELEASE” que vem no download .zip do Spring Framework.

  24. Giovani says:

    Parabéns, muito bom…
    O conteúdo do seu site é ótimo

  25. Sidnei says:

    Você me ajudou muito!!! Obrigado. Site save favoritos!

Leave a Reply