Informática -> Spring Security + ExtJS (Ajax)

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:

01Ext.onReady(function(){
02 Ext.QuickTips.init();
03
04 // Create a variable to hold our EXT Form Panel.
05
06 // Assign various config options as seen.
07 var login = new Ext.FormPanel({
08 labelWidth:80,
09 url:'j_spring_security_check',
10 frame:true,
11 title:'Please Login',
12
13 defaultType:'textfield',
14 width:300,
15 height:150,
16 monitorValid:true,
17 // Specific attributes for the text fields for username / password.
18 // The "name" attribute defines the name of variables sent to the server.
19
20 items:[{
21 fieldLabel:'Username',
22 name:'j_username',
23 allowBlank:false
24 },{
25 fieldLabel:'Password',
26
27 name:'j_password',
28 inputType:'password',
29 allowBlank:false
30 }],
31
32 // All the magic happens after the user clicks the button
33 buttons:[{
34
35 text:'Login',
36 formBind: true,
37 // Function that fires when user clicks the button
38 handler:function(){
39 login.getForm().submit({
40
41 method:'POST',
42
43 // Functions that fire (success or failure) when the server responds.
44 // The server would actually respond with valid JSON,
45 // something like: response.write "{ success: true}" or
46
47 // response.write "{ success: false, errors: { reason: 'Login failed. Try again.' }}"
48 // depending on the logic contained within your server script.
49 // If a success occurs, the user is notified with an alert messagebox,
50
51 // and when they click "OK", they are redirected to whatever page
52 // you define as redirect.
53
54 success:function(){
55 Ext.Msg.alert('Status', 'Login Successful!', function(btn, text){
56
57 if (btn == 'ok'){
58 window.location = 'main.action';
59 }
60 });
61
62 },
63
64 // Failure function, see comment above re: success and failure.
65 // You can see here, if login fails, it throws a messagebox
66 // at the user telling him / her as much.
67
68 failure:function(form, action){
69 if(action.failureType == 'server'){
70 obj = Ext.util.JSON.decode(action.response.responseText);
71
72 Ext.Msg.alert('Login Failed!', obj.errors.reason);
73 }else{
74 Ext.Msg.alert('Warning!', 'Authentication server is unreachable : ' + action.response.responseText);
75
76 }
77 login.getForm().reset();
78 }
79
80 });
81 }
82 }]
83 });
84
85 login.render('login');
86
87});

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:

01package com.loiane.security;
02
03import java.io.IOException;
04import java.io.Writer;
05
06import javax.servlet.http.HttpServletRequest;
07import javax.servlet.http.HttpServletResponse;
08import javax.servlet.http.HttpServletResponseWrapper;
09
10import org.springframework.security.Authentication;
11import org.springframework.security.AuthenticationException;
12import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
13
14public class MyAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
15
16 protected void onSuccessfulAuthentication(HttpServletRequest request,
17 HttpServletResponse response, Authentication authResult)
18 throws IOException {
19 super.onSuccessfulAuthentication(request, response, authResult);
20
21 HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
22
23 Writer out = responseWrapper.getWriter();
24
25 String targetUrl = determineTargetUrl( request );
26 out.write("{success:true, targetUrl : \'" + targetUrl + "\'}");
27 out.close();
28
29 }
30
31 protected void onUnsuccessfulAuthentication( HttpServletRequest request,
32 HttpServletResponse response, AuthenticationException failed )
33 throws IOException {
34
35 HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(response);
36
37 Writer out = responseWrapper.getWriter();
38
39 out.write("{ success: false, errors: { reason: 'Login failed. Try again.' }}");
40 out.close();
41
42 }
43
44}

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

01xml version="1.0" encoding="UTF-8"?>
02
08
09 <security:global-method-security />
10
11 <security:http auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint">
12 <security:intercept-url pattern="/index.jsp" filters="none" />
13 <security:intercept-url pattern="/*.action" access="ROLE_USER" />
14 security:http>
15
16 <bean id="authenticationProcessingFilter" class="com.loiane.security.MyAuthenticationProcessingFilter">
17 <security:custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
18 <property name="defaultTargetUrl" value="/main.html" />
19 <property name="authenticationManager" ref="authenticationManager" />
20 bean>
21
22 <security:authentication-manager alias="authenticationManager" />
23
24 <bean id="authenticationProcessingFilterEntryPoint"
25 class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
26 <property name="loginFormUrl" value="/index.jsp" />
27 <property name="forceHttps" value="false" />
28 bean>
29
30
38 <security:authentication-provider>
39 <security:password-encoder hash="md5"/>
40 <security:user-service>
41 <security:user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
42 <security:user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
43 <security:user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
44 <security:user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" />
45 security:user-service>
46 security:authentication-provider>
47beans>

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

Fonte: http://www.loiane.com/2010/02/integrando-spring-security-com-a-pagina-de-login-do-extjs/