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’);
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
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.
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_…’)
maio 5th, 2010 14:13
opa! Edson,
Parabens excelente post, bem didatico :).
abracos,
maio 13th, 2010 23:39
[…] Spring Security + BD […]
julho 7th, 2010 11:22
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.
julho 16th, 2010 14:34
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
julho 19th, 2010 16:31
@Pablo,
Veja aqui como fazer:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity-single.html#localization
Bons códigos!
julho 20th, 2010 12:28
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.
julho 20th, 2010 15:01
@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!
agosto 12th, 2010 7:30
[…] Spring Security + BD […]
agosto 31st, 2010 21:54
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]
outubro 20th, 2010 17:36
[…] Spring Security + BD […]
outubro 21st, 2010 2:30
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?
outubro 21st, 2010 12:00
Ja consegui! era só os .jar que não estavam sendo carregados direito. Valeu Parabens pelo Tutorial
novembro 7th, 2010 0:56
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.
novembro 10th, 2010 22:39
@Luiz,
Está enviando para um campo blob?
dezembro 20th, 2010 12:33
Olá!
Parabéns pela postagem.. perfeitaaa 😀
Me ajudou pra caramba!
Abraços
dezembro 13th, 2012 19:00
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!!