Integrando Spring Security com a página de Login do ExtJS

01 Feb 2010
4 mins read

Esse tutorial irá abordar como configurar o form de login da biblioteca ExtJS (Ajax) ao invés de utilizar a página de login (login.jsp) padrão do Spring Security..

Em vez de usar a página de login do Spring Security, por que não usar um form feito com Ajax?

E como integrar a página de login do ExtJS com o framework Spring Security?

Ok, você já tentou fazer isso, o usuário foi autenticado com sucesso, mas o usuário não é redirecionado para a página principal da aplicação. Como consertar isso?

Não importa de você setou a opção default-target-url no arquivo applicationContext-security.xml, ou setou uma URL para redirecionamento no lado do servidor. Não vai funcionar.

O problema é que o ExtJS faz uma chamada/request Ajax, e nenhum redirecionamento irá funcionar no lado servidor (spring). Você deve fazer esse redirecionamento no lado cliente, que é no código ExtJS/javascript.

Primeiro, você precisa crier o form do login. Você pode utilizar o código de exemplo disponibilizado pelo ExtJS: http://www.extjs.com/learn/Tutorial:Basic_Login e customizá-lo/modificá-lo para funcinoar com o Spring Security.

Se der uma olhada no arquivo login.jsp (padrão do Spring Security), você irá perceber três pontos chaves do form:

  1. URL / form action: j_spring_security_check
  2. Username input name: j_username
  3. Password input name: j_password

É isso que precisa customizar no login do ExtJS para fazê-lo funcinar com o Spring Security. Mas não pense que é assim tão fácil, ainda tem um pequeno detalhe que precisa consertar para funcionar perfeitamente.

O login.js irá ficar assim após as moficações:

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]
Ext.onReady(function(){
Ext.QuickTips.init();

// Create a variable to hold our EXT Form Panel.

// Assign various config options as seen.
var login = new Ext.FormPanel({
labelWidth:80,
url:'j_spring_security_check',
frame:true,
title:'Please Login',

defaultType:'textfield',
width:300,
height:150,
monitorValid:true,
// Specific attributes for the text fields for username / password.
// The "name" attribute defines the name of variables sent to the server.

items:[{
fieldLabel:'Username',
name:'j_username',
allowBlank:false
},{
fieldLabel:'Password',

name:'j_password',
inputType:'password',
allowBlank:false
}],

// All the magic happens after the user clicks the button
buttons:[{

text:'Login',
formBind: true,
// Function that fires when user clicks the button
handler:function(){
login.getForm().submit({

method:'POST',

// Functions that fire (success or failure) when the server responds.
// The server would actually respond with valid JSON,
// something like: response.write "{ success: true}" or

// response.write "{ success: false, errors: { reason: 'Login failed. Try again.' }}"
// depending on the logic contained within your server script.
// If a success occurs, the user is notified with an alert messagebox,

// and when they click "OK", they are redirected to whatever page
// you define as redirect.

success:function(){
Ext.Msg.alert('Status', 'Login Successful!', function(btn, text){

if (btn == 'ok'){
window.location = 'main.action';
}
});

},

// Failure function, see comment above re: success and failure.
// You can see here, if login fails, it throws a messagebox
// at the user telling him / her as much.

failure:function(form, action){
if(action.failureType == 'server'){
obj = Ext.util.JSON.decode(action.response.responseText);

Ext.Msg.alert('Login Failed!', obj.errors.reason);
}else{
Ext.Msg.alert('Warning!', 'Authentication server is unreachable : ' + action.response.responseText);

}
login.getForm().reset();
}

});
}
}]
});

login.render('login');

});

[/code]

O que está faltando?

É necessário customizar a classe AuthenticationProcessingFilter para o Spring Security executar a ação no login.

Os métodos “onSuccessfulAuthentication” e “onUnsuccessfulAuthentication” precisam retornar algum conteúdo JSON. Se o usuário for autenticado com sucesso, então redireciona-o para a página principal da aplicação, senão, a aplicação irá mostrar uma mensagem de erro.

Essa é a classe MyAuthenticationProcessingFilter customizada:

[code lang="java" firstline="1" toolbar="true" collapse="false" wraplines="false"]
package com.loiane.security;

import java.io.IOException;
import java.io.Writer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;

public class MyAuthenticationProcessingFilter extends AuthenticationProcessingFilter {

protected void onSuccessfulAuthentication(HttpServletRequest request,
HttpServletResponse response, Authentication authResult)
throws IOException {
super.onSuccessfulAuthentication(request, response, authResult);

HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);

Writer out = responseWrapper.getWriter();

String targetUrl = determineTargetUrl( request );
out.write("{success:true, targetUrl : \'" + targetUrl + "\'}");
out.close();

}

protected void onUnsuccessfulAuthentication( HttpServletRequest request,
HttpServletResponse response, AuthenticationException failed )
throws IOException {

HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);

Writer out = responseWrapper.getWriter();

out.write("{ success: false, errors: { reason: 'Login failed. Try again.' }}");
out.close();

}

}

[/code]

E o arquivo applicationContext-security.xml ficará assim:

[code lang="xml" firstline="1" toolbar="true" collapse="false" wraplines="false"]
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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.xsd">

<security:global-method-security />

<security:http auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint">
<security:intercept-url pattern="/index.jsp" filters="none" />
<security:intercept-url pattern="/*.action" access="ROLE_USER" />
</security:http>

<bean id="authenticationProcessingFilter" class="com.loiane.security.MyAuthenticationProcessingFilter">
<security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
<property name="defaultTargetUrl" value="/main.html" />
<property name="authenticationManager" ref="authenticationManager" />
</bean>

<security:authentication-manager alias="authenticationManager" />

<bean id="authenticationProcessingFilterEntryPoint"
class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/index.jsp" />
<property name="forceHttps" value="false" />
</bean>

<!--
Usernames/Passwords are
rod/koala
dianne/emu
scott/wombat
peter/opal
These passwords are from spring security app example
-->
<security:authentication-provider>
<security:password-encoder hash="md5"/>
<security:user-service>
<security:user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
<security:user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
<security:user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
<security:user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</beans>
[/code]

Agora irá logar normalmente com o form de login do ExtJS.

Fiz uma pequena aplicação de exemplo. Se desejar, pode fazer o download do meu repositório no GitHub: http://github.com/loiane/spring-security-extjs-login

Bons códigos!