Customizando o Spring Security 3.1

Veja neste artigo como podemos customizar o login padrão do Spring Security. Veremos como podemos modificar o layout padrão, mudar o valor dos atributos padrão, como fornecedor uma opção de logout, e muito mais.

Vamos ver nesse artigo como podemos customizar o nosso login para deixá-lo mais profissional e adequado a um ambiente de produção. Para conferir como podemos criar uma aplicação rapidamente com Spring Security disponibilizamos um link nas referências bibliográficas indicando o artigo.

Entre as customizações que serão realizadas destacam-se:

No restante do artigo veremos como podemos efetuar as customizações numa aplicação web.

Customizando o Login da Aplicação

Primeiramente devemos atualizar o arquivo security.xml, conforme exemplificado na Listagem 1.

<?xml version="1.0" encoding="UTF-8"?> <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-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <http auto-config="true" use-expressions="true"> <intercept-url pattern="/" access="permitAll"/> <intercept-url pattern="/login/*" access="permitAll"/> <intercept-url pattern="login.jsp" access="permitAll"/> <intercept-url pattern="/logout.jsp" access="permitAll"/> <intercept-url pattern="/index.jsp" access="hasRole('ROLE_USER')"/> <form-login login-page="/login.jsp" login-processing-url="/fazerLogin" authentication-failure-url="/login.jsp?error" default-target-url="/index.jsp"/> <logout logout-url="/logout" logout-success-url="/login.jsp?logout"/> </http> <authentication-manager> <authentication-provider> <user-service> <user name="user1@example.com" password="user1" authorities="ROLE_USER"/> <user name="admin1@example.com" password="admin1" authorities="ROLE_USER,ROLE_ADMIN"/> </user-service> </authentication-provider> </authentication-manager> </beans:beans>
Listagem 1. Exemplo do arquivo security.xml atualizado com novos elementos

Como novidades no arquivo security.xml, temos na tag em o atributo login-page que especifica onde o Spring Security redirecionará o browser quando uma página protegida for acessada e o usuário não estiver autenticado. Caso uma página de login não for especificada, o Spring Security irá redirecionar o usuário para a url padrão /spring_security_login. Dessa forma, org.springframework.security.web.filter.FilterChainProxy escolherá o org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter, na qual exibirá a página de login padrão. DefaultLoginPageGeneratingFilter está configurado para processar /spring_security_login. Como sobrescrevemos a URL default, somos responsáveis pela página de login quando a URL /login.jsp for solicitada.

O atributo login-processing-url tem como padrão /j_spring_security_check. Este atributo especifica a URL que o formulário de login (que deve incluir o nome de usuário e senha) deve ser submetido, usando um HTTP POST. Quando o Spring Security processa esta solicitação, ele tentará autenticar o usuário. Alteramos o valor padrão para /fazerLogin.

Os atributos username-parameter e password-parameter têm como padrão j_username e j_password respectivamente e especifica os parâmetros HTTP que o Spring Security usará para autenticar o usuário quando processar login-processing-url. Para este código não foram adicionados esses atributos, dessa forma, nosso formulário deverá ter os valores padrão.

O atributo authentication-failure-url especifica a página que o Spring Security redirecionará se o username e password submetido por login-processing-url for inválido.

É sempre uma boa prática sobrescrevermos todos esses atributos como forma de não expor que estamos supostamente usando o Spring Security. Revelar o framework que estamos usando é um tipo de "vazamento de informações", tornando mais fácil para os atacantes determinar potenciais falhas na segurança de uma aplicação. Portanto, apenas estamos utilizando o username e password padrão para fins didáticos, para que o leitor possa verificar que se não especificarmos esses atributos com seus valores de forma explícita deveremos usar os valores definidos por default.

Agora podemos definir o nosso login customizado. A JSP da Listagem 2 demonstra um exemplo de uma página de login.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Página de Login Customizado</title> </head> <body> <!-- HEADER --> <div> <ul class="nav"> <sec:authorize access="authenticated" var="authenticated"/> <c:choose> <c:when test="$"> <li id="greeting"> <div> Bem-vindo <sec:authentication property="name" /> </div> </li> <li> <a href="/ExemploSpringSecurity/logout">Logout</a> </li> </c:when> <c:otherwise> <li> <a id="navLoginLink" href="/login.jsp">Login</a> </li> </c:otherwise> </c:choose> </ul> </div> <!-- FORMULÁRIO --> <form name="f" action="<c:url value='/fazerLogin'/>" method="post"> <c:if test="${param.error != null}"> <div class="alert alert-error"> Falha ao fazer Login. <c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}"> Motivo: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" /> </c:if> </div> </c:if> <c:if test="${param.logout != null}"> <div class="alert alert-success"> Você efetuou log out. </div> </c:if> <label for="username">Username</label> <input type="text" id="j_username" name="j_username"/> <label for="password">Senha</label> <input type="password" id="j_password" name="j_password"/> <div class="form-actions"> <input id="submit" class="btn" name="submit" type="submit" value="Login"/> </div> </form> </body> </html>
Listagem 2. Exemplo de página JSP para login, logout e mensagens

Existem alguns itens importantes no código acima, entre eles destacam-se:

Segue na Listagem 3 o arquivo web.xml que estamos utilizando e já foi explicado no primeiro artigo sobre Spring Security (link nas referências bibliográficas).

<?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>Exemplo Spring Security</display-name> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> </welcome-file-list> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/security.xml</param-value> </context-param> <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> </web-app>
Listagem 3. Exemplo do arquivo web.xml utilizado

Assim sendo, com esses três arquivos já temos nossa aplicação pronta para rodar com um login customizado.

Podemos notar também que o nosso login customizado possui suporte para logout que é muito simples de ser criado visto que a configuração do Sprint Security adiciona suporte automático para esta funcionalidade. Dessa forma, precisamos apenas criar um link que aponta para /j_spring_security_logout. O logout também pode ser customizado no arquivo security.xml. Para criar o logout apenas adicionamos a linha abaixo no arquivo security.xml dentro da tag http:

<logout logout-url="/logout" logout-success-url="/login.jsp?logout"/>

Agora basta adicionar também um link para o usuário clicar e efetuar o logout da aplicação. Basta adicionarmos o código abaixo na nossa JSP:

<a href="/ExemploSpringSecurity/logout">Logout</a>

Para facilitar. também criamos uma mensagem indicando se o usuário fez logout. Nesse caso, quando o usuário faz logout é enviado um parâmetro logout, portanto basta verificarmos se o parâmetro foi recebido no carregamento da página. Segue na Listagem 4 o código que utilizamos na aplicação.

<c:if test="${param.logout != null}"> <div class="alert alert-success"> Você efetuou log out. </div> </c:if>
Listagem 4. Exemplo de como exibir uma mensagem quando o usuário faz logout

O Spring Security também permite que possamos colocar múltiplos elementos permitindo um melhor controle de diferentes porções da aplicação. Cada elemento http é considerado na ordem e a primeira combinação será realizada. Portanto, a ordem é muito importante.

Uma configuração muito importante que foi feita no arquivo security.xml é no onde definimos o . Essa configuração permite um controle mais granular sobre como os recursos podem ser acessados. Segue na Listagem 5 a configuração realizada.

<intercept-url pattern="/" access="permitAll"/> <intercept-url pattern="/login/*" access="permitAll"/> <intercept-url pattern="login.jsp" access="permitAll"/> <intercept-url pattern="/logout.jsp" access="permitAll"/> <intercept-url pattern="/index.jsp" access="hasRole('ROLE_USER')"/>
Listagem 5. Exemplo de um maior controle sobre os recursos da aplicação usando intercept-url

Nesse caso permitimos acesso total ao recurso de login, logout e restringimos acesso à página index.jsp apenas para usuários logados e que tenham o papel ROLE_USER que também está definido no arquivo security.xml. O pattern /login/* foi adicionado caso tenhamos algum servlet que faça alguma interceptação, caso não tenhamos ele não é necessário. É muito importante darmos permissão para todos acessarem as páginas de login e logout ou a aplicação pode entrar em loop e o navegador exibirá uma página de erro.

Devemos notar também que o atributo use-expressions está setado para true, nesse caso estamos usando a Spring Expression Language (SpEL) para determinar se um usuário tem autorização. Caso ele não esteja marcado como true, deveríamos usar conforme a Listagem 6.

<intercept-url pattern="/" access="ROLE_ANONYMOUS,ROLE_USER"/> <intercept-url pattern="/login.jsp" access="ROLE_ANONYMOUS,ROLE_USER"/> <intercept-url pattern="/logout.jsp" access="ROLE_ANONYMOUS,ROLE_USER"/> <intercept-url pattern="/index.jsp" access="ROLE_USER"/>
Listagem 6. Exemplo não utilizando expressões

Nesse caso ROLE_ANONYMOUS indica um usuário que não está logado e tem acesso total ao recurso.

Na aplicação que foi criada também utilizamos uma tag library para exibir informações sobre o usuário autenticado. Para isso tivemos que incluir a tag library chamada spring-securitytaglibs-3.1.0.RELEASE.jar na nossa aplicação.

Para utilizarmos essa tag library tivemos que incluir a diretiva abaixo na nossa JSP:

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

E no corpo da nossa página JSP utilizamos também:

<sec:authorize access="authenticated" var="authenticated"/>

e também:

<sec:authentication property="name" />

A tag determina se o usuário está autenticado ou não e atribui este valor para a variável authenticated. A tag procura pelo objeto org.spring.security.core.Authentication atual. O atributo property irá procurar o atributo "principal" em org.spring.security.core.Authentication, que neste caso é do tipo org.spring.security.core.userdetails.UserDetails. Este por sua vez obtém a propriedade username em UserDetails e exibe este valor na página.

Outra configuração interessante para uma aplicação é customizarmos o comportamento da aplicação após o login. O atributo default-target-url do elemento define para onde a aplicação será redirecionada após um login ser feito com sucesso. Se default-target-url estiver indefinido seremos redirecionados para a raiz da aplicação.

Vale ressaltar que se um usuário solicitar uma página protegida, antes de ser autenticado, o Spring Security vai lembrar-se da última página protegida que foi acessada antes de autenticar. Para isso o Spring Security usa o org.spring.security.web.savedrequest.RequestCache. Portanto, após a autenticação ser bem sucedida, o Spring Security irá enviar o usuário para a última página protegida que foi acessada antes da autenticação. Por exemplo, se um usuário não autenticado solicita a página teste.jsp, ele será enviado para a página de login, para que o usuário possa ser autenticado antes de acessar o recurso protegido. Após o usuário ser autenticado com sucesso, o usuário será enviado para a solicitação anteriormente solicitada, ou seja, a página teste.jsp.

Se quisermos que o usuário sempre seja redirecionado para o default-target-url podemos setar o atributo alwaysuse-default-target para true. Segue abaixo um exemplo:

<form-login ... always-use-default-target="true"/>

Executando a aplicação

Na Figura 1 vemos como ficou a estrutura completa da aplicação.

Figura 1. Estrutura da aplicação

Ao executar a aplicação temos primeiramente a tela de login sendo exibida ao usuário, solicitando suas credenciais. Veja a tela da Figura 2.

Figura 2. Tela de login

A tela de login foi exibida, pois a aplicação tentou acessar a index.jsp, porém para acessar essa página o usuário precisa de autorização, conforme configuramos no arquivo security.xml. Se digitarmos um login errado, como exibido na Figura 3. teremos a seguinte tela sendo exibida.

Figura 3. Mensagem de falha ao fazer login é exibida

Se colocarmos o username e senha corretos somos redirecionados para a tela solicitada, ou seja, para a página index.jsp. A Figura 4 mostra a tela.

Figura 4. Tela requisitada exibida com sucesso

Se visitarmos novamente a tela de login, veremos que duas novas opções serão exibidas. Uma das opções é para que possamos fazer logout e a outra é o nome do usuário atualmente logado na aplicação. A Figura 5 mostra a tela.

Figura 5. Informações do usuário e opção de logout são exibidas para o usuário autenticado

Essas informações também poderiam estar disponíveis em toda a aplicação, num cabeçalho ou num menu fixo.

Dessa forma, nossa aplicação já possui um login customizado e executando no navegador do usuário.

Bibliografia:
  1. Spring Security, disponível em projects.spring.io/spring-security
  2. Robert Winch, Peter Mularien. Spring Security 3.1.. Packt Publishing, 2012.
  3. Mick Knutson. Java EE 6 Cookbook for Securing, Tuning and Extending Enterprise Applications. Packt Publishing, 2012.
  4. Spring Security 3.1: Configuração e utilização em um exemplo web.

Artigos relacionados