SessionManager
O SessionManager tem a finalidade de controlar as sessões de autenticação do usuário e controlar suas sessões de login. Bem como, a partir da identificação da sessão do usuário permitir ou não acesso aos métodos da fachada.
Inicialização do Sistema
Antes de começar a utilizar o SessionManager, é preciso inicializa-lo. Para isso a melhor maneira de fazer isso é pelo método initializeSessionManager(...) da clase FW. Isso porque se houver alterações em relação a inicialização do serviço nas novas versões, esse método será atualizado pedindo todas as informações em um único lugar. Garantindo que o desenvolvedor dê atenção ao atualizar para a nova versão.
Outra maneira de inicializar e controlar melhor o serviço é olhar os métodos das classes que compõe o SessionManager, e suas documentações (JavaDOC).
SessionManager
SessionManager é a classe principal do sistema de autenticação e segurança. Os métodos e configuração, autenticação (LogIn), forçar o fechamento de uma sessão (LogOff), etc. podem ser encontrados nela.
SessionBackOperation
Entre as obrigatoriedades para inicializar o SessionManager, é obrigatório definir uma implementação da interface SessionBackOperation. Essa interface é responsável por passar para o sistema a autenticação do usuário. O SessionManager cria e gerencia as sessões dos usuários, mas cabe ao sistema autenticar, autorizar e criar os objetos de sessão definindo as permissões que o usuário terá no sistema. Todas essas funções são repassadas ao sistema através desta interface.
Funcionamento
Para ter acesso aos métodos é preciso que o "actor" (quem estiver tentando acessar o sistema) faça uma autenticação (LogIn). Ao realizar a autenticação será criada uma sessão para esta autenticação. A sessão é reconhecida por UUID (Universal Unique ID). Este UUID será utilizado para permitir que os métodos sejam acessados. Em outras palavras, o que dá permissão nos métodos é o identificador UUID de uma sessão válida, os dados utilizados no Login (como usuário/senha) só são utilizados para iniciar a sessão e mais nada.
A sessão permanecerá viva enquanto ela continuar sendo utilizada para acessar os métodos da fachada do sistema. A sessão expira se não houver atividade por um determinado tempo. Esse tempo é configurável através dos métodos do SessionManager.
Tipos de Autenticação
Atualmente o SessionManager aceita realizar 2 tipos de atutenticação:
- Usuário/Senha - A autenticação é realizada por um par de usuário/senha que é repassado pela interface para o sistema validar. Nesse modo é permitido também receber um Locale, para configurar a sessão do usuário com uma formatação regional diferente. Esse Locale é repassado também pela interface, e recomenda-se que fique anexado no objeto de sessão criado.
- Token - A autenticação por token é útil quando temos uma autenticação de máquina, como estações ou sistemas que se integrarão no sistema.
Fluxo de Autenticação
Os passos da autenticação são:
- Para qualquer tipo de LogIn, o sistema deve oferecer um método na fachada para realizar o LogIn, que por óbvio não exige nenhum tipo de autenticação para acesso. Esse método deve ser chamado pelo "ator" tentando se autenticar.
- Esse método deve chamar o SessionManager para inicializar a sessão para este usuário, criando seu UUID já neste momento.
- O SessionManager chamará a interface SessionBackOperation para autenticar o usuário e saber se a sessão deve ser criada ou não.
- A implementação da SessionBackOperation deve lançar uma exception caso não valide o LogIn, ou retornar um objeto que implemente SessionVO, com os dados que o sistema julgar necessário constarem na sessão do usuário.
- Ao receber o SessionVO de retorno, o SessionManager entende que a autenticação correu bem e que registra o UUID como uma nova sessão válida, associando o SessionVO recebido à sessão.
- O SessionVO recebido do próprio sistema também é retornado pelo SessionManager e este objeto normalmente é retornado para o ator. Pois dentro dele constará o UUID que o ator precisará para chamar qualquer método que requeira uma autenticação.
![]() |
|
Autenticação nos Métodos
Por falta de uma maneira melhor, o SessionManager sempre procura o UUID no primeiro parâmetro do método. Isso quer dizer que todo método que exigir algum tipo de autenticação, obrigatoriamente deve ter seu primeiro parâmetro como String e deve ser usado para transportar o UUID. Caso método exija uma autenticação e não se detecte uma UUID válida no primeiro parâmetro do método, a autenticação será invalidada e o acesso não será permitido.
Definição de Acesso dos Métodos
Lembrando que SessionManager tem a finalidade de validar o acesso à métodos de Fachada. Dito isso, a definição de acesso dos métodos é realizado pela annotation Security.
A annotation permite definir alguns diferentes tipos de acesso:
SKIP - Sem Segurança
O tipo de segurança SKIP é utilizado quando não queremos que o SessionManager não faça nenhum tipo de autenticação ou verificação. Útil por exemplo para os próprios métodos de LogIn em que o usuário não tem nenhuma sessão válida ainda. Esse modo também é útil quando tempos alguns métodos de consulta pública ou que não requerem autenticação.
Por não exigir uma autenticação, os métodos com segurança SKIP são os únicos que não necessitam (nem faz sentido) ter o primeiro atributo como sendo String para o envio do UUID.
![]() |
|
![]() |
Exemplo Método sem Segurança
@Override
@Security(action = SecurityAction.SKIP)
public SessionVO doLogin(String user, String password, Locale locale) throws BISException {
return SessionManager.doLogin(user, password, locale);
}
|
HASSESSION - Exige usuário autenticado
Este modo requer que o usuário esteja autenticado. Embora não ofereça nenhum tipo de filtro de acesso por usuário, apenas que seja um usuário com uma sessão válida no sistema. Em outras palavras, ao utilizar esse método todos os usuários autenticados do sistema terão acesso. Permite acesso tando para UUID quando para TOKEN.
![]() |
|
![]() |
Exemplo Método com Exigência de Autenticação
@Override
@Security(action = SecurityAction.HASSESSION)
public LocalDateTime getSystemTime() {
return FW.getDateTime();
}
|
HASKEY - Exige uma chave específica
Este modo requer não apenas que o usuário tenha uma sessão válida, como requer também que o usuário tenha acesso a uma chave de acesso determinada. A chave de acesso deve ser definida no atributo key da annotation. E embora seja uma String, é recomendável que seja uma constante em alguma classe estática do sistema, de forma que a mesma constante seja usada tanto na fachada quando na montagem do "chaveiro de acesso" do usuário.
![]() |
Exemplo Método com Exigência de Autenticação
@Override
@Security(action = SecurityAction.HASKEY, key = System.SECKEY_DANCE)
public void danceInTheRain() {
...
}
|
HASTOKEN - Exige autenticação por TOKEN
Este modo requer apenas que o usuário tenha uma autenticação por Token, ou seja, ao invés de enviar um UUID no primeiro argumento, deve ser enviado um Token de autorização de máquina (API).
![]() |
Exemplo Método com Exigência de Autenticação
@Override
@Security(action = SecurityAction.HASTOKENSESSION)
public void apiMethod() {
...
}
|
Chaveiro de Acesso
Chamamos de chaveiro de acesso o conjunto de chaves que um usuário possui, ou que são atribuídas à uma sessão. Quando o SessionManager cria uma sessão e recebe do sistema uma implementação do objeto SessionVO, este tem dois método hasAccess().
- hasAccess(String seckey, Long objID) - Inclui além da chave de acesso um ID de objeto, que tem a finalidade de definir chave de acesso que dão permissão não apenas à uma ação, mas também a alguns objetos do sistema e outros não. Por exemplo imagine que o sistema tenha diferentes contas bancárias cadastradas, mas queremos que um usuário tenha acesso à uma e não a outra. Para esses casos utilizamos este método e a utilização de chaves dinâmicas. Este método não é utilizado pela validação automática da fachada, e a verificação de acesso deve ser realizada dentro do método.
- hasAccess(String key) - Este método por sua vez é chamado passando a chave do método. Se a implementação SessionVO retornar true é permitida a chamada do método, caso contrário é lançada uma exception por falta de acesso.
Interceptador da Fachada
Para que o SessionManager tenha acesso a intermediar a chamada da fachada precisamos incluir o SMInterceptor como interceptador da fachada. Para isso precisamos definir a seguinte annotation na classe da fachada do sistema:
@Interceptors(SMInterceptor.class)
Ao realizar esta declaração na classe da fachada, o Servidor de Aplicação chamará a classe do Interceptor antes de passar a chamada para a Fachada.
SMInterceptor
O SMInterceptor tem basicamente a função de testar a segurança da sessão antes de passar a chamada para os métodos e atua também como um verificador de RollBack. Sempre que o método da fachada retornar com uma exception, independente de qualquer tipo, ela forçará um Rollback de todas as ações controladas pelo container (EJB CMT).
![]() |
|
Autenticação por Token
A autenticação por Token foi criada para a validação de autenticação de máquina, como uma API, um acesso que não dependesse de usuário/senha.
Os tokens são String geradas pelo sistema da maneira que ele preferir, mas todas devem conter um prefixo, que permite que o SessionManager rapidamente distinga uma UUID do token. Recomenda-se algo curto (entre 3 e 6 letras) e um separador (como "-|=:>"). Ex: "SToken:", "TK=", etc.
Esse prefixo deve ser definido na inicialização do SessionManager e não deve mais ser trocado. Isso pq esse prefixo faz parte de todos os tokens informados nos sistemas externos. Trocar o prefixo fará com que todos os tokens dos sistemas externos se tornem inválidos.
Métodos Distintos para UUID e Token
A recomendação é que os métodos, mesmo que tenham a mesma função, tenham chamadas diferentes na fachada quando forem para acesso por UUID ou quando forem por TOKEN. Assim evitamos alguns problemas de alteração na fachada e garantimos os tipos de segurança independentes.
Obtendo a Sessão Corrente
Depois de passarmos pela fachada o SessionManager associa a thread em execução à sessão que foi passada pelo UUID. Em outras palavras, quando a fachada é chamada externamente o "ator" externo passará a UUID ou o Token conforme o tipo de segurança no método chamado. Uma vez que a autenticação foi aprovada, o SessionManager associará essa sessão à Thread corrente. Assim, para obter o objeto SessionVO de qualquer parte do sistema (depois de passada da fachada) basta utilizar o método:
SessionManager.getSession()
ou
SessionManager.getSessionIfExists()
A diferença entre um e outro é que o primeiro lança uma exception caso a Sessão não exista, já o segundo só retorna um 'null'. Apenas chamadas com o tipo de segurança 'SKIP' permitem que não se tenha uma sessão depois da fachada.
Execução em Paralelo e Lançamento de novas Threads
Como a vinculação da sessão é realiza na Thread atual, sempre que o desenvolvedor se utilizar do recurso de lançar novas Threads para execução em paralelo a sessão não estará disponível na nova Thread. Mesmo que o desenvolvedor não use diretamente o conteúdo da sessão, outras ferramentas fazem uso dela em background com o FWLogger, JobMonitor e até o BISDAO.
Para vincular a sessão atual nas novas Threads pode-se utilizar o método
SessionManager.attachSessionToThread(...)
Isso incluirá a sessão de autenticação na Thread e permitirá que os outros serviços encontre a sessão da mesma maneira como se estivesse aqui.
Para fazer o efeito contrário, desassociar uma sessão de uma Thread pode-se utilizar o método cleanThread(...). Embora isso possa ser útil em alguns casos, o SessionManager verifica temporariamente as Threads que estão vinculadas às sessões, e uma vez que elas apresentem o estado "State.TERMINATED", o próprio SessionManager removerá a referência para a Thread de forma a permitir que o GC colete essa Thread.