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
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 é FormsAuthenticationModule
o . 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 FormsAuthenticationModule
e 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.
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.aspx
o . 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.
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 FileAuthorizationModule
autoridade 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
.
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.
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.
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.aspx
o . 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.aspx
o , 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 usaUrlAuthorizationModule
as 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.aspx
o , o o redirecionaria FormsAuthenticationModule
para Login.aspx?ReturnUrl=OnlyTito.aspx
o . 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.
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.
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.
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.
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 LoginViewForFileContentsTextBox
e mova a FileContents
marcação declarativa do TextBox para o controle LoggedInTemplate
LoginView .
<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.
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.
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:
- Determine se o usuário que visita a página pode acessar a funcionalidade e
- 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).
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)
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
.
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 PrincipalPermissionAttributes
o .
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:
- Adicionando regras de autorização às camadas de negócios e de dados usando
PrincipalPermissionAttributes
- ASP.NET Autorização
- Alterações entre a segurança do IIS6 e do IIS7
- Configurando arquivos e subdiretórios específicos
- Limitando a funcionalidade de modificação de dados com base no usuário
- Inícios rápidos do controle LoginView
- Noções básicas sobre autorização de URL do IIS7
UrlAuthorizationModule
Documentação técnica- Trabalhando com dados no ASP.NET 2.0
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.