Com o post anterior provemos uma boa performance, garantindo que a mesma informação seja compartilhada entre todos os usuários de uma aplicação específica. Além disso, em posts anteriores analisamos como extrair os dados da fonte de dados mas, tudo o que fizemos até então, é a manipulação de dados, mas a regra para validar se o usuário atual possui ou não direito de acesso não foi definida.
Todas as funcionalidades que falamos no primeiro post desta série estão acopladas ao pipeline do ASP.NET através de módulos (IHttpModule). Se analisarmos o arquivo Web.config do servidor (aquele que está dentro do diretório do .NET Framework), veremos os módulos vinculados à execução da aplicação:
<httpModules>
<add
name=”WindowsAuthentication”
type=”System.Web.Security.WindowsAuthenticationModule”/>
<add
name=”FormsAuthentication”
type=”System.Web.Security.FormsAuthenticationModule”/>
<add
name=”RoleManager”
type=”System.Web.Security.RoleManagerModule”/>
<add
name=”UrlAuthorization”
type=”System.Web.Security.UrlAuthorizationModule”/>
<add
name=”FileAuthorization”
type=”System.Web.Security.FileAuthorizationModule”/>
</httpModules>
Os módulos de autenticação são executados de acordo com a opção escolhida no arquivo Web.config (Forms ou Windows). Para autorização, a classe HttpApplication fornece um evento chamado AuthorizeRequest, que é disparado quando chega o momento de determinar a autorização da página requisitada. Os módulos “assinam” esse evento, e são notificados quando ele ocorre e, com isso, executam as verificações necessárias para determinar se o usuário possui ou não os privilégios necessários para acessar a página solicitada.
A UrlAuthorization é a responsável por efetuar essa verificação levando em conta as políticas de acesso que colocamos no arquivo Web.config e, como sabemos, se alteradas forçam o processo do ASP.NET ser reciclado, o que queremos evitar. Como não vamos mais levar em conta as configurações de políticas de acesso que estão no arquivo de configuração, devemos remover este módulo da execução mas, antes disso, ainda é necessário criarmos o nosso.
DBAuthorizationModule é uma classe que implementa a Interface IHttpModule e também assina o evento AuthorizeRequest. No nosso caso, temos que avaliar se o usuário possui ou não privilégio de acesso consultando o provider anteriormente criado. O provider, por sua vez, irá retornar as políticas do cache ou efetuar a busca no banco de dados.
Depois que o provider devolver as políticas definidas, temos que aplicar a regra necessária para conceder ou negar o acesso para o usuário corrente. Mas antes de efetivamente executar essa tarefa, precisamos analisar a propriedade SkipAuthorization da classe HttpContext. Essa propriedade retorna um valor booleano indicando se a autorização deve ou não ser avaliada. Um exemplo disso é quando você solicita a página de Login. Essa página não deve sofrer nenhuma espécie de autorização, já que todos devem acessá-la. Caso essa propriedade retorne False, então devemos avaliar os privilégios do usuário. O código abaixo ilustra o módulo:
public class DBAuthorizationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.AuthorizeRequest += new EventHandler(OnEnter);
}
private void OnEnter(object sender, EventArgs e)
{
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
if (!context.SkipAuthorization)
{
if (!IsUserAllowed(context.User, context.Request.Path, context.Request.RequestType))
{
context.Response.StatusCode = 401;
context.Response.StatusDescription = “Unauthorized”;
application.CompleteRequest();
}
}
}
public static bool IsUserAllowed(IPrincipal principal, string requestedPath, string verb)
{
return DBAuthorization.GetAllRulesByPath(requestedPath).IsUserAllowed(principal, requestedPath, verb);
}
}
Para avaliar as políticas, criei um método estático chamado IsUserAllowed que recebe como parâmetro o usuário corrente, a página (path) requisitada e o verbo (GET ou POST). Esse método recorre ao provider para recuperar a coleção de regras definidas e, em seguida, invoca o método IsUserAllowed da classe DBAuthorizationRuleCollection informando os mesmos parâmetros. É dentro deste método que toda a “mágica” ocorre, e que por questões de espaço, não será exibida aqui.
Caso os métodos IsUserAllowed retornem False, definimos o código de status de HTTP como 401 (Unauthorized) e invocamos o método CompleteRequest da classe HttpApplication, que obriga o ASP.NET a dar um “bypass” em todos os eventos da classe HttpApplication, direcionando a requisição para o evento EndRequest.
Com o módulo criado, então é necessário adicioná-lo à execução. Mas antes disso, como não faremos uso da Url Authorization, precisamos também removê-la. Dentro do arquivo Web.config da aplicação temos uma seção chamada httpModules. Essa seção permite alistarmos todos os módulos que serão utilizados pela aplicação e, a remoção do módulo “UrlAuthorization” se faz necessária porque em runtime ele mescla os módulos do Web.config do servidor com o da aplicação. Abaixo temos a configuração necessária:
<httpModules>
<remove
name=”UrlAuthorization”/>
<add
name=”DBAuthorizationModule”
type=”ProjetandoNET.Web.Security.DBAuthorizationModule, ProjetandoNET”/>
</httpModules>
Note que o tipo (atributo type do elemento add) deve refletir o nome completo da classe, ou seja, incluindo os possíveis namespaces e também o nome do Assembly. As configurações do arquivo Web.config da aplicação não param por aí. Veremos mais detalhes no próximo post desta série.