SessionManager: mudanças entre as edições

De BIS Wiki
Ir para navegação Ir para pesquisar
Linha 118: Linha 118:
}
}
</syntaxhighlight>}}
</syntaxhighlight>}}
= 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:
<pre>@Interceptors(SMInterceptor.class)</pre>
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).

Edição das 19h29min de 16 de outubro de 2020

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:

  1. 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.
  2. Esse método deve chamar o SessionManager para inicializar a sessão para este usuário, criando seu UUID já neste momento.
  3. O SessionManager chamará a interface SessionBackOperation para autenticar o usuário e saber se a sessão deve ser criada ou não.
  4. 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.
  5. 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.
  6. 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.


Fluxo de Autenticação por Usuário/Senha
O fluxo de autenticação citado acima é referente apenas para o LogIn através do par usuário/senha. Autenticação feita por Token não exige a ação do Login.

Embora ao receber um Token o SessionManager cria uma sessão válida com um UUID válido vinculado, normalmente os métodos destinados para comunicação diretamente com outra máquinas passam sempre o Token como primeiro método e não o UUID.

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.


Métodos Públicos em Fachada Diferente
Perceba que por questões de segurança, quando se trata de métodos realmente publicos para acesso em massa de desconhecidos e não de sistemas controlados o mais prudente é ter uma fachada diferente da principal com os métodos de manipulação e acesso ao sistema.


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.


HASSESSION é o Nível de Segurança Padrão
Quando a annotation Security não é definida em um método de fachada que esteja sendo controlada pelo SessionManager, a segurança padrão definida no método será a HASSESSION. Ou seja, qualquer método sem nenhum tipo de anotação de segurança exige que o usuário esteja no mínimo autenticado, e consequentemente deve ter no seu primeiro parâmetro o UUID.


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).