ExtJS 4: Gerenciamento de Sessão: Session Timeout

Ei pessoal,

Uma pergunta que recebo bastante por email e nas palestras sobre ExtJS que ministro é: Como fazer gerenciamento de sessão usando ExtJS 4? Bem, nesse post vamos tratar justamente sobre esse assunto através de um exemplo prático!

O exemplo desse post irá mostrar uma mensagem para o usuário perguntando se deseja continuar com a sessão aberta, e caso positivo, a app vai cutucar o servidor para manter a sessão ativa; caso negativo, a app automaticamente faz logout. Essa janela vai ficar 1 minuto aberta; caso o usuário não tome nenhuma decisão, também faz logout. A sessão se torna inativa caso o usuário não tome nenhuma ação em uma quantidade de tempo (exemplo: 15 minutos), isso inclui digitar alguma coisa no teclado ou mexer o mouse:

A classe que faz toda essa mágica está aqui:

[code lang="js" firstline="1" toolbar="true" collapse="false" wraplines="false"]<br />/**<br /><%%KEEPWHITESPACE%%> * Session Monitor task, alerts the user that their session will expire in 60 seconds and provides<br /><%%KEEPWHITESPACE%%> * the options to continue working or logout.  If the count-down timer expires,  the user is automatically<br /><%%KEEPWHITESPACE%%> * logged out.<br /><%%KEEPWHITESPACE%%> */<br />Ext.define('MyApp.widgets.SessionMonitor', {<br /><%%KEEPWHITESPACE%%>  singleton: true,<br /><br /><%%KEEPWHITESPACE%%>  interval: 1000 * 10,  // run every 10 seconds.<br /><%%KEEPWHITESPACE%%>  lastActive: null,<br /><%%KEEPWHITESPACE%%>  maxInactive: 1000 * 60 * 15,  // 15 minutes of inactivity allowed; set it to 1 for testing.<br /><%%KEEPWHITESPACE%%>  remaining: 0,<br /><%%KEEPWHITESPACE%%>  ui: Ext.getBody(),<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   * Dialog to display expiration message and count-down timer.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  window: Ext.create('Ext.window.Window', {<br /><%%KEEPWHITESPACE%%>    bodyPadding: 5,<br /><%%KEEPWHITESPACE%%>    closable: false,<br /><%%KEEPWHITESPACE%%>    closeAction: 'hide',<br /><%%KEEPWHITESPACE%%>    modal: true,<br /><%%KEEPWHITESPACE%%>    resizable: false,<br /><%%KEEPWHITESPACE%%>    title: 'Session Timeout Warning',<br /><%%KEEPWHITESPACE%%>    width: 325,<br /><%%KEEPWHITESPACE%%>    items: [{<br /><%%KEEPWHITESPACE%%>      xtype: 'container',<br /><%%KEEPWHITESPACE%%>      frame: true,<br /><%%KEEPWHITESPACE%%>      html: "Your session will automatically expires after 15 minutes of  inactivity. If your session expires, any unsaved data will be lost and  you will be automatically logged out. &lt;/br&gt;&lt;/br&gt;If you want  to continue working, click the 'Continue Working'  button.&lt;/br&gt;&lt;/br&gt;"<br /><%%KEEPWHITESPACE%%>    },{<br /><%%KEEPWHITESPACE%%>      xtype: 'label',<br /><%%KEEPWHITESPACE%%>      text: ''<br /><%%KEEPWHITESPACE%%>    }],<br /><%%KEEPWHITESPACE%%>    buttons: [{<br /><%%KEEPWHITESPACE%%>      text: 'Continue Working',<br /><%%KEEPWHITESPACE%%>      handler: function() {<br /><%%KEEPWHITESPACE%%>        Ext.TaskManager.stop(MyApp.widgets.SessionMonitor.countDownTask);<br /><%%KEEPWHITESPACE%%>        MyApp.widgets.SessionMonitor.window.hide();<br /><%%KEEPWHITESPACE%%>        MyApp.widgets.SessionMonitor.start();<br /><%%KEEPWHITESPACE%%>        // 'poke' the server-side to update your session.<br /><%%KEEPWHITESPACE%%>        Ext.Ajax.request({<br /><%%KEEPWHITESPACE%%>          url: 'user/poke.action'<br /><%%KEEPWHITESPACE%%>        });<br /><%%KEEPWHITESPACE%%>      }<br /><%%KEEPWHITESPACE%%>    },{<br /><%%KEEPWHITESPACE%%>      text: 'Logout',<br /><%%KEEPWHITESPACE%%>      action: 'logout',<br /><%%KEEPWHITESPACE%%>      handler: function() {<br /><%%KEEPWHITESPACE%%>        Ext.TaskManager.stop(MyApp.widgets.SessionMonitor.countDownTask);<br /><%%KEEPWHITESPACE%%>        MyApp.widgets.SessionMonitor.window.hide();<br /><br /><%%KEEPWHITESPACE%%>        // find and invoke your app's "Logout" button.<br /><%%KEEPWHITESPACE%%>        Ext.ComponentQuery.query('button[action="buttonLogout"]')[0].fireEvent('click');<br /><%%KEEPWHITESPACE%%>      }<br /><%%KEEPWHITESPACE%%>    }]<br /><%%KEEPWHITESPACE%%>  }),<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   * Sets up a timer task to monitor for mousemove/keydown events and<br /><%%KEEPWHITESPACE%%>   * a count-down timer task to be used by the 60 second count-down dialog.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  constructor: function(config) {<br /><%%KEEPWHITESPACE%%>    var me = this;<br /><br /><%%KEEPWHITESPACE%%>    // session monitor task<br /><%%KEEPWHITESPACE%%>    this.sessionTask = {<br /><%%KEEPWHITESPACE%%>      run: me.monitorUI,<br /><%%KEEPWHITESPACE%%>      interval: me.interval,<br /><%%KEEPWHITESPACE%%>      scope: me<br /><%%KEEPWHITESPACE%%>    };<br /><br /><%%KEEPWHITESPACE%%>    // session timeout task, displays a 60 second countdown<br /><%%KEEPWHITESPACE%%>    // message alerting user that their session is about to expire.<br /><%%KEEPWHITESPACE%%>    this.countDownTask = {<br /><%%KEEPWHITESPACE%%>      run: me.countDown,<br /><%%KEEPWHITESPACE%%>      interval: 1000,<br /><%%KEEPWHITESPACE%%>      scope: me<br /><%%KEEPWHITESPACE%%>    };<br /><%%KEEPWHITESPACE%%>  },<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   * Simple method to register with the mousemove and keydown events.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  captureActivity : function(eventObj, el, eventOptions) {<br /><%%KEEPWHITESPACE%%>    this.lastActive = new Date();<br /><%%KEEPWHITESPACE%%>  },<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   *  Monitors the UI to determine if you've exceeded the inactivity threshold.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  monitorUI : function() {<br /><%%KEEPWHITESPACE%%>    var now = new Date();<br /><%%KEEPWHITESPACE%%>    var inactive = (now - this.lastActive);<br /><br /><%%KEEPWHITESPACE%%>    if (inactive &gt;= this.maxInactive) {<br /><%%KEEPWHITESPACE%%>      this.stop();<br /><br /><%%KEEPWHITESPACE%%>      this.window.show();<br /><%%KEEPWHITESPACE%%>      this.remaining = 60;  // seconds remaining.<br /><%%KEEPWHITESPACE%%>      Ext.TaskManager.start(this.countDownTask);<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>  },<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   * Starts the session timer task and registers mouse/keyboard activity event monitors.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  start : function() {<br /><%%KEEPWHITESPACE%%>    this.lastActive = new Date();<br /><br /><%%KEEPWHITESPACE%%>    this.ui = Ext.getBody();<br /><br /><%%KEEPWHITESPACE%%>    this.ui.on('mousemove', this.captureActivity, this);<br /><%%KEEPWHITESPACE%%>    this.ui.on('keydown', this.captureActivity, this);<br /><br /><%%KEEPWHITESPACE%%>    Ext.TaskManager.start(this.sessionTask);<br /><%%KEEPWHITESPACE%%>  },<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   * Stops the session timer task and unregisters the mouse/keyboard activity event monitors.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  stop: function() {<br /><%%KEEPWHITESPACE%%>    Ext.TaskManager.stop(this.sessionTask);<br /><%%KEEPWHITESPACE%%>    this.ui.un('mousemove', this.captureActivity, this);  //  always wipe-up after yourself...<br /><%%KEEPWHITESPACE%%>    this.ui.un('keydown', this.captureActivity, this);<br /><%%KEEPWHITESPACE%%>  },<br /><br /><%%KEEPWHITESPACE%%>  /**<br /><%%KEEPWHITESPACE%%>   * Countdown function updates the message label in the user dialog which displays<br /><%%KEEPWHITESPACE%%>   * the seconds remaining prior to session expiration.  If the counter expires, you're logged out.<br /><%%KEEPWHITESPACE%%>   */<br /><%%KEEPWHITESPACE%%>  countDown: function() {<br /><%%KEEPWHITESPACE%%>    this.window.down('label').update('Your session will expire in ' +  this.remaining + ' second' + ((this.remaining == 1) ? '.' : 's.') );<br /><br /><%%KEEPWHITESPACE%%>    --this.remaining;<br /><br /><%%KEEPWHITESPACE%%>    if (this.remaining &lt; 0) {<br /><%%KEEPWHITESPACE%%>      this.window.down('button[action="logout"]').handler();<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>  }<br /><br />});<br />[/code]

Vamos ver alguns detalhes importantes:

  • linha 11maxInactive - tempo de inatividade do usuário para inativar a sessão. Nesse caso, o valor padrão são de 15 minutos, mas você pode mudar esse valor para a quantidade de tempo que você deseja (em minutos).
  • linha 29: html - mensagem que será mostrada para o usuário na janela. Está em inglês, mas você pode traduzir para português se desejar.
  • linha 42: url - aqui você vai colocar a url para "cutucar" o servidor para manter a sessão ativa no server também.
  • linha 53: aqui colocamos o selector a ser chamado para disparar o evento de logout automaticamente. Nesse exemplo, repare que tem um botão de logout, e essa linha chama exatamente a função que faz logut (nesse exemplo é apenas um alert, mas isso você deve tratar como deseja na sua aplicação). Só um detalhe: não chame a action do seu botão de logut de logout, pois este já está sendo usado por essa classe.

Bem pessoal, é isso!

Se quiser usar essa classe na sua aplicação, basta chamar MyApp.widgets.SessionMonitor.start(). Se estiver usando a arquitetura MVC, coloque isso dentro da função launch da app. No final desse post tem um exemplo de uma app usando essa classe.

Código Fonte Completo: https://github.com/loiane/extjs4-session-timeout

Disclaimer: eu não sou autora desse código, portando, qualquer problema reclame nessa thread do fórum da Sencha: http://www.sencha.com/forum/showthread.php?195957-Session-Timeout-Alert-using-PHP-amp-Extjs4

Até a próxima! :)