Tutorial: Começando com Spring Security
Esse tutorial vai cobrir um cenário básico onde integra um módulo do framework Spring – Spring Security, utilizando autenticação via banco de dados, em uma aplicação web que já utiliza o Spring.
Como qualquer outro assunto relacionado à Spring, a curva de aprendizado em um pouco grande. Mas como qualquer outro assunto Spring, uma vez que você faz a primeira configuração e aprende, pode usá-la sempre em outras aplicações, ou seja, o famoso Ctrl C + Ctrl V.
Quando comecei a estudar Spring Security no final de dezembro, encontrei esses primeiros passos sugeridos na página do Spring Security.
E se você quiser configurar a parte de segurança na sua aplicação web utilizando Spring Security, segia os seguintes passos:
A primeira coisa que precisa fazer é adicionar os arquivos do framework no classpath da aplicação. Faça o download do Spring Security, e copie os seguintes arquivos da pasta dist; cole-os na pasta lib da sua aplicação:
- spring-security-core-2.0.4.jar
- spring-security-core-tiger-2.0.4.jar
- spring-security-acl-2.0.4.jar
- spring-security-taglibs-2.0.4.jar
Também é necessário fazer o download do Apache Commons Codec: commons-codec-1.3.jar
Vamos começar com a configuração dos XMLs:
Web.xml
Cole o seguinte código no arquivo web.xml. Deve ser inserido logo após ao final da tag /context-param.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<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>
applicationContext-security.xml
Foi feita uma referência ao arquivo applicationContext-security.xml dentro do web.xml. Vamos criá-lo.
Seguindo o tutorial da página do Spring Security, sugiro começar com o XML encontrado no tutorial de exemplo, e incrementá-lo aos poucos. Esse é o arquivo básico:
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Sample namespace-based configuration
-
- $Id: applicationContext-security.xml 3019 2008-05-01 17:51:48Z luke_t $
-->
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
<global-method-security secured-annotations="enabled">
</global-method-security>
<http auto-config="true">
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
<!--
Usernames/Passwords are
rod/koala
dianne/emu
scott/wombat
peter/opal
-->
<authentication-provider>
<password-encoder hash="md5"/>
<user-service>
<user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
<user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
<user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
<user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</beans:beans>
Agora você pode tentar executar a aplicação.
Após tentar executar a sua app, e se a seguinte exception for lançada:
SEVERE: Context initialization failed org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/applicationContext-security.xml]; nested exception is java.lang.NoClassDefFoundError: org/aspectj/lang/Signature at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:420) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:92) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:123) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:423) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:353) at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3764) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4216) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014) at org.apache.catalina.core.StandardHost.start(StandardHost.java:736) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443) at org.apache.catalina.core.StandardService.start(StandardService.java:448) at org.apache.catalina.core.StandardServer.start(StandardServer.java:700) at org.apache.catalina.startup.Catalina.start(Catalina.java:552) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)
Download aspectjrt-1.5.4.jar e o adicione no classpath.
Vamos então fazer algumas mudanças no arquivo applicationContext-security.xml.
Primeira mudança: substitua o bloco de código abaixo
<http auto-config="true">
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
por
<http auto-config="true">
<!-- Don't set any role restrictions on login.jsp -->
<intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<!-- Restrict access to ALL other pages -->
<intercept-url pattern="/**" access="ROLE_USER" />
<!-- Set the login page and what to do if login fails -->
<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" />
</http>
O atributo auto-config basicamente diz para o spring-security configurar as opções padrões sozinho, ou seja, o framework faz tudo automático.
A página login.jsp pode ser acessada por qualquer ROLE.
Restringir acessor à página delogin siginifcaria que ninguém poderia acessá-la. Ficaria muito estranho, posi como pode autenticar um usuário se este ainda não efetuou o login?
Note que a página de login é um jsp, e não uma action. A página de login não precisa ser acessada através de uma action.
Na configuração acima, também foi restringido o acesso a todas as outras página. Apenas os usuários com a ROLE_USER podem acessá-la.
Digamos que tenha mais uma role. Fazer o mapeamento das URLS para as roles é bem fácil. Dentro da tag http, coloque as URLS e as roles assim:
<intercept-url pattern="/admin/*.do" access="ROLE_ADMIN" /> <intercept-url pattern="/manager/*.do" access="ROLE_MANAGER" /> <intercept-url pattern="/**.do" access="ROLE_USER,ROLE_ADMIN, ROLE_MANAGER" />
É claro que você não quer colocar (nem deve) todos os usernames (usuários) e passwords (senhas) e suas respectivas roles no arquivo applicationContext-security.xml. Como então dizemos ao spring-security para obter essas informações do banco de dados?
Coloque o seguinte bloco de código no applicationContext-security.xml (substitua o bloco com os usuários e senhas)
<!-- Configure the authentication provider --> <security:authentication-provider> <security:jdbc-user-service data-source-ref="dataSource" /> </security:authentication-provider>
Para isso é necessário já ter um dataSource criado (não vou abordar esse assunto nesse post).
Agora a pergunta: para isso funcionar, como é que o framework espera que o meu banco de dados se pareça?
A autenticação padrão do spring security requer que a estrutura do banco de dados seja dessa maneira:
CREATE TABLE users
(
username character varying(50) NOT NULL,
"password" character varying(50) NOT NULL,
enabled boolean NOT NULL,
CONSTRAINT users_pkey PRIMARY KEY (username)
);
CREATE TABLE authorities
(
username character varying(50) NOT NULL,
authority character varying(50) NOT NULL,
CONSTRAINT fk_authorities_users FOREIGN KEY (username)
REFERENCES users (username) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE UNIQUE INDEX ix_auth_username
ON authorities
USING btree
(username, authority);
Mas se desejar configurar as queries que são usadas, pode utilizar a sua própria estrutura do banco de dados, só deve tomar cuidado para que os atributos/colunas da sua query “casem” (sejam os mesmos) com os atributos esperados pelo framework.
Por exemplo? você quer apenas ter uma tabela com todas essas informações no banco de dados (ou até uma tabela com nome e colunas diferentes). Vamos ver como fica a configuração do XML:
<jdbc-user-service data-source-ref="dataSource"
authorities-by-username-query="select username,authority from users where username=?"/>
Talvex você ainda também queira configurar outras páginas:
Access Denied: essa página será exibida caso o usuário tente acessar uma página que não pertença a sua role:
<http ... access-denied-page="/accessDenied.jsp">
...
</http>
Default Target URL: essa é a página que o usuário será redirecionado caso seja autenticado com sucesso no login:
<http ... >
...
<form-login ... default-target-url="/home.do"/>
...
</http>
Logout URL: essa é a página que o suusário será redirecionado quando fizer logout:
<http ... >
...
<logout logout-success-url="/home.do"/>
...
</http>
Login Failure URL: essa é a página que o usuário será redirecionado caso a autenticação falhe (login). Geralmente é a própria página de login, com algum parâmetro indicando um erro:
<http ... >
...
<form-login ... authentication-failure-url="/login.jsp?login_error=1"/>
...
</http>
Bem, é isso que você precisa para começar com Spring Security.
Na próxima semana, posto como a página login.jsp do Spring Security se parece.
Bons códigos!
Posts Similares
Comments (35)
Links to this Post
- Tweets that mention Tutorial: Começando com Spring Security | Loiane Groner -- Topsy.com | 18 de January de 2010
- Retrospectiva 2010 | Loiane Groner | 23 de December de 2010















Bom artigo, espero para ver a continuação
security-applicationContext.xml
applicationContext-security.xml
Loiane,
tem um erro no tutorial.
No web xml o nome do arquivo applicationContext-security está errado.
Thanks
Corrigido!
Valeu pela dica!
Estou esperando a continuação do tutorial, estou tenho problemas na implementação de um customAuthenticationManager e CustomAuthenticationProvider
vlw
olá Loiane,
Legal o post, eu so fiquei na duvida se a palavra ROLE de access é required, pq antes quando se usava realm em “role” poderia ser qualquer coisa “gerente” , “analista” e com o spring se fizer isso colocar em access=”Gerente” nao funciona, como contornar isso?
abracos,
Ei Camilo,
Sim, é necessário colocar o prefixo ROLE. Se no banco de dados vc não tiver o prefixo, pode fazer uma customização de classes do framework e concatenar o prefixo com o valor que veio do banco para ficar igual ao arquivo de configuração.
Infelizmente, se não tiver o prefixo, não vai funcionar.
[]‘s
olá loiane,
Pow isso eh muito ruim, pois como dar suporte a sistema legado que nao tem o prefix? até o realm é mais inteligente nesse aspecto, andei pesquisando, mas sem sucesso em solucionar o problema veja: http://forum.springsource.org/showthread.php?t=59296 http://forum.springsource.org/showthread.php?t=60644
vc ja passou por algo ? e como conseguiu fixar, as opcoes nos links acima, nao funcionaram aqui. Acho que falta algo que nao foi comentado no forum .
abracos, tks
@Camilo,
Bem, eu não acho isso um problema na verdade. Isso é muito fácil de resolver no lado servidor com uma concatenação de strings, sem mudar banco de dados nem nada. Sim, já passei por isso e foi assim que resolvi. Tive que implementar a parte de segurança de uma app usando spring security puro.
Você teria que ter a parte básica configurada para a configuração que foi mencionada no forum funcionar.
O framework Spring é um dos melhores que já vi, faz muito sucesso fora do Brasil, por ser um framework bem completo. A documentação não é das melhores, mas compensa. Aqui na empresa só usamos Spring, e implementaram um customização do spring secuirty que é utilizado em todas as aplicações java. E olha que segurança é o que ele mais olham nas app, por se tratar de informações extremamente confidenciais.
Dê uma estudada mais à fundo que vc vai acabar gostando!
olá Loiane,
Realmente concordo com vc, ja li sobre o framework e vi que realmente tem varios recursos que interessantes e bem implementados. já pesquisei de varias formas no google, mas sem sucesso, muitos posts, incompletos ou sem respostas para o mesmo problema que estou passando. E assim nao faz sentindo ele ter ROLE_ como prefix e default, assim os sistemas legados dar essa dor de cabeça, a ideia na minha opiniao era que deveria funcionar desde o sem ROLE_ para o ROLE_. tipo nessa app, em um form JSF, temos lá selectOneItem e ele carrega por exemplo dados que foram inserido no BD por outro usuario como : Gerente, Vendedor etc. e nao poderia carregar no form: ROLE_Gerente. nao faria sentindo isso.
Mas, obrigado pela ajuda, vou ver se consigo remover esse prefix. =/
abracos,
@Camilo,
Na verdade, não vi nenhum empecilho nesse exemplo que citou. Dê uma olhada como customizar o authentication. Você não precisa fazer nenhuma mudança no banco ou no arquivo de configuração, só no lado server.
Por exemplo, a sua configuração vai ficar como ROLE_GERENTE, no banco vai ficar apenas gerente, e na hora de fazer o login, você só vai precisa concatenar a String “ROLE_” com o que veio do banco no server.
Dê uma olhada nessa thread que tem explicando direitinho:
http://forum.springsource.org/showthread.php?t=65079
opa! Loiane,
Pois eh, vi ontem a solucao sem mexer algo no banco ficou assim: SELECT username, concat(‘ROLE_’,authority) FROM users where username = ?
hehe thanks girl
Você também pode configurar o bean RoleVoter para o prefixo de suas permissões.
Lembrando que você pode também criar grupos de usuários, e ao invés de associar o usuários as permissões, você associa as permissões ao grupo, e o(s) grupo(s) ao usuário.
Tudo bem loiane, eu estou com algumas duvidas sobre o como vc deixou os seus beans, vc poderia postar eles ?
Olá Loiane! Legal seu tutorial.
Estou com um probleminha, no footer.jsp coloquei:
<a href="”>Login
Olá
<a href="”>Logout
Mas o rodapé com o “Login” só aparece na Index, nas outras só aparece o rodapé sem o “Login”, sabe que pode ser?
Abraço!
Olá Guevara,
Você está incluindo o seu footer em todas as páginas?
Olá,
Gostaria de manter as URLs e os ROLEs (do intercept-url) em banco de dados. Não configurando cada URL/ROLEs via XML.
Saberia como recuperar/configurar os ROLES e as URLs em banco de dados?
Muito Obrigado.
Olá..
Estou tentando implementar o login em uma pequena app jsf 2 que fiz, porém, quando tento rodar, aparece o erro:
java.lang.ClassNotFoundException: org.springframework.web.filter.DelegatingFilterProxy
Adicionei as bibliotecas indicadas às bibliotecas do netbeans, no mesmo lugar que tenho driver jdbc, hibernate e skin do primefaces… só a versão que está diferente, baixei a 2.0.6.
Ví que você já tinha o spring no resto do app, eu não tenho, devo adicionar alguma outra biblioteca?
Obrigado.
Olá Douglas,
Vc precisa adicionar os outros jars do spring que estão no projeto, pois são dependência.
[]‘s
Loiane Boa tarde..
Na minha APP não estou conseguindo fazer o Logout usando Spring security, como deve ficar o arquivo aplication e o jsp ?
Olá Junior,
Só precisa de colocar o link do logout na sua aplicação: http://www.loiane.com/2010/01/spring-security-login-e-logout-form-jsp/
Loiane
quando eu adiciona as tags no meu web.xml
springSecurityFilterChain
/*
as paginas da minha aplicação n ficam mais acessiveis, da aquele erro, HTTP Status 404, n sei o que fazer
Olá Junior,
Só olhando o config da aplicação e a estrutura da mesma pra falar qual é o erro.
Você pode estar protegendo arquivos essenciais para visualização que deveriam fica públicos.
Como por exemplo, a página de login não pode ter filtro, pois os ususário ainda não estão autenticados para fazer o filtro.
Olá Alvaro,
Demorei, mas descobri a resposta para a sua pergunta.
Existem alguma opções:
1 – Pode fazer através de Domain Object Security (ACLs): http://static.springsource.org/spring-security/site/docs/3.0.x/reference/domain-acls.html
2 – Pode implementar uma subclasse do AbstractSecurityInterceptor: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/technical-overview.html#secure-objects
Galera, estou comecando agora com o spring, e a explicacao acima é boa mesmo, mas eu quero perceber a funcao de cada “coisa” que configuramos, por exemplo o listener, filtre, etc..
Se pudesem me explicar ou indicar um tutorial que expliquem isso, agradeco.
Eh urgente galera
Obrigado
Olá Bma,
Sugiro que dê uma olhada na documentação que também explica cada tópico que citou.
Obrigada!
[]‘s
Olá!!! Apreciei bastante o conteúdo aqui apresentado. De muita qualidade e atenção com toda a comunidade Java. Todavia tenho uma dúvida que a princípio pode ser simples, mas não consegui ainda ver aderência de como fazer neste exemplo.
Imaginemos que eu já tenho uma classe que faz a abstração da minha camada de autenticação, ou seja, valida se as credenciais fornecidas estão corretas ou não… e retorna no método um boolean pra informar sucesso ou falha no processo.
Poderia usar esta classe como a ponte entre a página JSF e o SPRING SECURITY nos bastidores? Entendo que tudo não passa de user + password, apenas informando quem é o Authentication Provider que o SPRING SECURITY vai utilizar para validar a informação. A partir daí, ele mesmo está pronto para caminhar com as próprias pernas.
Neste exemplo procuro simplificar apenas para ter a ROLE_ADMIN e ROLE_USER.
Imagine que o set de páginas está disponível e que você deseja apenas implementar a segurança. Faz sentido esta idéia?
Obrigado pela atenção!
Legal o tutorial! Pelo Spring Security eu posso usar autenticação por outros meios como o oauth ou Google Connect!?
O Grails tem umas facilidades com relação ao SS, mas a mesma facilidade atrapalha em fazer coisas diferentes do que o plugin permite… rs! Seu tutorial foi de grande ajuda nisso!
Olá, Loiane, boa tarde! Uma pergunta (mas acho que já sei a resposta). Pra utilizar o SpringSecurity é necessário que o projeto já esteja utilizando o Spring?
Olá Obi-Wan_Zure,
Não. Se vc usar Spring fica mais fácil de configurar, mas vc pode usar o Spring Security sozinho no projeto sem problemas.
O Spring tem vários módulos, e esses módulos podem ser usados separadamente. O spring security é um deles e o spring mvc é um outro módulo.
[]‘s
Olá Geek And Cook,
Sim, é possível fazer essas integrações sim. Eu não tenho nenhum tutorial pronto sobre isso, mas na net vc acha alguns.
[]‘s
Olá PAlfonso,
Sim, é possível fazer isso sim.
Vc precisa criar sua própria forma de autenticação, mas precisa retornar o que o Spring Security espera receber.
Dê uma olhada na net como fazer a customização disso. Se não me engano, vc precisa criar apenas uma nova classe e e fazer a configuração no arquivo xml.
[]‘s
Bom dia Loiane, desde já gostei bastante de sua iniciativa mas, sou novo em Java e já tentei algumas tutoriais e não consegui até o momento aplicar o Spring nos exemplos, quando mando compilar da o seguinte erro:
O módulo não foi implementado.
at org.netbeans.modules.j2ee.deployment.devmodules.api.Deployment.deploy(Deployment.java:210)
at org.netbeans.modules.j2ee.ant.Deploy.execute(Deploy.java:106)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.GeneratedMethodAccessor77.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:390)
at org.apache.tools.ant.Target.performTasks(Target.java:411)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
at org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:284)
at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:539)
at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:153)
Me ajuda por favor!!!
No aguardo,
Olá Michael,
Infelizmente não uso Netbeans com SpringSecurity…