Compartilhar via


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 os 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 uma série de vantagens sobre o sistema de associação ASP.NET, incluindo:

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

Baixar código ou baixar PDF

Neste tutorial, veremos como limitar o acesso às páginas e restringir a funcionalidade no nível da página por meio de uma variedade de técnicas.

Introdução

A maioria dos aplicativos da web que oferecem contas de usuário o faz em parte para restringir o acesso de determinados visitantes a determinadas páginas do site. Na maioria dos sites de quadro de mensagens online, por exemplo, todos os usuários - anônimos e autenticados - podem visualizar 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 acessadas por um usuário específico (ou um conjunto específico de usuários). Além disso, a funcionalidade no nível da página pode diferir de usuário para usuário. Ao visualizar uma lista de postagens, os usuários autenticados recebem uma interface para avaliar 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 específico 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 meios programáticos e declarativos.

Neste tutorial, veremos como limitar o acesso às páginas e restringir a 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 ASP.NET processa uma solicitação de um recurso ASP.NET, a solicitação gera vários eventos durante seu ciclo de vida. Os 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 vem com vários módulos HTTP que executam tarefas essenciais nos bastidores.

Um desses módulos HTTP é FormsAuthenticationModuleo . 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 incorporado ao URL. Essa identificação ocorre durante o AuthenticateRequest evento.

Outro módulo HTTP importante é o UrlAuthorizationModule, que é gerado em resposta ao AuthorizeRequest evento (que acontece após o AuthenticateRequest evento). O UrlAuthorizationModule examina a marcação de configuração para Web.config 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 ver o que UrlAuthorizationModule o faz, dependendo se a solicitação está autorizada ou não. Se o UrlAuthorizationModule determinar que a solicitação está autorizada, ele não fará nada e continuará durante seu ciclo de vida. No entanto, se a solicitação não for autorizada, o anulará UrlAuthorizationModule o ciclo de vida e instruirá o Response objeto a retornar um status HTTP 401 não autorizado. Ao usar a autenticação de formulários, esse status HTTP 401 nunca é retornado ao cliente porque, FormsAuthenticationModule se detectar um status HTTP 401, ele será modificado para um redirecionamento HTTP 302 para a página de login.

A Figura 1 ilustra o fluxo de trabalho do pipeline de ASP.NET, o FormsAuthenticationModulee quando UrlAuthorizationModule 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 anula UrlAuthorizationModule a solicitação e retorna um status HTTP 401 Não autorizado. Em FormsAuthenticationModule seguida, converte o status 401 em uma página de redirecionamento 302 para login. Depois que o usuário é autenticado por meio da página de login, 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 UrlAuthorizationModule o 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 ilustra 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 ele tentou visitar especificada na querystring. Depois que o usuário fizer logon com êxito, ele será redirecionado automaticamente de volta para o recurso que estava tentando exibir inicialmente.

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

Imagine que nosso site tivesse suas regras de autorização de URL configuradas de forma que a página OnlyTito.aspx ASP.NET fosse acessível apenas para Tito. Agora, imagine que Sam visita o site, faz logon e tenta visitar OnlyTito.aspxo . O UrlAuthorizationModule interromperá o ciclo de vida da solicitação e retornará um status HTTP 401 não autorizado, que detectará FormsAuthenticationModule e redirecionará Sam para a página de login. Como Sam já fez login, ela pode se perguntar por que foi enviada de volta à página de login. Ela pode raciocinar que suas credenciais de login foram perdidas de alguma forma ou que ela inseriu credenciais inválidas. Se Sam inserir novamente suas credenciais na página de login, ela será conectada (novamente) e redirecionada para OnlyTito.aspx. O UrlAuthorizationModule detectará que Sam não pode visitar esta página e ela retornará à página de login.

A Figura 2 mostra 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 confundir rapidamente até mesmo o visitante mais experiente em computadores. Veremos 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 solicitadas. A autorização de arquivo é mais comumente usada com a 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 visita o site. Como esta série de tutoriais se concentra na autenticação de formulários, não discutiremos a autorização de arquivos.

O escopo da autorização de URL

O UrlAuthorizationModule código gerenciado é que faz parte do ASP.NET runtime. 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. Em resumo, no IIS 6 e anteriores, o ASP. NET UrlAuthorizationModule só é executada quando uma solicitação é delegada do IIS para o tempo de execução ASP.NET. Por padrão, o IIS processa o conteúdo estático em si, como páginas HTML e CSS, JavaScript e arquivos de imagem, e só entrega solicitações ao ASP.NET tempo de execução quando uma página com uma extensão de .aspx, .asmx, ou .ashx é solicitada.

O IIS 7, no entanto, permite pipelines integrados do IIS e do ASP.NET. Com algumas definições de configuração, você pode configurar o IIS 7 para invocar o para todas as solicitações, o que significa que as UrlAuthorizationModule 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 a integração ASP.NET e a funcionalidade de autorização de URL nativa do IIS 7, consulte Noções básicas sobre autorização de URL do IIS7. Para uma análise mais aprofundada da integração do ASP.NET e do IIS 7, pegue uma cópia do livro de Shahram Khosravi, Professional IIS 7 and ASP.NET Integrated Programming (ISBN: 978-0470152539).

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

Observação

Há algumas diferenças sutis, mas importantes, em como o ASP. NET UrlAuthorizationModule e IIS 7 Este tutorial não examina a funcionalidade de autorização de URL do IIS 7 ou as diferenças em como ele 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 deve 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 explicadas no elemento na forma de <allow> elementos filho e <deny> .<authorization> Cada <allow> elemento filho pode <deny> especificar:

  • Um usuário específico
  • Uma lista de usuários delimitada por vírgulas
  • 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 têm permissão - Tito e Scott - enquanto o <deny> elemento instrui que todos os usuários sejam negados.

Observação

Os <allow> elementos and <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 o 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 e Web.config se aplicam a todos os recursos ASP.NET no aplicativo Web. Muitas vezes, 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 examinar os produtos, ver análises de produtos, pesquisar no catálogo e assim por diante. No entanto, apenas usuários autenticados podem acessar o checkout 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 sites.

ASP.NET facilita a definição de diferentes regras de autorização para diferentes arquivos e pastas 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 um 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 mouse na Membership 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 Web.config à pasta de associação

Figura 3: Adicionar um Web.config arquivo à pasta (clique para exibir a Membership 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 Membership o 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 página inicial em um navegador e verifique se você está desconectado. 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 da esquerda. Isso o levará ao ~/Membership/CreatingUserAccounts.aspx. Como o Web.config Membership arquivo na pasta define regras de autorização para proibir o acesso anônimo, o anula UrlAuthorizationModule a solicitação e retorna um status HTTP 401 Não autorizado. O FormsAuthenticationModule modifica isso para um status de redirecionamento 302, enviando-nos para a página de login. 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 login

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

Ao fazer login com sucesso, 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 sejam substituídos por outro Web.config arquivo). Em alguns casos, porém, podemos querer que todos os recursos ASP.NET em um determinado diretório tenham uma configuração de autorização específica, exceto para uma ou duas páginas específicas. Isso pode ser feito 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 nele.

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

<?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 ASP.NET recursos na Membership pasta e suas subpastas. O <location> elemento nos permite substituir essas regras para 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 da Membership pasta, como UserBasedAuthorization.aspxo , o negará UrlAuthorizationModule a solicitação e você será redirecionado para a página de login. Depois de fazer login como, digamos, Scott, você pode visitar qualquer página da Membership pasta, exceto CreatingUserAccounts.aspx . Tentar visitar CreatingUserAccounts.aspx conectado como qualquer pessoa, exceto Tito, resultará em uma tentativa de acesso não autorizado, redirecionando você de volta para a página de login.

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 usaUrlAuthorizationModuleas regras de autorização para conceder ou negar acesso

O UrlAuthorizationModule determina se uma identidade específica deve ser autorizada para um URL específico analisando as regras de autorização de URL, uma de cada vez, começando pela primeira e descendo. Assim que uma correspondência é encontrada, o usuário recebe ou nega acesso, dependendo se a correspondência foi encontrada em um <allow> elemento or <deny> . Se nenhuma correspondência for encontrada, o usuário terá 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 terão acesso.

Para entender melhor o processo usado pela UrlAuthorizationModule autoridade para determinar, 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 o acesso a todos. Se um usuário anônimo visita, começa UrlAuthorizationModule perguntando: O anônimo é Scott ou Tito? A resposta, obviamente, é não, então prossegue para a segunda regra. O anônimo está no conjunto de todos? Como a resposta aqui é Sim, a <deny> regra é colocada em vigor e o visitante é redirecionado para a página de login. Da mesma forma, se Jisun estiver visitando, começa UrlAuthorizationModule perguntando: Jisun é Scott ou Tito? Como ela não está, prossegue UrlAuthorizationModule para a segunda pergunta: Jisun está no set de todos? Ela é, então ela também tem acesso negado. Finalmente, se Tito visitar, a primeira pergunta feita pelo UrlAuthorizationModule é uma resposta afirmativa, então Tito tem acesso.

Como o processa UrlAuthorizationModule as regras de autorização de cima para baixo, parando em qualquer partida, é importante que as regras mais específicas venham antes das menos específicas. Ou seja, para definir regras de autorização que proíbem o Jisun e usuários anônimos, mas permitem todos os outros usuários autenticados, você começaria com a regra mais específica - aquela que afeta o Jisun - e depois prosseguiria 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 primeiro negando o Jisun e, em seguida, negando qualquer usuário anônimo. Qualquer usuário autenticado que não seja Jisun terá acesso porque nenhuma dessas <deny> declarações corresponderá.

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

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

Como discutimos anteriormente neste tutorial na seção Uma olhada no fluxo de trabalho de autorização de URL, sempre que uma solicitação não autorizada ocorrer, o anulará UrlAuthorizationModule a solicitação e retornará um status HTTP 401 não autorizado. Esse status 401 é modificado pelo FormsAuthenticationModule status em um status de redirecionamento 302 que envia o usuário para a página de login. Esse fluxo de trabalho ocorre em qualquer solicitação não autorizada, mesmo que o usuário seja autenticado.

Retornar um usuário autenticado à página de login provavelmente o confundirá, pois ele já fez login 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 de 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 mestra. Depois de criar essa página, remova o controle Content que faz referência ao LoginContent ContentPlaceHolder para que o conteúdo padrão da página mestra 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 login. A lógica que redireciona solicitações não autorizadas para a página de logon está enterrada em um método privado da FormsAuthenticationModule classe, portanto, não podemos personalizar esse comportamento. O que podemos fazer, no entanto, é adicionar nossa própria lógica à página de login 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 login, ele anexa o URL não autorizado solicitado à querystring com o nome ReturnUrl. Por exemplo, se um usuário não autorizado tentasse visitar OnlyTito.aspxo , o o redirecionaria FormsAuthenticationModule para Login.aspx?ReturnUrl=OnlyTito.aspxo . Portanto, se a página de logon for acessada por um usuário autenticado com uma querystring que inclui o ReturnUrl parâmetro, saberemos que esse usuário não autenticado apenas tentou visitar uma página que não está autorizado a exibir. Nesse caso, queremos redirecioná-la para UnauthorizedAccess.aspx.

Para fazer isso, adicione o seguinte código ao manipulador de Page_Load eventos da página de 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 da esquerda. Isso o levará à ~/Membership/CreatingUserAccounts.aspx página, que na Etapa 1 configuramos para permitir apenas o acesso ao Tito. Como usuários anônimos são proibidos, o nos FormsAuthenticationModule redireciona de volta para a página de login.

Neste ponto, somos anônimos, então Request.IsAuthenticated retorna false e não somos redirecionados para UnauthorizedAccess.aspx. Em vez disso, a página de login é exibida. Faça login como um usuário diferente de Tito, como Bruce. Depois de inserir as credenciais apropriadas, a página de login nos redireciona de volta para ~/Membership/CreatingUserAccounts.aspx. No entanto, como esta página é acessível apenas ao Tito, não estamos autorizados a visualizá-la e retornamos prontamente à página de login. Desta vez, no entanto, Request.IsAuthenticated retorna true (e o ReturnUrl parâmetro querystring existe), então 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 a imagem em tamanho real)

Esse fluxo de trabalho personalizado apresenta uma experiência de usuário mais sensata e direta, 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 a autorização de URL, podemos declarar sucintamente quais identidades são permitidas e quais são negadas de visualizar uma página específica ou todas as páginas em uma pasta. Em determinados cenários, no entanto, podemos permitir que todos os usuários visitem uma página, mas limitar a funcionalidade da página com base no usuário que a visita.

Considere o caso de um site de comércio eletrônico que permite que visitantes autenticados avaliem 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 tem a oportunidade de deixar uma avaliaçã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 tivesse revisado este produto, a interface permitiria que ele enviasse uma avaliação; caso contrário, mostraria a eles a avaliaçã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 estoque 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 granulação 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 granulação fina por meio do controle LoginView. Em seguida, exploraremos técnicas programáticas. Antes de podermos examinar a aplicação de regras de autorização refinadas, no entanto, primeiro precisamos criar uma página cuja funcionalidade dependa do usuário que a visita.

Vamos criar uma página que lista os arquivos em um diretório específico dentro de 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 Exibir e outra intitulada Excluir. Se o botão Exibir link for clicado, o conteúdo do arquivo selecionado será exibido; se o botão Excluir LinkButton for clicado, o arquivo será excluído. Vamos inicialmente criar esta página de forma que sua funcionalidade de visualização e exclusão esteja disponível para todos os usuários. Nas seções Usando o controle LoginView e a funcionalidade de limitação programática, 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, autorização, contas de usuário e funções de formulários, 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 qual efeito determinadas propriedades têm na saída renderizada. Para obter um exame completo do controle GridView, consulte minha série de tutoriais Trabalhando com dados no ASP.NET 2.0.

Comece abrindo o UserBasedAuthorization.aspx Membership arquivo na 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 View e as propriedades do primeiro BoundField HeaderText e DataField como Name. Defina a propriedade do HeaderText segundo BoundField como Size in 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. Na janela Propriedades, defina a propriedade do 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 escrever 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 Page_Load eventos da 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 associada ao GridView. O FileInfo objeto tem uma variedade de propriedades, como Name, Length, e 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 and FileInfo são encontradas no System.IO namespace. Portanto, você precisará preceder esses nomes de classe com seus nomes de namespace ou importar o namespace para o arquivo de classe (via using System.IO).

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 Exibir ou Excluir 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 de exibir o conteúdo do arquivo selecionado. Retorne ao Visual Studio e adicione um TextBox nomeado FileContents acima do 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 do SelectedIndexChanged GridView 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 do 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 do 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 do 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, em seguida, tentar exibir ou excluir um arquivo, receberá um HttpRequestValidationException erro. Isso ocorre porque, no postback, o conteúdo do TextBox é enviado de volta ao servidor Web. Por padrão, ASP.NET gera um HttpRequestValidationException erro sempre que um 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 de 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 do RowDeleting GridView:

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 FileContents no TextBox sem realmente excluir o arquivo.

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

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 usuários anônimos de exibir as páginas na Membership pasta. Para exibir melhor a autenticação refinada, 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 Web.config seguinte <location> elemento ao 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 logout do site. Como 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 visualizar ou excluir arquivos. Vamos fazer com que apenas usuários autenticados possam visualizar o conteúdo de um arquivo e apenas Tito possa excluir um arquivo. Essas regras de autorização de granulação 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 interfaces diferentes para usuários autenticados e anônimos e oferece uma maneira fácil de ocultar funcionalidades que não são acessíveis 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 for visitada por um usuário autenticado. Para fazer isso, adicione um controle LoginView à página, nomeie-o LoginViewForFileContentsTextBoxe mova a FileContents marcação declarativa do TextBox para o controle LoggedInTemplateLoginView .

<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 Web nos modelos do LoginView não podem mais ser acessados diretamente da classe code-behind. Por exemplo, os FilesGrid manipuladores de SelectedIndexChanged eventos e RowDeleting GridView atualmente fazem referência ao FileContents controle TextBox com código como:

FileContents.Text = text;

No entanto, este código não é mais válido. Ao mover o FileContents TextBox para o TextBox, o LoggedInTemplate TextBox não pode ser acessado 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 forma:

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, o FileContents TextBox não é exibido. No entanto, o View LinkButton ainda é exibido.

O controle LoginView renderiza apenas o FileContents TextBox para usuários autenticados

Figura 10: O controle LoginView renderiza apenas o TextBox para usuários autenticados (clique para exibir a FileContents 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 View LinkButton. Em seguida, podemos adicionar um controle LoginView ao TemplateField e colocar o LinkButton dentro do LoginView, ocultando assim o botão Exibir de visitantes anônimos LoggedInTemplate. Para fazer isso, clique no link Editar Colunas na 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 View LinkButton 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, pois a coluna View ainda é exibida, mesmo que os ViewLinkButtons dentro da coluna estejam ocultos. Veremos como ocultar toda a coluna GridView (e não apenas o LinkButton) na próxima seção.

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

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

Limitando programaticamente a funcionalidade

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

Para limitar a funcionalidade programaticamente, precisamos executar duas tarefas:

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

Para demonstrar a aplicação dessas duas tarefas, vamos permitir que Tito exclua apenas 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 Delete do GridView. As colunas do GridView podem ser acessadas por meio de sua Columns propriedade; uma coluna só será renderizada se sua Visible propriedade estiver definida como true (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 como true; caso contrário, ela será definida como false. O resultado líquido é que, quando alguém que não seja Tito visita a página, outro usuário autenticado ou um usuário anônimo, a coluna Delete não é renderizada (consulte a Figura 12); no entanto, quando Tito visita a página, a coluna Delete está presente (consulte a Figura 13).

A coluna Excluir não é renderizada quando visitada por alguém que não seja Tito (como Bruce)

Figura 12: A coluna Excluir não é renderizada quando visitada por alguém que não seja 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: Aplicando regras de autorização a classes e métodos

Na Etapa 3, proibimos que usuários anônimos visualizem 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 adequadamente os elementos da interface do usuário foi simples, mas e os sites mais complexos, onde 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 da 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 através do qual podemos definir essas regras.

Vamos demonstrar o uso do atributo nos manipuladores de eventos e GridView para proibir a PrincipalPermission execução por usuários anônimos SelectedIndexChanged RowDeleting e usuários diferentes de 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 do SelectedIndexChanged manipulador de eventos determina que apenas usuários autenticados podem executar o manipulador de eventos, enquanto 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, um SecurityException será gerado

Figura 14: Se o contexto de segurança não estiver autorizado a executar o método, a SecurityException será lançado (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 manipulador de RowDeleting eventos, adicione dois PrincipalPermission 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 são normalmente implementadas como Bibliotecas de Classes e oferecem classes e métodos para executar a funcionalidade relacionada à 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 do blog de Scott Guthrie Adicionando regras de autorização às camadas de negócios e dados usando PrincipalPermissionAttributeso .

Resumo

Neste tutorial, vimos como aplicar regras de autorização baseadas no usuário. Começamos com uma olhada no ASP. NET da estrutura de autorização de URL. Em cada solicitação, o mecanismo de pesquisa ASP.NET inspeciona as regras de UrlAuthorizationModule autorização de URL definidas na configuração do aplicativo para determinar se a identidade está autorizada a acessar o recurso solicitado. Resumindo, 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 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 mais refinadas. 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 mostrar dados diferentes ou oferecer funcionalidades diferentes, dependendo do usuário que visita 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 funcionalidades proibidas. Além disso, é possível usar atributos para restringir o acesso às classes e a execução de seus métodos para determinados usuários.

Boa programação!

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 ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Web da Microsoft desde 1998. Scott trabalha como consultor, instrutor e escritor independente. 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 a

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