maio 04 2010

Segurança com Spring Security 3.0 utilizando banco de dados em apenas alguns minutos

Category: SpringEdson Gonçalves @ 5:13

Olá, tudo bom? Como vão vocês?
Este artigo é o segundo, de uma série, que falaremos sobre segurança em aplicações Java, continuando com o Spring Security.  Dúvidas e críticas são bem vindas.

Spring Security 3.0 com banco de dados

Aplicações comerciais sempre necessitam de acesso ao banco de dados então, porque seria diferente criar uma segurança sem estar com as informações contidas no banco de dados.

No artigo anterior,   tivemos uma introdução de como usar o Spring Security utilizando as configurações somente no XML. Neste artigo veremos como criar a segurança utilizando o Spring Security e o banco de dados.

O banco de dados

O banco de dados utilizado será o MySQL. Você pode baixar a versão 5.1, utilizada no artigo, aqui.

Preparando o banco de dados do exemplo

Abra o terminal do MySQL com seu usuário e senha ROOT (aquela que você configurou na instalação).

Crie o banco de dados e a tabela executando os seguintes comandos:

create database security;

use security;

CREATE TABLE users

(

username VARCHAR(15) NOT NULL,

password VARCHAR(40),

authority VARCHAR(15),

PRIMARY KEY (username)

);

Insira um usuário assim:

INSERT INTO users  VALUES(‘edson’,’integrator’,’ROLE_ADMIN’);

Figura 1 – A tabela criada no banco de dados security

Figura 1 – A tabela criada no banco de dados security

Baixando e descompactando o driver JDBC

Podemos baixar o MySQL clicando aqui, além da versão atual do driver JDBC, chamado de Connector/J 5.1. Baixe os arquivos, instale o MySQL, caso não o tenha em sua máquina e descompacte o arquivo JAR, do qual iremos precisar, que será:

mysql-connector-java-5.1.10-bin.jar

O projeto

Pegue o projeto criado no artigo anterior, adicione a biblioteca JDBC do MySQL e também a biblioteca JAR do Spring Framework:

org.springframework.jdbc-3.0.0.RELEASE.jar

Figura 2 – Bibliotecas adicionadas ao projeto nas properties

Figura 2 – Bibliotecas adicionadas ao projeto nas properties

O arquivo de configurações do Spring

No arquivo de configurações do Spring, altere como mostrado na Listagem 1 a seguir:

Listagem 1 – 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" >

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

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

</http>

<authentication-manager>

<authentication-provider>

<jdbc-user-service data-source-ref="dataSource"

users-by-username-query="SELECT username, password, 'true' as enable FROM users WHERE username=?"

authorities-by-username-query="SELECT username, authority FROM users WHERE username=?"

/>

</authentication-provider>

</authentication-manager>

<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >

<beans:property name="url" value="jdbc:mysql://localhost:3306/security" />

<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />

<beans:property name="username" value="root" />

<beans:property name="password" value="integrator" />

</beans:bean>

</beans:beans>

A mudança feita no arquivo applicationContext.xml, visto na Listagem 1, com relação ao artigo anterior, está na forma como vamos acessar o usuário, senha e seu role. Alteramos o antigo elemento <user-service /> por <jdbc-user-service />.

Em <jdbc-user-service />, temos TRÊS atributos importantes: users-by-username-query e authorities-by-username-query e data-source-ref. O primeiro atributo possui uma query que deve buscar três informações: username, password e um valor booleano chamado enable. A tabela criada no banco de dados, vista na Figura 1, possui três campos (username, password e authority), mas não possui enable. Portanto, a query deve simular este campo, informando que todos os cadastrados na tabela estão habilitados:

SELECT username, password, ‘true’ as enable FROM users WHERE username=?

Observe também que há uma condição WHERE na cláusula SQL, para que o usuário seja filtrado no login e para que o Spring Security se encarregue do resto, analisando se o usuário transmitido e sua senha são similares ao existente no banco de dados.

Além de verificar se o usuário existe, o Spring Security precisa saber se ele possui autorização para acessar determinado local. O role, no caso, foi adicionado no terceiro campo da tabela users, chamado de authority. O atributo authorities-by-username-query, portanto, recebe uma query onde existe o usuário e seu role, sempre filtrando pelo usuário:

SELECT username, authority FROM users WHERE username=?

Atenção: A query utilizada para o spring security precisa retornar os seguintes nomes de colunas: username, password, enable e authority.

No atributo data-source-ref indicamos o data source necessário para se conectar ao banco de dados.

A conexão é feita pelo elemento <beans/> no qual a classe utilizada é a org.springframework.jdbc.datasource.DriverManagerDataSource, responsável por fazer a conexão com o banco de dados.

Logout, acesso negado e criptografia da senha

Depois de concluído o acesso à área restrita, temos que efetuar o logout quando não interessa mais estar naquela área.

Também precisamos definir o que será apresentado aos usuários cujo não possuem acesso a uma determinada área.

Logout

Para fazer o logout de uma área restrita, utilizamos o padrão para fazer isto é: /j_spring_security_logout. Sua utilização será no arquivo /admin/index.jsp, como por exemplo a adição de um link, como na Listagem 2.

Listagem 2 – Adição do link para efetuar logout da área restrita

<html>

...

<body>

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

<hr />

Faça logout <a href="../j_spring_security_logout">clicando aqui</a>.

</body>

</html>

Página com a mensagem de acesso negado

Caso tenhamos um usuário cadastrado válido, que não possui permissão de acesso a uma determinada área, vemos uma página padrão, como de costume, exibida pelo servidor.

Figura 3 – Acesso negado a um usuário que não tem permissão a esta área

Figura 3 – Acesso negado a um usuário que não tem permissão a esta área

Para modificar esta página, podemos criar uma página que será exibida em seu lugar, informando o usuário da falta de permissão de acesso.

Sua configuração depois será no elemento <http />, do arquivo applicationContext.xml, com o atributo access-denied-page. Abaixo vemos a página negado.jsp como sendo a responsável por exibir uma mensagem personalizada caso o usuário entre em uma área não permitida.

<http auto-config="true" access-denied-page="/negado.jsp">

...

</http>

Criptografia de senha

O padrão para as senhas é de texto plano, como já sabem. Entretanto, o Spring Security permite ler informações criptografadas. Digamos que, na tabela de usuários, tenhamos as senhas criptografadas em MD5. Teríamos que informar isso nas configurações do Spring Security da seguinte forma:

<authentication-manager>
<authentication-provider>
<password-encoder hash="md5" />
…
</authentication-provider>
</authentication-manager>

Nota: No MySQL podemos adicionar uma string em MD5 simplesmente utilizando a função de mesmo nome:

INSERT INTO users VALUES(‘usuario’, MD5(‘senha’),’ROLE_…’)

Tags: , , , ,

16 Responses to “Segurança com Spring Security 3.0 utilizando banco de dados em apenas alguns minutos”

  1. camilo lopes says:

    opa! Edson,

    Parabens excelente post, bem didatico :).

    abracos,

  2. Crislaine says:

    Parabés pelo artigo Edson!

    Tenho uma dúvida, gostaria de saber se ao invés de utilizar esse datasource para a configuração de acesso ao banco eu posso utilizar as informações contidas no persistence.xml da minha aplicação, pois minha aplicação utiliza JPA + Spring pra acesso ao banco. Ou se só é possível usando o jdbc mesmo.

  3. Pablo Borges says:

    Edson, parabéns pelo artigo!

    só uma dúvida como eu faço para traduzir as mensagens do spring security p/ pt_BR.

    tentei usar dessa forma

    tentei substituir o messages por messages_pt_Br.

    mas não funciona.

    obrigado

  4. Marcelo R Oliveira says:

    Edson, muito bom esses dois artigos sobre Spring Security. Parabéns!

    No meu caso, estou tendo dificuldade na integração do Spring Security com JSF, especificamente na parte de login e logout.

    Acho que caberia um outro artigo exemplificando como fazer essa integração com a página de login e logout.

  5. Edson Gonçalves says:

    @Marcelo,

    Vou criar tal artigo, assim que postar os atuais que estão sendo escritos no momento. Mas adianto que não é difícil e, com toda certeza, na Internet devem haver exemplos explicando. Acredito que, mesmo sem um tutorial bem detalhado, com o que aprendeu por aqui, é possível entender.
    Bons Códigos!

  6. Dener Santana says:

    Parabés pelo artigo Edson! Ficou muito bom

    Tenho uma dúvida, gostaria de saber se ao invés de utilizar esse datasource para a configuração de acesso ao banco eu posso utilizar as informações contidas no persistence.xml da minha aplicação, pois minha aplicação utiliza JPA + Hibernate pra acesso ao banco. Ou se só é possível usando o jdbc mesmo. [2]

  7. Germanno says:

    To com um problema fiz os tutoriais tudo certo, o primeiro deu tudo certo ja o segundo aparece uma msg de erro!
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.security.authentication.ProviderManager#0’: Cannot create inner bean ‘(inner bean)’ of type [org.springframework.security.config.authentication.AuthenticationManagerFactoryBean] while setting bean property ‘parent’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘(inner bean)’: FactoryBean threw exception on object creation; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.security.authenticationManager’: Cannot resolve reference to bean ‘org.springframework.security.authentication.dao.DaoAuthenticationProvider#0’ while setting bean property ‘providers’ with key [0]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.security.authentication.dao.DaoAuthenticationProvider#0’: Cannot resolve reference to bean ‘org.springframework.security.provisioning.JdbcUserDetailsManager#0’ while setting bean property ‘userDetailsService’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.security.provisioning.JdbcUserDetailsManager#0’: Cannot resolve reference to bean ‘dataSource’ while setting bean property ‘dataSource’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSource’ defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
    PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property ‘driverClassName’ threw exception; nested exception is java.lang.IllegalStateException: Could not load JDBC driver class [com.mysql.jdbc.Driver]

    Alguem tem ideia do que seja?

  8. Germanno says:

    Ja consegui! era só os .jar que não estavam sendo carregados direito. Valeu Parabens pelo Tutorial

  9. Luiz says:

    Oi Edson,

    Criei um form para fazer upload de imagem para uma tabela MySql, mas ao enviar, retorna o erro Comando ‘INSERT’ negado para o usuário… isso acontece apenas no campod e upload da imagem, os demias campos funcionam. POde me dar uma dica ? Obrigado.

  10. Edson Gonçalves says:

    @Luiz,

    Está enviando para um campo blob?

  11. Andréia says:

    Olá!
    Parabéns pela postagem.. perfeitaaa 😀
    Me ajudou pra caramba!

    Abraços

  12. erick willian says:

    excelente, foi de grande valia estava desenvolvendo um projeto que utiliza spring security porém não tinha nenhuma explicação mais detalhada, SHow, Parabéns!!

Leave a Reply