19 abril 2011

[Single Sign-On] Adicionando Concurrency Session Control

E cá vamos nós de novo. A demanda desta semana (curta, diga-se de passagem) foi impedir que fosse possível um usuário efetuar login quando ele "já" estivesse logado. Por exemplo, se um usuário efetuar login no navegador A, que não fosse possível efetuar o login (com o mesmo usuário e senha) no navegador B da mesma máquina. Ou ainda, se um usuário fizer login numa máquina (IP) A, que não fosse possível efetuar login (com o mesmo usuário e senha) numa máquina (IP) B.

No post anterior, vimos como foi possível implementar o Single Sign On (SSO) numa aplicação web java usando o JaSig CAS e o Spring Security.

Configurando


Lendo a sessão Concurrent Session Control da documentação do Spring Security, iremos ver que, antes de qualquer coisa, precisamos configurar um novo listener no web.xml

<listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

O uso deste listener faz com que um ApplicationEvent seja publicado no contexto do spring sempre que uma sessão de usuário inicie ou termine.

Após a configuração do listener, também é necessário criar um ConcurrencySessionFilter e adicioná-lo à cadeia de filtros de segurança do spring.

<http entry-point-ref="casAuthenticationEntryPoint" use-expressions="true" >
    <intercept-url pattern="/javax.faces.resource/**" filters="none" />
    <intercept-url pattern="/resources/**" filters="none" />
    <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
    <intercept-url pattern="/secure/**" access="hasRole('ROLE_ADMIN')" requires-channel="https" />
    <logout logout-success-url="${logout.success.url}"  />
    
    <custom-filter ref="casAuthenticationFilter" after="CAS_FILTER"/>
    <custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER" />
    
    <session-management session-authentication-strategy-ref="sas" />
</http>

Criaremos um novo ConcurrentSessionFilter, referenciando-o por concurrencyFilter. Adicionamos este novo filtro na cadeia de fitlros do spring através da tag custom-filter. Na tag session-management, definimos qual o bean responsável pelo controle customizado de sessão. É importante que criemos um novo bean, ao invés do bean default (criado quando utilizamos a tag concurrency-control dentro da tag session-management), porque precisaremos integrá-lo à configuração do SSO.

Configuramos no CasAuthenticationFilter a propriedade sessionAuthenticationStrategy, com o mesmo bean que utilizamos para configurar a tag session-management:

<beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
    ...
    <beans:property name="sessionAuthenticationStrategy" ref="sas" />        
    ...
</beans:bean>

Agora, criaremos os novos beans necessários para o controle de sessões concorrentes.

<beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/sessionExpired.jsf" />
</beans:bean>
 

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    <beans:property name="exceptionIfMaximumExceeded" value="true" />
</beans:bean>

<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

Como mencionamos anteriormente, o novo listener que configuramos lança um ApplicaitonEvent no contexto do spring quando uma sessão de usuário se inicia ou termina. Isso permite que o SessionRegistry seja notificado quando uma sessão de usuário termina. O SessionRegistry permite que se conheça todas as sessões abertas para determinado usuário, informação esta que é utilizada pelo ConcurrentSessionControlStrategy para barrar novos logins.

Testando


Os testes que eu fiz se resumem a logar em navegadores diferentes, e em máquinas diferentes. Pra ambos os casos, o segundo login foi barrado, de forma que o usuário é redirecionado para a página de acesso não autorizado, configurada anteriormente no CasAuthenticationFilter.

Creative Commons License
Esta obra está licenciada sob uma Licença Creative Commons.
Comentários
1 Comentários

1 comments:

Balarini disse...

Olá Soriano,

Como recuperar os dados que estão em sessão? Gostaria de guardar algumas informações do usuário na sessão, é possível?!

Postar um comentário

Regras são chatas, mas...

- Seu comentário precisa ter relação com o assunto do post;
- Em hipótese alguma faça propaganda de outros blogs ou sites;
- Não inclua links desnecessários no conteúdo do seu comentário;
- Se quiser deixar sua URL, comente usando a opção OpenID;
- CAIXA ALTA, miguxês ou erros de ortografia não serão tolerados;
- Ofensas pessoais, ameaças e xingamentos não são permitidos;