Autorização baseada em usuário (C#)

por Scott Mitchell

Observação

Desde que este artigo foi escrito, os provedores de associação ASP.NET foram substituídos pelo ASP.NET Identity. É altamente recomendável atualizar aplicativos para usar a plataforma ASP.NET Identity em vez dos provedores de associação apresentados no momento em que este artigo foi escrito. ASP.NET Identity tem várias vantagens em relação ao sistema de associação ASP.NET, incluindo :

  • Melhor desempenho
  • Extensibilidade e testabilidade aprimoradas
  • Suporte para OAuth, OpenID Connect e autenticação de dois fatores
  • Suporte à identidade baseada em declarações
  • Melhor interoperabilidade com o ASP.Net Core

Baixar código ou baixar PDF

Neste tutorial, examinaremos a limitação do acesso às páginas e a restrição da funcionalidade no nível da página por meio de uma variedade de técnicas.

Introdução

A maioria dos aplicativos Web que oferecem contas de usuário faz isso em parte para impedir que determinados visitantes acessem determinadas páginas dentro do site. Na maioria dos sites de quadro de mensagens online, por exemplo, todos os usuários - anônimos e autenticados - são capazes de exibir as postagens do quadro de mensagens, mas apenas usuários autenticados podem visitar a página da Web para criar uma nova postagem. E pode haver páginas administrativas que só podem ser acessíveis a um usuário específico (ou a um determinado conjunto de usuários). Além disso, a funcionalidade no nível da página pode ser diferente de usuário por usuário. Ao exibir uma lista de postagens, os usuários autenticados são mostrados como uma interface para classificação de cada postagem, enquanto essa interface não está disponível para visitantes anônimos.

ASP.NET facilita a definição de regras de autorização baseadas no usuário. Com apenas um pouco de marcação no Web.config, páginas da Web específicas ou diretórios inteiros podem ser bloqueados para que sejam acessíveis apenas a um subconjunto especificado de usuários. A funcionalidade no nível da página pode ser ativada ou desativada com base no usuário conectado no momento por meio de meios programáticos e declarativos.

Neste tutorial, examinaremos a limitação do acesso às páginas e a restrição da funcionalidade no nível da página por meio de uma variedade de técnicas. Vamos começar!

Uma olhada no fluxo de trabalho de autorização de URL

Conforme discutido no tutorial Uma Visão geral da Autenticação de Formulários , quando o runtime do ASP.NET processa uma solicitação de um recurso de ASP.NET, a solicitação gera vários eventos durante seu ciclo de vida. Módulos HTTP são classes gerenciadas cujo código é executado em resposta a um evento específico no ciclo de vida da solicitação. ASP.NET é fornecido com vários módulos HTTP que executam tarefas essenciais nos bastidores.

Um desses módulos HTTP é FormsAuthenticationModule. Conforme discutido nos tutoriais anteriores, a função principal do FormsAuthenticationModule é determinar a identidade da solicitação atual. Isso é feito inspecionando o tíquete de autenticação de formulários, que está localizado em um cookie ou inserido na URL. Essa identificação ocorre durante o AuthenticateRequest evento.

Outro módulo HTTP importante é o UrlAuthorizationModule, que é gerado em resposta ao evento (que AuthorizeRequest ocorre após o AuthenticateRequest evento). O UrlAuthorizationModule examina a marcação de configuração em Web.config para determinar se a identidade atual tem autoridade para visitar a página especificada. Esse processo é conhecido como autorização de URL.

Examinaremos a sintaxe das regras de autorização de URL na Etapa 1, mas primeiro vamos examinar o que o UrlAuthorizationModule faz dependendo se a solicitação está autorizada ou não. Se o UrlAuthorizationModule determinar que a solicitação está autorizada, ela não fará nada e a solicitação continuará durante seu ciclo de vida. No entanto, se a solicitação não estiver autorizada, o UrlAuthorizationModule anulará o ciclo de vida e instruirá o Response objeto a retornar um HTTP 401 Não autorizado status. Ao usar a autenticação de formulários, este status HTTP 401 nunca é retornado ao cliente porque se o FormsAuthenticationModule detectar um HTTP 401 status o modificará para um Redirecionamento HTTP 302 para a página de logon.

A Figura 1 ilustra o fluxo de trabalho do pipeline de ASP.NET, o FormsAuthenticationModulee o UrlAuthorizationModule quando uma solicitação não autorizada chega. Em particular, a Figura 1 mostra uma solicitação de um visitante anônimo para ProtectedPage.aspx, que é uma página que nega acesso a usuários anônimos. Como o visitante é anônimo, o UrlAuthorizationModule anula a solicitação e retorna um http 401 não autorizado status. Em FormsAuthenticationModule seguida, o converte o status 401 em uma página redirecionamento 302 para logon. Depois que o usuário é autenticado por meio da página de logon, ele é redirecionado para ProtectedPage.aspx. Desta vez, o FormsAuthenticationModule identifica o usuário com base em seu tíquete de autenticação. Agora que o visitante está autenticado, o permite o UrlAuthorizationModule acesso à página.

O fluxo de trabalho de autenticação de formulários e autorização de URL

Figura 1: o fluxo de trabalho de autenticação de formulários e autorização de URL (clique para exibir a imagem em tamanho real)

A Figura 1 descreve a interação que ocorre quando um visitante anônimo tenta acessar um recurso que não está disponível para usuários anônimos. Nesse caso, o visitante anônimo é redirecionado para a página de logon com a página que ela tentou visitar especificada na querystring. Depois que o usuário tiver feito logon com êxito, ela será redirecionada automaticamente de volta para o recurso que estava inicialmente tentando exibir.

Quando a solicitação não autorizada é feita por um usuário anônimo, esse fluxo de trabalho é simples e é fácil para o visitante entender o que aconteceu e por quê. Mas tenha em mente que o FormsAuthenticationModule redirecionará qualquer usuário não autorizado para a página de logon, mesmo que a solicitação seja feita por um usuário autenticado. Isso pode resultar em uma experiência confusa do usuário se um usuário autenticado tentar visitar uma página para a qual ela não tem autoridade.

Imagine que nosso site tinha suas regras de autorização de URL configuradas de modo que a página OnlyTito.aspx ASP.NET fosse acessada apenas para Tito. Agora, imagine que Sam visite o site, faça logon e tente visitar OnlyTito.aspx. O UrlAuthorizationModule interromperá o ciclo de vida da solicitação e retornará um status HTTP 401 Não autorizado, que o FormsAuthenticationModule detectará e redirecionará Sam para a página de logon. Como Sam já se conectou, no entanto, ela pode se perguntar por que foi enviada de volta para a página de logon. Ela pode argumentar que suas credenciais de logon foram perdidas de alguma forma ou que ela inseriu credenciais inválidas. Se Sam receber novamente suas credenciais da página de logon, ela será conectada (novamente) e redirecionada para OnlyTito.aspx. O UrlAuthorizationModule detectará que Sam não pode visitar esta página e ela será retornada para a página de logon.

A Figura 2 ilustra esse fluxo de trabalho confuso.

O fluxo de trabalho padrão pode levar a um ciclo confuso

Figura 2: O fluxo de trabalho padrão pode levar a um ciclo confuso (clique para exibir a imagem em tamanho real)

O fluxo de trabalho ilustrado na Figura 2 pode rapidamente confundir até mesmo o visitante mais experiente do computador. Examinaremos maneiras de evitar esse ciclo confuso na Etapa 2.

Observação

ASP.NET usa dois mecanismos para determinar se o usuário atual pode acessar uma página da Web específica: autorização de URL e autorização de arquivo. A autorização de arquivo é implementada pelo , que determina a FileAuthorizationModuleautoridade consultando as ACLs de arquivo(s) solicitadas. A autorização de arquivo é mais comumente usada com autenticação do Windows porque as ACLs são permissões que se aplicam a contas do Windows. Ao usar a autenticação de formulários, todas as solicitações no nível do sistema operacional e do sistema de arquivos são executadas pela mesma conta do Windows, independentemente do usuário que estiver visitando o site. Como esta série de tutoriais se concentra na autenticação de formulários, não discutiremos a autorização de arquivo.

O escopo da autorização de URL

O UrlAuthorizationModule é um código gerenciado que faz parte do runtime do ASP.NET. Antes da versão 7 do servidor Web do IIS (Serviços de Informações da Internet) da Microsoft, havia uma barreira distinta entre o pipeline HTTP do IIS e o pipeline do ASP.NET runtime. Resumindo, no IIS 6 e anterior, ASP. O do UrlAuthorizationModule NET só é executado quando uma solicitação é delegada do IIS para o runtime do ASP.NET. Por padrão, o IIS processa o próprio conteúdo estático , como páginas HTML e CSS, JavaScript e arquivos de imagem , e apenas entrega solicitações para o runtime ASP.NET quando uma página com uma extensão de .aspx, .asmxou .ashx é solicitada.

No entanto, o IIS 7 permite pipelines IIS e ASP.NET integrados. Com algumas definições de configuração, você pode configurar o IIS 7 para invocar o para todas as solicitações, o UrlAuthorizationModule que significa que as regras de autorização de URL podem ser definidas para arquivos de qualquer tipo. Além disso, o IIS 7 inclui seu próprio mecanismo de autorização de URL. Para obter mais informações sobre ASP.NET integração e a funcionalidade de autorização de URL nativa do IIS 7, consulte Noções básicas sobre a autorização de URL do IIS7. Para ver mais detalhadamente ASP.NET e integração do IIS 7, escolha uma cópia do livro de Shahram Khosravi, Professional IIS 7 e programação integrada ASP.NET (ISBN: 978-0470152539).

Em poucas palavras, em versões anteriores ao IIS 7, as regras de autorização de URL só são aplicadas aos recursos manipulados pelo runtime do ASP.NET. Mas com o IIS 7 é possível usar o recurso de autorização de URL nativa do IIS ou integrar o ASP. O NET está UrlAuthorizationModule no pipeline HTTP do IIS, estendendo essa funcionalidade para todas as solicitações.

Observação

Há algumas diferenças sutis, mas importantes, em como o ASP. O recurso de UrlAuthorizationModule autorização de URL do NET e do IIS 7 processa as regras de autorização. Este tutorial não examina a funcionalidade de autorização de URL do IIS 7 ou as diferenças na forma como analisa as regras de autorização em comparação com o UrlAuthorizationModule. Para obter mais informações sobre esses tópicos, consulte a documentação do IIS 7 no MSDN ou em www.iis.net.

Etapa 1: Definindo regras de autorização de URL emWeb.config

O UrlAuthorizationModule determina se é necessário conceder ou negar acesso a um recurso solicitado para uma identidade específica com base nas regras de autorização de URL definidas na configuração do aplicativo. As regras de autorização são escritas no <authorization> elemento na forma de <allow> e <deny> elementos filho. Cada <allow> elemento filho e <deny> pode especificar:

  • Um usuário específico
  • Uma lista delimitada por vírgulas de usuários
  • Todos os usuários anônimos, indicados por um ponto de interrogação (?)
  • Todos os usuários, indicados por um asterisco (*)

A marcação a seguir ilustra como usar as regras de autorização de URL para permitir que os usuários Tito e Scott neguem todos os outros:

<authorization>
 <allow users="Tito, Scott" />
 <deny users="*" />
</authorization>

O <allow> elemento define quais usuários são permitidos - Tito e Scott - enquanto o <deny> elemento instrui que todos os usuários são negados.

Observação

Os <allow> elementos e <deny> também podem especificar regras de autorização para funções. Examinaremos a autorização baseada em função em um tutorial futuro.

A configuração a seguir concede acesso a qualquer pessoa que não seja Sam (incluindo visitantes anônimos):

<authorization>
 <deny users="Sam" />
</authorization>

Para permitir apenas usuários autenticados, use a seguinte configuração, que nega acesso a todos os usuários anônimos:

<authorization>
 <deny users="?" />
</authorization>

As regras de autorização são definidas dentro do <system.web> elemento em Web.config e se aplicam a todos os recursos ASP.NET no aplicativo Web. Geralmente, um aplicativo tem regras de autorização diferentes para seções diferentes. Por exemplo, em um site de comércio eletrônico, todos os visitantes podem usar os produtos, ver revisões de produtos, pesquisar o catálogo e assim por diante. No entanto, somente usuários autenticados podem acessar o check-out ou as páginas para gerenciar o histórico de envio. Além disso, pode haver partes do site que só podem ser acessadas por usuários selecionados, como administradores de site.

ASP.NET facilita a definição de regras de autorização diferentes para arquivos e pastas diferentes no site. As regras de autorização especificadas no arquivo da Web.config pasta raiz se aplicam a todos os recursos ASP.NET no site. No entanto, essas configurações de autorização padrão podem ser substituídas para uma pasta específica adicionando uma Web.config com uma <authorization> seção.

Vamos atualizar nosso site para que apenas usuários autenticados possam visitar as páginas ASP.NET na Membership pasta. Para fazer isso, precisamos adicionar um Web.config arquivo à Membership pasta e definir suas configurações de autorização para negar usuários anônimos. Clique com o botão direito do Membership mouse na pasta no Gerenciador de Soluções, escolha o menu Adicionar Novo Item no menu de contexto e adicione um novo Arquivo de Configuração da Web chamado Web.config.

Adicionar um arquivo de Web.config à pasta de associação

Figura 3: Adicionar um Web.config arquivo à Membership pasta (clique para exibir a imagem em tamanho real)

Neste ponto, seu projeto deve conter dois Web.config arquivos: um no diretório raiz e outro na Membership pasta.

Seu aplicativo agora deve conter dois arquivos Web.config

Figura 4: seu aplicativo agora deve conter dois Web.config arquivos (clique para exibir a imagem em tamanho real)

Atualize o arquivo de configuração na pasta para que ele proíba o Membership acesso a usuários anônimos.

<?xml version="1.0"?>
<configuration>
 <system.web>
 <authorization>
 <deny users="?" />
 </authorization>
 </system.web>
</configuration>

Isso é tudo!

Para testar essa alteração, visite a home page em um navegador e verifique se você está conectado. Como o comportamento padrão de um aplicativo ASP.NET é permitir todos os visitantes e, como não fizemos nenhuma modificação de autorização no arquivo do Web.config diretório raiz, podemos visitar os arquivos no diretório raiz como um visitante anônimo.

Clique no link Criando Contas de Usuário encontrado na coluna à esquerda. Isso levará você ao ~/Membership/CreatingUserAccounts.aspx. Como o Web.config arquivo na Membership pasta define regras de autorização para proibir o acesso anônimo, o UrlAuthorizationModule anula a solicitação e retorna uma status HTTP 401 Não autorizada. O FormsAuthenticationModule modifica isso para um status de Redirecionamento 302, enviando-nos para a página de logon. Observe que a página que estávamos tentando acessar (CreatingUserAccounts.aspx) é passada para a página de logon por meio do ReturnUrl parâmetro querystring.

Como as regras de autorização de URL proíbem o acesso anônimo, somos redirecionados para a página de logon

Figura 5: como as regras de autorização de URL proíbem o acesso anônimo, somos redirecionados para a página de logon (clique para exibir a imagem em tamanho real)

Após fazer logon com êxito, somos redirecionados para a CreatingUserAccounts.aspx página. Desta vez, o UrlAuthorizationModule permite o acesso à página porque não somos mais anônimos.

Aplicando regras de autorização de URL a um local específico

As configurações de autorização definidas na <system.web> seção de Web.config aplicam-se a todos os recursos ASP.NET nesse diretório e seus subdiretórios (até que seja substituído por outro Web.config arquivo). Em alguns casos, porém, talvez queiramos que todos os recursos ASP.NET em um determinado diretório tenham uma configuração de autorização específica, exceto uma ou duas páginas específicas. Isso pode ser obtido adicionando um <location> elemento em Web.config, apontando-o para o arquivo cujas regras de autorização diferem e definindo suas regras de autorização exclusivas nela.

Para ilustrar o uso do <location> elemento para substituir as configurações de um recurso específico, vamos personalizar as configurações de autorização para que apenas Tito possa visitar CreatingUserAccounts.aspx. Para fazer isso, adicione um <location> elemento ao Membership arquivo da Web.config pasta e atualize sua marcação para que ele se pareça com o seguinte:

<?xml version="1.0"?>
<configuration>
 <system.web>
 <authorization>
 <deny users="?" />
 </authorization>
 </system.web>

 <location path="CreatingUserAccounts.aspx">
 <system.web>
 <authorization>
 <allow users="Tito" />
 <deny users="*" />
 </authorization>
 </system.web>
 </location>
</configuration>

O <authorization> elemento em <system.web> define as regras de autorização de URL padrão para recursos de ASP.NET na Membership pasta e suas subpastas. O <location> elemento nos permite substituir essas regras por um recurso específico. Na marcação acima, o <location> elemento faz referência à CreatingUserAccounts.aspx página e especifica suas regras de autorização, como permitir Tito, mas negar todos os outros.

Para testar essa alteração de autorização, comece visitando o site como um usuário anônimo. Se você tentar visitar qualquer página na Membership pasta, como UserBasedAuthorization.aspx, o UrlAuthorizationModule negará a solicitação e você será redirecionado para a página de logon. Depois de fazer logon como, digamos, Scott, você pode visitar qualquer página na Membership pasta , exceto para CreatingUserAccounts.aspx. Tentar visitar CreatingUserAccounts.aspx o logon como qualquer pessoa, mas o Tito resultará em uma tentativa de acesso não autorizada, redirecionando você de volta para a página de logon.

Observação

O <location> elemento deve aparecer fora do elemento da <system.web> configuração. Você precisa usar um elemento separado <location> para cada recurso cujas configurações de autorização você deseja substituir.

Uma olhada em como o usa asUrlAuthorizationModuleregras de autorização para conceder ou negar acesso

O UrlAuthorizationModule determina se uma identidade específica deve ser autorizada para uma URL específica analisando as regras de autorização de URL uma de cada vez, começando da primeira e trabalhando para baixo. Assim que uma correspondência for encontrada, o usuário receberá ou negará acesso, dependendo se a correspondência tiver sido encontrada em um <allow> elemento ou <deny> . Se nenhuma correspondência for encontrada, o usuário receberá acesso. Consequentemente, se você quiser restringir o acesso, é imperativo que você use um <deny> elemento como o último elemento na configuração de autorização de URL. Se você omitir um<deny>elemento , todos os usuários receberão acesso.

Para entender melhor o processo usado pelo para determinar a UrlAuthorizationModule autoridade, considere as regras de autorização de URL de exemplo que examinamos anteriormente nesta etapa. A primeira regra é um <allow> elemento que permite o acesso a Tito e Scott. A segunda regra é um <deny> elemento que nega acesso a todos. Se um usuário anônimo visita, o UrlAuthorizationModule começa perguntando, Scott ou Tito anônimo? A resposta, obviamente, é Não, portanto, ela prossegue para a segunda regra. É anônimo no conjunto de todos? Como a resposta aqui é Sim, a <deny> regra é colocada em vigor e o visitante é redirecionado para a página de logon. Da mesma forma, se Jisun está visitando, o UrlAuthorizationModule começa perguntando, Jisun é Scott ou Tito? Como ela não está, a UrlAuthorizationModule continuação para a segunda pergunta, Jisun está no conjunto de todos? Ela é, então ela, também, é negado acesso. Por fim, se Tito visitar, a primeira pergunta feita pelo UrlAuthorizationModule será uma resposta afirmativa, portanto, Tito receberá acesso.

Como o UrlAuthorizationModule processa as regras de autorização de cima para baixo, parando em qualquer correspondência, é importante que as regras mais específicas venham antes das menos específicas. Ou seja, para definir regras de autorização que proíbem Jisun e usuários anônimos, mas permitem todos os outros usuários autenticados, você começa com a regra mais específica - aquela que afeta o Jisun - e, em seguida, prossiga para as regras menos específicas - aquelas que permitem todos os outros usuários autenticados, mas negam todos os usuários anônimos. As regras de autorização de URL a seguir implementam essa política negando primeiro o Jisun e negando qualquer usuário anônimo. Qualquer usuário autenticado diferente do Jisun receberá acesso porque nenhuma dessas <deny> instruções corresponderá.

<authorization>
 <deny users="Jisun" />
 <deny users="?" />
</authorization>

Etapa 2: Corrigir o fluxo de trabalho para usuários não autorizados e autenticados

Como discutimos anteriormente neste tutorial na seção A Look at the URL Authorization Workflow, sempre que uma solicitação não autorizada ocorre, a UrlAuthorizationModule anula a solicitação e retorna uma status HTTP 401 Não autorizada. Este status 401 é modificado pelo FormsAuthenticationModule em um status de redirecionamento 302 que envia o usuário para a página de logon. Esse fluxo de trabalho ocorre em qualquer solicitação não autorizada, mesmo que o usuário seja autenticado.

Retornar um usuário autenticado para a página de logon provavelmente o confundirá, pois ele já fez logon no sistema. Com um pouco de trabalho, podemos melhorar esse fluxo de trabalho redirecionando usuários autenticados que fazem solicitações não autorizadas para uma página que explica que eles tentaram acessar uma página restrita.

Comece criando uma nova página ASP.NET na pasta raiz do aplicativo Web chamada UnauthorizedAccess.aspx; não se esqueça de associar essa página à Site.master página master. Depois de criar esta página, remova o controle Conteúdo que faz referência ao LoginContent ContentPlaceHolder para que o conteúdo padrão da página master seja exibido. Em seguida, adicione uma mensagem que explique a situação, ou seja, que o usuário tentou acessar um recurso protegido. Depois de adicionar essa mensagem, a UnauthorizedAccess.aspx marcação declarativa da página deve ser semelhante à seguinte:

<%@ Page Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
CodeFile="UnauthorizedAccess.aspx.cs" Inherits="UnauthorizedAccess"
Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent"
Runat="Server">
 <h2>Unauthorized Access</h2>
 <p>
 You have attempted to access a page that you are not authorized to view.
 </p>
 <p>
 If you have any questions, please contact the site administrator.
 </p>
</asp:Content>

Agora precisamos alterar o fluxo de trabalho para que, se uma solicitação não autorizada for executada por um usuário autenticado, ela seja enviada para a UnauthorizedAccess.aspx página em vez da página de logon. A lógica que redireciona solicitações não autorizadas para a página de logon é enterrada dentro de um método privado da FormsAuthenticationModule classe, portanto, não é possível personalizar esse comportamento. No entanto, o que podemos fazer é adicionar nossa própria lógica à página de logon que redireciona o usuário para UnauthorizedAccess.aspx, se necessário.

Quando o FormsAuthenticationModule redireciona um visitante não autorizado para a página de logon, ele acrescenta a URL solicitada e não autorizada à querystring com o nome ReturnUrl. Por exemplo, se um usuário não autorizado tentasse visitar OnlyTito.aspx, o o FormsAuthenticationModule redirecionaria para Login.aspx?ReturnUrl=OnlyTito.aspx. Portanto, se a página de logon for acessada por um usuário autenticado com uma querystring que inclui o ReturnUrl parâmetro , sabemos que esse usuário não autenticado apenas tentou visitar uma página que ela não está autorizada a exibir. Nesse caso, queremos redirecioná-la para UnauthorizedAccess.aspx.

Para fazer isso, adicione o seguinte código ao manipulador de eventos da página de Page_Load logon:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        if (Request.IsAuthenticated && !string.IsNullOrEmpty(Request.QueryString["ReturnUrl"]))
        // This is an unauthorized, authenticated request...
        Response.Redirect("~/UnauthorizedAccess.aspx");
    }
}

O código acima redireciona usuários autenticados e não autorizados para a UnauthorizedAccess.aspx página. Para ver essa lógica em ação, visite o site como um visitante anônimo e clique no link Criando Contas de Usuário na coluna à esquerda. Isso levará você para a ~/Membership/CreatingUserAccounts.aspx página, que na Etapa 1 configuramos para permitir apenas o acesso ao Tito. Como os usuários anônimos são proibidos, o FormsAuthenticationModule nos redireciona de volta para a página de logon.

Neste ponto, somos anônimos, portanto Request.IsAuthenticated , retorna false e não somos redirecionados para UnauthorizedAccess.aspx. Em vez disso, a página de logon é exibida. Faça logon como um usuário diferente de Tito, como Bruce. Depois de inserir as credenciais apropriadas, a página de logon nos redireciona de volta para ~/Membership/CreatingUserAccounts.aspx. No entanto, como essa página só é acessível ao Tito, não estamos autorizados a exibi-la e retornamos prontamente à página de logon. Desta vez, no entanto, Request.IsAuthenticated retorna true (e o ReturnUrl parâmetro querystring existe), portanto, somos redirecionados para a UnauthorizedAccess.aspx página.

Usuários autenticados e não autorizados são redirecionados para UnauthorizedAccess.aspx

Figura 6: Usuários autenticados e não autorizados são redirecionados para UnauthorizedAccess.aspx (clique para exibir imagem em tamanho real)

Esse fluxo de trabalho personalizado apresenta uma experiência de usuário mais sensata e simples, causando um curto-circuito no ciclo descrito na Figura 2.

Etapa 3: Limitando a funcionalidade com base no usuário conectado no momento

A autorização de URL facilita a especificação de regras de autorização grosseiras. Como vimos na Etapa 1, com autorização de URL, podemos declarar de forma sucinta quais identidades são permitidas e quais são negadas de exibir uma página específica ou todas as páginas em uma pasta. Em determinados cenários, no entanto, talvez queiramos permitir que todos os usuários acessem uma página, mas limitem a funcionalidade da página com base no usuário que a visita.

Considere o caso de um site do eCommerce que permite que os visitantes autenticados revisem seus produtos. Quando um usuário anônimo visita a página de um produto, ele vê apenas as informações do produto e não teria a oportunidade de sair de uma revisão. No entanto, um usuário autenticado que visita a mesma página veria a interface de revisão. Se o usuário autenticado ainda não tiver revisado este produto, a interface permitirá que ele envie uma revisão; caso contrário, mostraria a revisão enviada anteriormente. Para levar esse cenário um passo adiante, a página do produto pode mostrar informações adicionais e oferecer recursos estendidos para os usuários que trabalham para a empresa de comércio eletrônico. Por exemplo, a página do produto pode listar o inventário em estoque e incluir opções para editar o preço e a descrição do produto quando visitado por um funcionário.

Essas regras de autorização de granularidade fina podem ser implementadas de forma declarativa ou programática (ou por meio de alguma combinação dos dois). Na próxima seção, veremos como implementar a autorização de granularidade fina por meio do controle LoginView. Depois disso, exploraremos técnicas programáticas. Antes de podermos examinar a aplicação de regras de autorização de granularidade finas, no entanto, primeiro precisamos criar uma página cuja funcionalidade depende do usuário visitá-la.

Vamos criar uma página que lista os arquivos em um diretório específico em um GridView. Além de listar o nome, o tamanho e outras informações de cada arquivo, o GridView incluirá duas colunas de LinkButtons: uma intitulada Exibição e outra intitulada Excluir. Se o Link De exibiçãoButton for clicado, o conteúdo do arquivo selecionado será exibido; se a opção Excluir LinkButton for clicada, o arquivo será excluído. Vamos criar inicialmente esta página de modo que sua funcionalidade de exibição e exclusão esteja disponível para todos os usuários. Nas seções Usando o controle LoginView e limitando programaticamente a funcionalidade, veremos como habilitar ou desabilitar esses recursos com base no usuário que visita a página.

Observação

A página ASP.NET que estamos prestes a criar usa um controle GridView para exibir uma lista de arquivos. Como esta série de tutoriais se concentra na autenticação de formulários, autorização, contas de usuário e funções, não quero gastar muito tempo discutindo o funcionamento interno do controle GridView. Embora este tutorial forneça instruções passo a passo específicas para configurar esta página, ele não se aprofunda nos detalhes de por que determinadas escolhas foram feitas ou que efeito determinadas propriedades têm na saída renderizada. Para obter um exame detalhado do controle GridView, consulte minha série de tutoriais Trabalhando com Dados no ASP.NET 2.0 .

Comece abrindo o UserBasedAuthorization.aspx arquivo na Membership pasta e adicionando um controle GridView à página chamada FilesGrid. Na Marca Inteligente do GridView, clique no link Editar Colunas para iniciar a caixa de diálogo Campos. A partir daqui, desmarque a caixa de seleção Gerar campos automaticamente no canto inferior esquerdo. Em seguida, adicione um botão Selecionar, um botão Excluir e dois BoundFields no canto superior esquerdo (os botões Selecionar e Excluir podem ser encontrados no tipo CommandField). Defina a propriedade do SelectText botão Selecionar como Exibir e as propriedades e DataField do HeaderText primeiro BoundField como Nome. Defina a segunda propriedade de HeaderText BoundField como Size em Bytes, sua DataField propriedade como Length, sua DataFormatString propriedade como {0:N0} e sua HtmlEncode propriedade como False.

Depois de configurar as colunas do GridView, clique em OK para fechar a caixa de diálogo Campos. No janela Propriedades, defina a propriedade gridView DataKeyNames como FullName. Neste ponto, a marcação declarativa do GridView deve ser semelhante à seguinte:

<asp:GridView ID="FilesGrid" DataKeyNames="FullName" runat="server" AutoGenerateColumns="False">
 <Columns>
 <asp:CommandField SelectText="View" ShowSelectButton="True"/>
 <asp:CommandField ShowDeleteButton="True" />
 <asp:BoundField DataField="Name" HeaderText="Name" />
 <asp:BoundField DataField="Length" DataFormatString="{0:N0}"
 HeaderText="Size in Bytes" HtmlEncode="False" />
 </Columns>
</asp:GridView>

Com a marcação do GridView criada, estamos prontos para gravar o código que recuperará os arquivos em um diretório específico e os associará ao GridView. Adicione o seguinte código ao manipulador de eventos da Page_Load página:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        string appPath = Request.PhysicalApplicationPath;
        DirectoryInfo dirInfo = new DirectoryInfo(appPath);

        FileInfo[] files = dirInfo.GetFiles();

        FilesGrid.DataSource = files;
        FilesGrid.DataBind();
    }
}

O código acima usa a DirectoryInfo classe para obter uma lista dos arquivos na pasta raiz do aplicativo. O GetFiles() método retorna todos os arquivos no diretório como uma matriz de FileInfo objetos, que é então associado ao GridView. O FileInfo objeto tem uma variedade de propriedades, como Name, Lengthe IsReadOnly, entre outras. Como você pode ver em sua marcação declarativa, o GridView exibe apenas as Name propriedades e Length .

Observação

As DirectoryInfo classes e FileInfo são encontradas no System.IO namespace. Portanto, você precisará preceder esses nomes de classe com seus nomes de namespace ou ter o namespace importado para o arquivo de classe (por meio using System.IOde ).

Reserve um momento para visitar esta página por meio de um navegador. Ele exibirá a lista de arquivos que residem no diretório raiz do aplicativo. Clicar em qualquer um dos LinkButtons de Exibição ou Exclusão causará um postback, mas nenhuma ação ocorrerá porque ainda não criamos os manipuladores de eventos necessários.

O GridView lista os arquivos no diretório raiz do aplicativo Web

Figura 7: o GridView lista os arquivos no diretório raiz do aplicativo Web (clique para exibir a imagem em tamanho real)

Precisamos de um meio para exibir o conteúdo do arquivo selecionado. Retorne ao Visual Studio e adicione uma Caixa de Texto chamada FileContents acima de GridView. Defina sua TextMode propriedade como MultiLine e suas Columns propriedades e Rows como 95% e 10, respectivamente.

<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>

Em seguida, crie um manipulador de eventos para o evento gridView SelectedIndexChanged e adicione o seguinte código:

protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
    // Open the file and display it
    string fullFileName = FilesGrid.SelectedValue.ToString();
    string contents = File.ReadAllText(fullFileName);
    FileContents.Text = contents;
}

Esse código usa a propriedade GridView SelectedValue para determinar o nome completo do arquivo selecionado. Internamente, a DataKeys coleção é referenciada para obter o SelectedValue, portanto, é imperativo que você defina a propriedade gridView DataKeyNames como Name, conforme descrito anteriormente nesta etapa. A File classe é usada para ler o conteúdo do arquivo selecionado em uma cadeia de caracteres, que é atribuída à FileContents propriedade TextBox Text , exibindo assim o conteúdo do arquivo selecionado na página.

O conteúdo do arquivo selecionado é exibido na Caixa de Texto

Figura 8: o conteúdo do arquivo selecionado é exibido na Caixa de Texto (Clique para exibir a imagem em tamanho real)

Observação

Se você exibir o conteúdo de um arquivo que contém marcação HTML e tentar exibir ou excluir um arquivo, receberá um HttpRequestValidationException erro. Isso ocorre porque, no postback, o conteúdo do TextBox é enviado de volta para o servidor Web. Por padrão, ASP.NET gera um HttpRequestValidationException erro sempre que conteúdo de postback potencialmente perigoso, como marcação HTML, é detectado. Para desabilitar a ocorrência desse erro, desative a validação de solicitação para a página adicionando ValidateRequest="false" à @Page diretiva . Para obter mais informações sobre os benefícios da validação da solicitação, bem como quais precauções você deve tomar ao desabilitá-la, leia Validação de solicitação – Prevenção de ataques de script.

Por fim, adicione um manipulador de eventos com o seguinte código para o evento gridviewRowDeleting:

protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    string fullFileName = FilesGrid.DataKeys[e.RowIndex].Value.ToString();
    FileContents.Text = string.Format("You have opted to delete {0}.", fullFileName);

    // To actually delete the file, uncomment the following line
    // File.Delete(fullFileName);
}

O código simplesmente exibe o nome completo do arquivo a ser excluído na FileContents TextBox sem realmente excluir o arquivo.

Clicar no botão Excluir não exclui o arquivo de fato

Figura 9: Clicar no botão Excluir não exclui o arquivo (clique para exibir a imagem em tamanho real)

Na Etapa 1, configuramos as regras de autorização de URL para proibir que usuários anônimos visualizem as páginas na Membership pasta. Para exibir melhor a autenticação de granularidade fina, vamos permitir que usuários anônimos visitem a UserBasedAuthorization.aspx página, mas com funcionalidade limitada. Para abrir esta página para ser acessada por todos os usuários, adicione o seguinte <location> elemento ao Web.config arquivo na Membership pasta :

<location path="UserBasedAuthorization.aspx">
 <system.web>
 <authorization>
 <allow users="*" />
 </authorization>
 </system.web>
</location>

Depois de adicionar esse <location> elemento, teste as novas regras de autorização de URL fazendo logoff do site. Como um usuário anônimo, você deve ter permissão para visitar a UserBasedAuthorization.aspx página.

Atualmente, qualquer usuário autenticado ou anônimo pode visitar a UserBasedAuthorization.aspx página e exibir ou excluir arquivos. Vamos torná-lo para que apenas usuários autenticados possam exibir o conteúdo de um arquivo e apenas O Tito possa excluir um arquivo. Essas regras de autorização de granularidade fina podem ser aplicadas declarativamente, programaticamente ou por meio de uma combinação de ambos os métodos. Vamos usar a abordagem declarativa para limitar quem pode exibir o conteúdo de um arquivo; usaremos a abordagem programática para limitar quem pode excluir um arquivo.

Usando o controle LoginView

Como vimos em tutoriais anteriores, o controle LoginView é útil para exibir diferentes interfaces para usuários autenticados e anônimos e oferece uma maneira fácil de ocultar a funcionalidade que não é acessível a usuários anônimos. Como os usuários anônimos não podem exibir ou excluir arquivos, só precisamos mostrar o FileContents TextBox quando a página é visitada por um usuário autenticado. Para conseguir isso, adicione um controle LoginView à página, nomeie-o LoginViewForFileContentsTextBoxe mova a FileContents marcação declarativa do TextBox para o controle LoginView.LoggedInTemplate

<asp:LoginView ID=" LoginViewForFileContentsTextBox " runat="server">
 <LoggedInTemplate>
 <p>
 <asp:TextBox ID="FileContents" runat="server" Rows="10"
 TextMode="MultiLine" Width="95%"></asp:TextBox>
 </p>
 </LoggedInTemplate>
</asp:LoginView>

Os controles da Web nos modelos do LoginView não são mais acessíveis diretamente da classe code-behind. Por exemplo, os FilesGrid manipuladores de eventos e RowDeleting GridView SelectedIndexChanged atualmente fazem referência ao FileContents controle TextBox com código como:

FileContents.Text = text;

No entanto, esse código não é mais válido. Ao mover a FileContents Caixa de Texto para a LoggedInTemplate Caixa de Texto não pode ser acessada diretamente. Em vez disso, devemos usar o FindControl("controlId") método para referenciar programaticamente o controle. Atualize os FilesGrid manipuladores de eventos para fazer referência ao TextBox da seguinte maneira:

TextBox FileContentsTextBox = LoginViewForFileContentsTextBox.FindControl("FileContents") as TextBox;
FileContentsTextBox.Text = text;

Depois de mover o TextBox para o LoginView LoggedInTemplate e atualizar o código da página para fazer referência ao TextBox usando o FindControl("controlId") padrão, visite a página como um usuário anônimo. Como mostra a Figura 10, a FileContents Caixa de Texto não é exibida. No entanto, o LinkButton de Exibição ainda é exibido.

O controle LoginView renderiza apenas a Caixa de Texto FileContents para usuários autenticados

Figura 10: o controle LoginView renderiza apenas a FileContents Caixa de Texto para Usuários Autenticados (Clique para exibir a imagem em tamanho real)

Uma maneira de ocultar o botão Exibir para usuários anônimos é converter o campo GridView em um TemplateField. Isso gerará um modelo que contém a marcação declarativa para o LinkButton de Exibição. Em seguida, podemos adicionar um controle LoginView ao TemplateField e colocar o LinkButton dentro de LoggedInTemplateLoginView, ocultando assim o botão Exibir de visitantes anônimos. Para fazer isso, clique no link Editar Colunas da Marca Inteligente do GridView para iniciar a caixa de diálogo Campos. Em seguida, selecione o botão Selecionar na lista no canto inferior esquerdo e clique no link Converter este campo em um TemplateField. Isso modificará a marcação declarativa do campo de:

<asp:CommandField SelectText="View" ShowSelectButton="True"/>

Para:

<asp:TemplateField ShowHeader="False">
 <ItemTemplate>
 <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
 CommandName="Select" Text="View"></asp:LinkButton>
 </ItemTemplate>
</asp:TemplateField>

Neste ponto, podemos adicionar um LoginView ao TemplateField. A marcação a seguir exibe o LinkButton de Exibição somente para usuários autenticados.

<asp:TemplateField ShowHeader="False">
 <ItemTemplate>
 <asp:LoginView ID="LoginView1" runat="server">
 <LoggedInTemplate>
 <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
 CommandName="Select" Text="View"></asp:LinkButton>
 </LoggedInTemplate>
 </asp:LoginView>
 </ItemTemplate>
</asp:TemplateField>

Como mostra a Figura 11, o resultado final não é tão bonito quanto a coluna Exibir ainda é exibida, mesmo que o LinkButtons de exibição dentro da coluna esteja oculto. Examinaremos como ocultar toda a coluna GridView (e não apenas o LinkButton) na próxima seção.

O controle LoginView oculta o linkbuttons de exibição para visitantes anônimos

Figura 11: O controle LoginView oculta o linkbuttons de exibição para visitantes anônimos (clique para exibir a imagem em tamanho real)

Limitando programaticamente a funcionalidade

Em algumas circunstâncias, técnicas declarativas são insuficientes para limitar a funcionalidade a uma página. Por exemplo, a disponibilidade de determinadas funcionalidades de página pode depender de critérios além de o usuário visitar a página ser anônimo ou autenticado. Nesses casos, os vários elementos da interface do usuário podem ser exibidos ou ocultos por meios programáticos.

Para limitar a funcionalidade programaticamente, precisamos executar duas tarefas:

  1. Determinar se o usuário que visita a página pode acessar a funcionalidade e
  2. Modifique programaticamente a interface do usuário com base em se o usuário tem acesso à funcionalidade em questão.

Para demonstrar a aplicação dessas duas tarefas, vamos permitir apenas que Tito exclua arquivos do GridView. Nossa primeira tarefa, então, é determinar se é Tito visitando a página. Depois que isso for determinado, precisamos ocultar (ou mostrar) a coluna Excluir do GridView. As colunas do GridView são acessíveis por meio de sua Columns propriedade; uma coluna só será renderizada se sua Visible propriedade estiver definida true como (o padrão).

Adicione o seguinte código ao Page_Load manipulador de eventos antes de associar os dados ao GridView:

// Is this Tito visiting the page?
string userName = User.Identity.Name;
if (string.Compare(userName, "Tito", true) == 0)
    // This is Tito, SHOW the Delete column
    FilesGrid.Columns[1].Visible = true;
else
    // This is NOT Tito, HIDE the Delete column
    FilesGrid.Columns[1].Visible = false;

Como discutimos no tutorial Uma Visão geral da Autenticação de Formulários , User.Identity.Name retorna o nome da identidade. Isso corresponde ao nome de usuário inserido no controle de logon. Se for Tito visitando a página, a propriedade da segunda coluna do Visible GridView será definida truecomo ; caso contrário, ela será definida falsecomo . O resultado líquido é que, quando alguém diferente de Tito visita a página, outro usuário autenticado ou um usuário anônimo, a coluna Excluir não é renderizada (consulte a Figura 12); no entanto, quando Tito visita a página, a coluna Excluir está presente (consulte a Figura 13).

A coluna Delete não é renderizada quando visitada por alguém diferente de Tito (como Bruce)

Figura 12: a coluna Delete não é renderizada quando visitada por alguém diferente de Tito (como Bruce) (clique para exibir a imagem em tamanho real)

A coluna Delete é renderizada para Tito

Figura 13: a coluna Delete é renderizada para Tito (clique para exibir a imagem em tamanho real)

Etapa 4: Aplicar regras de autorização a classes e métodos

Na Etapa 3, não permitimos que usuários anônimos exibiam o conteúdo de um arquivo e proibimos todos os usuários, exceto Tito, de excluir arquivos. Isso foi feito ocultando os elementos de interface do usuário associados para visitantes não autorizados por meio de técnicas declarativas e programáticas. Para nosso exemplo simples, ocultar corretamente os elementos da interface do usuário era simples, mas e os sites mais complexos em que pode haver muitas maneiras diferentes de executar a mesma funcionalidade? Ao limitar essa funcionalidade a usuários não autorizados, o que acontece se esquecermos de ocultar ou desabilitar todos os elementos de interface do usuário aplicáveis?

Uma maneira fácil de garantir que uma determinada funcionalidade não possa ser acessada por um usuário não autorizado é decorar essa classe ou método com o PrincipalPermission atributo . Quando o runtime do .NET usa uma classe ou executa um de seus métodos, ele verifica se o contexto de segurança atual tem permissão para usar a classe ou executar o método . O PrincipalPermission atributo fornece um mecanismo por meio do qual podemos definir essas regras.

Vamos demonstrar o uso do PrincipalPermission atributo nos manipuladores de eventos e RowDeleting gridView SelectedIndexChanged para proibir a execução por usuários anônimos e usuários diferentes do Tito, respectivamente. Tudo o que precisamos fazer é adicionar o atributo apropriado em cima de cada definição de função:

[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
protected void FilesGrid_SelectedIndexChanged(object sender, EventArgs e)
{
    ...
}

[PrincipalPermission(SecurityAction.Demand, Name="Tito")]
protected void FilesGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    ...
}

O atributo para o SelectedIndexChanged manipulador de eventos determina que somente usuários autenticados podem executar o manipulador de eventos, em que como o atributo no RowDeleting manipulador de eventos limita a execução a Tito.

Se, de alguma forma, um usuário diferente de Tito tentar executar o RowDeleting manipulador de eventos ou um usuário não autenticado tentar executar o SelectedIndexChanged manipulador de eventos, o runtime do .NET gerará um SecurityException.

Se o contexto de segurança não estiver autorizado a executar o método , uma SecurityException será gerada

Figura 14: se o contexto de segurança não estiver autorizado a executar o método, um SecurityException será gerado (clique para exibir a imagem em tamanho real)

Observação

Para permitir que vários contextos de segurança acessem uma classe ou método, decore a classe ou o método com um PrincipalPermission atributo para cada contexto de segurança. Ou seja, para permitir que Tito e Bruce executem o RowDeleting manipulador de eventos, adicione doisPrincipalPermission atributos:

[PrincipalPermission(SecurityAction.Demand, Name="Tito")]

[PrincipalPermission(SecurityAction.Demand, Name="Bruce")]

Além de ASP.NET páginas, muitos aplicativos também têm uma arquitetura que inclui várias camadas, como Lógica de Negócios e Camadas de Acesso a Dados. Essas camadas normalmente são implementadas como Bibliotecas de Classes e oferecem classes e métodos para executar funcionalidades relacionadas à lógica de negócios e aos dados. O PrincipalPermission atributo é útil para aplicar regras de autorização a essas camadas.

Para obter mais informações sobre como usar o PrincipalPermission atributo para definir regras de autorização em classes e métodos, consulte a entrada de blog de Scott GuthrieAdicionando regras de autorização a camadas de dados e de negócios usando PrincipalPermissionAttributes.

Resumo

Neste tutorial, analisamos como aplicar regras de autorização baseadas no usuário. Começamos com uma olhada no ASP. Estrutura de autorização de URL do NET. Em cada solicitação, o mecanismo de UrlAuthorizationModule ASP.NET inspeciona as regras de autorização de URL definidas na configuração do aplicativo para determinar se a identidade está autorizada a acessar o recurso solicitado. Em resumo, a autorização de URL facilita a especificação de regras de autorização para uma página específica ou para todas as páginas em um diretório específico.

A estrutura de autorização de URL aplica regras de autorização de página por página. Com a autorização de URL, a identidade solicitante está autorizada a acessar um recurso específico ou não. Muitos cenários, no entanto, exigem regras de autorização de granularidade mais finas. Em vez de definir quem tem permissão para acessar uma página, talvez seja necessário permitir que todos acessem uma página, mas para mostrar dados diferentes ou oferecer funcionalidades diferentes, dependendo do usuário visitar a página. A autorização no nível da página geralmente envolve ocultar elementos específicos da interface do usuário para impedir que usuários não autorizados acessem a funcionalidade proibida. Além disso, é possível usar atributos para restringir o acesso a classes e a execução de seus métodos para determinados usuários.

Programação feliz!

Leitura Adicional

Para obter mais informações sobre os tópicos discutidos neste tutorial, consulte os seguintes recursos:

Sobre o autor

Scott Mitchell, autor de vários livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Scott pode ser contatado em mitchell@4guysfromrolla.com ou através de seu blog em http://ScottOnWriting.NET.

Agradecimentos Especiais

Esta série de tutoriais foi revisada por muitos revisores úteis. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, deixe-me uma linha em mitchell@4GuysFromRolla.com.