Arquivo da categoria ‘ASP.NET’

Problema com ASP.NET AJAX 1.0 no Visual Studio 2008

Cenário: Alguns desenvolvedores tem encontrado dificuldades para rodar projetos ASP.NET 2.0 que utilizam o AJAX Extensions versão 1.0.61025.0 no Visual Studio 2008. Muitos destes desenvolvedores, tentam apenas fazer a referência para a biblioteca  AjaxControlToolKit.dll diretamente a partir da janela Choose ToolBox Items. Contudo, o Ajax Control Toolkit utiliza o assembly System.Web.Extensions que é registrado no GAC (Global Assembly Cache). O que ocorre é que quando você instala o Visual Studio 2008, o assembly System.Web.Extensions registrado no GAC é da versão 3.5.0.0. Assim, quando você tenta abrir um projeto que utiliza o AJAX 1.0 a IDE do Visual Studio exibe uma mensagem de erro informando que o assembly System.Web.Extensions não pode ser carregado por que ele ou uma das suas dependências não foi encontrado.
Solução:

Instale o ASP.NET AJAX Extensions 1.0 a partir do arquivo ASPAJAXExtSetup.msi (disponível para download no site www.asp.net). Esta instalção adiciona e registra no GAC a versão 1.0.61025 necessária para seus projetos. As duas versões vão continuar funcionando normalmente pois o Global Assembly Cache gerencia de forma eficiente os assemblies de mesmo nome e com vesões diferentes.

Alternativa:
Se você não quiser instalar o ASP.NET AJAX Extensions 1.0 completo, basta copiar o assembly System.Web.Extensions de outro computador que tenha a versão 1.0.61025 e registrá-lo no GAC manualmente utilizando a ferramenta GACUtil.exe. O comando deve ser invocado a partir do prompt de comando do Visual Studio 2008 com a seguinte sintaxe GACUtil.exe -i <path>\System.Web.Extensions.dll. Esta prática é recomendada apenas para ambientes de desenvolvimento para ambientes de produção, o desenvolvedor deve instalar os componentes através do MSCORCFG.msc ou preferencialmente por um pacote do Windows Installer (.msi).
Abaixo está o screenshot da pasta C:\WINDOWS\ASSEMBLY mostrando as duas versões do assembly System.Web.Extensions devidamente configuradas: GAC_SystemWebExt  

Exclusão de Registro com Confirmação do Usuário

Introdução
Apresentar registros em formato de tabela é uma das tarefas mais comuns em todos os tipos de aplicações. No ASP.NET 2.0 temos novos controles do tipo Grid (GridView e DetailsView) e suas flexibilidades nos permitem iteragir com o usuário de diversas formas. Neste exemplo, vou demonstrar como solicitar a confirmação do usuário antes de excluir um registro do banco de dados apresentado num GridView.

Criando nosso cenário
Para ilustrar meu exemplo, vou utilizar a tabela de Produtos do banco de dados Northwind do SQL Server. O primeiro passo é criar uma nova aplicação ASP.NET e adicionar ao novo WebForm um controle do tipo GridView (GridProducts) e um Label (lblError). A propriedade AutoGenerateColumns do GridView deve ser definida como False. Adicionei apenas duas colunas para este exemplo, uma delas exibirá o código do produto e a outra a descrição do produto. O Quadro 1 apresenta o código HTML completo para implementação deste GridView.

<asp:GridView ID=”GridProducts” runat=”server” CellPadding=”4″
DataKeyNames=”ProductID” ForeColor=”#333333″ GridLines=”None”
OnRowDeleting=”GridProducts_RowDeleting” AutoGenerateColumns=”False”
OnRowDataBound=”GridProducts_RowDataBound” OnRowCommand=”GridProducts_RowCommand”>

<FooterStyle BackColor=”#5D7B9D” Font-Bold=”True” ForeColor=”White” />
<RowStyle BackColor=”#F7F6F3″ ForeColor=”#333333″ />
<EditRowStyle BackColor=”#999999″ />
<SelectedRowStyle BackColor=”#E2DED6″ Font-Bold=”True” ForeColor=”#333333″ />
<PagerStyle BackColor=”#284775″ ForeColor=”White” HorizontalAlign=”Center” />
<HeaderStyle BackColor=”#5D7B9D” Font-Bold=”True” ForeColor=”White” />
<AlternatingRowStyle BackColor=”White” ForeColor=”#284775″ />

<Columns>
<asp:BoundField DataField=”ProductID” HeaderText=”ProductID” ReadOnly=”True” />
<asp:BoundField DataField=”ProductName” HeaderText=”ProductName” />
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID=”cmdDelete”
CommandArgument=’<%# Eval(”ProductID”) %>’
CommandName=”Delete” runat=”server”>
Delete</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Quadro 1: Código HTML para implementar o GridView

A propriedade DataKeyNames
Através desta propriedade, o desenvolvedor pode obter ou definir a(s) coluna(s) que representa(m) a Chave Primária dos registros exibidos no GridView. Ela será utilizada em conjunto com o evento RowDeleting. Neste exemplo, seu valor foi definido como “ProductID”.

As colunas do GridView
O desenvolvedor deverá adicionar duas colunas (BoundColumn) para exibição dos dados (ProductID e ProductName). Além destas colunas, será necessário a criação de uma coluna do tipo TemplateField que exibirá o botão Excluir. No exemplo, optei por um LinkButton. Seu ID foi definido como cmdDelete e duas propriedades deste botão são fundamentais para o correto funcionamento. A primeira delas é a propriedade CommandName, definida como “Delete”. Esta propriedade é utilizada em conjunto com o evento RowCommand que também será explicado neste artigo. A segunda propriedade é a CommandArgument que definiremos com o auxilio da expressão Eval(“ProductID”) conforme apresentado no Quadro 1.

Carregando o GridView
A carga deste controle pode ser feita de diversas formas, neste exemplo utilizei um DataSet como fonte de dados. O Quadro 2 arpesenta o código do procedimento LoadGridView na sua íntegra.

protected void LoadGridProducts()
{
SqlConnection cn = new SqlConnection("SERVER=(local);DATABASE=northwind;UID=sa;PWD=;");
SqlCommand cmd = new SqlCommand();

cn.Open();

cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "select ProductID, ProductName from Products";

SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();

da.Fill(ds, "Products");

GridProducts.DataSource = ds;

GridProducts.DataBind();

cn.Close();
}

Quadro 2: O procedimento para carga do GridView

O evento RowDataBound
A chave do funcionamento desta implementação está na manipulação do evento RowDataBound para adicionarmos o código JavaScript que solicitará a confirmação do usuário antes de executar a operação de exclusão. O evento RowDataBound é disparado sempre que uma linha é adicionada ao GridView, isto ocorre tanto quando o GridView é criado pela primeira vez e sempre que a página é recarregada. O Quadro 3 apresenta o código implementado para este evento.

protected void GridProducts_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton MyButton = (LinkButton)e.Row.FindControl("cmdDelete");
MyButton.Attributes.Add("onclick", "javascript:return " +
"confirm('Confirma a exclusão do Produto nº " +
DataBinder.Eval(e.Row.DataItem, "ProductID") + "?')");
}
}

Quadro 3: Código do evento RowDataBound

Os eventos RowCommand e RowDeleting
O controle GridView permite que o desenvolvedor implemente procedimentos para exclusão de registros em dois eventos o RowCommand e o RowDeleting. Na verdade, o evento RowComand é disparado sempre que um botão de comando do GridView é pressionado. Seja qual for o comando associado ao botão este evento será acionado. O evento RowDeleting é disparado sempre que o comando “Delete” do GridView for invocado, mas sempre antes de efetivar a exclusão. Este evento pode ser utilizado inclusive para cancelar o processo de exclusão disparado pelo evento RowCommand. Mas isto é assunto para outro artigo. Neste exemplo vou mostrar como podemos capturar a chave do registro, ProductID, utilizando os dois eventos separadamente.

Codificando os eventos RowCommand e RowDeleting
Os Quadros 4 e 5 apresentam o código completo para os eventos RowCommand e RowDeleting respectivamente. O procedimento implementado é idêntico exceto pela forma como obtém a Chave-Primária do registro que será excluído. No evento RowCommand a Chave-Primária é obtida através do parâmetro CommandArgument alimentado na criação do GridView como mostra o Quadro 1. Já no evento RowDeleting, a Chave-Primária é obtida através da coleção DataKeys do GridView. Analise atentamente o código apresentado nos quadro abaixo.

protected void GridProducts_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
int ProdID = Convert.ToInt32(e.CommandArgument);

SqlConnection cn = new SqlConnection("SERVER=(local);DATABASE=northwind;UID=sa;PWD=;");
SqlCommand cmd = new SqlCommand();

try
{
cn.Open();

cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "delete from products where productid=" + ProdID;
cmd.ExecuteNonQuery();

LoadGridProducts();
}
catch (Exception ex)
{
lblError.Text = ex.Message.ToString();
}
finally
{
cn.Close();
}
}

Quadro 4: O procedimento do evento RowCommand

protected void GridProducts_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
SqlConnection cn = new SqlConnection("SERVER=(local);DATABASE=northwind;UID=sa;PWD=;");
SqlCommand cmd = new SqlCommand();

try
{
cn.Open();

cmd.Connection = cn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "delete from products where productid=" +
GridProducts.DataKeys[e.RowIndex].Value;

cmd.ExecuteNonQuery();

LoadGridProducts();
}
catch (Exception ex)
{
lblError.Text = ex.Message.ToString();
}
finally
{
cn.Close();
}
}

Quadro 5: O procedimento do evento RowDeleting

Testando o funcionamento
Lembre-se que o artigo apresenta duas formas diferentes para realizar a mesma tarefa. Sendo assim, para testar esse exemplo, você deve executar a aplicação ora com o evento RowDeleting tratado e ora com o evento RowCommand. Simultaneamente os dois provocarão uma Exception.

Conclusão
No exemplo acima, o desenvolvedor pode acompanhar a implementação de uma rotina em JavaScript para solicitar a confirmação do usuário antes de executar uma exclusão de registros numa aplicação ASP.NET. Foi esclarecido o funcionamento dos eventos RowCommand, RowDeleting e RowDataBound, assim como o uso das propriedades DataKeys, CommandArgument e CommandName. Os controles do tipo Grid são muito flexíveis e suas aplicações práticas são as mais diversas. Implemente o exemplo acima e adpte-o para suas necessidades.

ASP.NET 2.0 - Criando Menus Baseados no Perfil de Usuário usando Sitemap

O ASP.NET 2.0 introduziu novos controles de navegação como o SiteMapPath e o controle Menu. Com esses controles, o desenvolvedor pode criar facilmente mecanismos de navegação para sua aplicação web armazenando a estrutura de navegação em arquivos XML. Imaginando um cenário de uma aplicação segura, seria muito produtivo se pudéssemos alimentar esses controles de navegação dinamicamente, de acordo com o perfil do usuário. Este é o propósito deste artigo. Vou demonstrar como utilizar um controle Menu e modificar seu DataSource dinamicamente utilizando as classes XmlSiteMapProvider e SiteMapDataSource.

Criando nossa aplicação de exemplo
Para ilustrar nosso artigo criaremos um novo website onde as páginas serão organizadas em pastas de acordo com o perfil do usuário. Nosso exemplo, terá três pastas distinguindo os perfis de Gerentes, Supervisores e Usuários. Cada pasta terá uma página Default.aspx. Para mantermos a organização, acrescentaremos uma pasta chamada _Sitemaps onde colocaremos os arquivos *.sitemap que serão utilizados para construir os menus dinamicamente.

Os arquivos Sitemaps
Basicamente, um arquivo Sitemap é um arquivo XML com a extensão *.sitemap. Esses arquivos contém a estrutura de navegação do website. O nome padrão para o arquivo Sitemap é Web.sitemap. Este arquivo deve ser colocado no diretório Root da aplicação. A implementação do nosso exemplo, utilizará o atributo siteMapFile do arquivo Web.sitemap para adicionarmos quantos Sitemaps forem necessários para atender os diferentes perfis de usuários da nossa aplicação. A Listagem 1 apresenta o conteúdo do arquivo Web.sitemap.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0″ >
<siteMapNode url=”Default.aspx” title=”Menu Principal” description=”Menu Principal”>
<siteMapNode siteMapFile=”~/_Sitemaps/Gerente.sitemap” />
<siteMapNode siteMapFile=”~/_Sitemaps/Supervisor.sitemap” />
<siteMapNode siteMapFile=”~/_Sitemaps/Usuario.sitemap” />
</siteMapNode>
</siteMap>
Listagem 1. O conteúdo do arquivo Web.sitemap

Observe no código da Listagem 1 que foram utilizados os caracteres “~/” para indicar a URL do atributo siteMapFile. Estes caracteres indicam que o ASP.NET Runtime deverá procurar pela pasta _Sitemaps no diretório Root da aplicação.

Criando Diferentes Sitemaps
Para atingirmos o objetivo deste artigo, vamos criar os três arquivos *.sitemaps indicados no arquivo Web.sitemap. A Listagem 2 apresenta o conteúdo do arquivo Gerente.sitemap. Para os perfis de usuário Supervisores e Usuários utilizaremos arquivos com o mesma estrutura modificando apenas as URLs para prover a correta navegação.

<?xml version=”1.0″ encoding=”utf-8″ ?>
<siteMap xmlns=”http://schemas.microsoft.com/AspNet/SiteMap-File-1.0″ >
<siteMapNode url=”~/Gerentes/Default.aspx” title=”Gerentes” description=”Menu Gerentes”>
<siteMapNode url=”Supervisores.aspx” title=”Supervisores” description=”Gerenciar Supervisores” />
<siteMapNode url=”Usuarios.aspx” title=”Usuários” description=”Gerenciar Usuários” />
<siteMapNode url=”RelatoriosGerenciais.aspx” title=”Relatórios” description=”Relatórios” />
<siteMapNode url=”PainelControle.aspx” title=”Painel Controle” description=”Painel de Controle” />
<siteMapNode url=”Sistema.aspx” title=”Sistema” description=”Ferramentas do Sistema” />
</siteMapNode>
</siteMap>
Listagem 2. O arquivo Gerente.sitemap

A página Default.aspx
Para mantermos o foco deste artigo, a página Default.aspx não terá nenhuma interface, apenas um texto no cabeçalho e um único controle Menu, chamado Menu1. Precisaremos de apenas algumas linhas de código nesta página para invocarmos o método GetSiteMapDataSource da classe DynamicSiteMap. Confira o código na Listagem 3.

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
///


/// Não importa o método utilizado para autenticação do usuário.
/// Basta passar o parâmetro PROFILE (string) para o procedimento
/// GetSiteMapDataSource para exibir o menu correspondente ao
/// perfil do usuário autenticado.
///

Menu1.DataSource = DynamicSiteMap.GetSiteMapDataSource(”Gerente”);
Menu1.DataBind();
}
}

Listagem 3. O procedimento Page_Load

A Classe DynamicSiteMap.cs
Para finalizarmos, adicione uma nova classe à sua aplicação. Por padrão o Visual Studio 2005 sugerirá ao desenvolvedor para armazenar sua classe na pasta App_Code de forma que seus métodos possam ser acessados por toda a aplicação. Nossa classe é bastante simples e terá um único método chamado GetSiteMapDataSource. Este método utilizará as classes XmlSiteMapProvider e SiteMapDataSource para manipular o arquivo Web.sitemap dinamicamente. A Listagem 4 apresenta o código desta classe na íntegra.

public class DynamicSiteMap
{

///


/// Este procedimento recebe o parâmetro PROFILE (string) que identifica
/// o perfil do usuário, e define o nó principal do controle Menu1
/// de acordo com esse perfil.
///

public static SiteMapDataSource GetSiteMapDataSource(string profile)
{
// variável para armazenar a URL do nó principal do menu
string URL = String.Empty;

// define a URL de acordo com o parâmetro PROFILE
if (profile.Equals(”Gerente”)) URL = “~/Gerentes/Default.aspx”;
else if (profile.Equals(”Supervisor”)) URL = “~/Supervisores/Default.aspx”;
else if (profile.Equals(”Usuario”)) URL = “~/Usuarios/Default.aspx”;

// cria o objeto XmlSiteMapProvider que será utilizado para construir
// o SiteMap dinamicamente.
XmlSiteMapProvider xmlSiteMap = new XmlSiteMapProvider();
System.Collections.Specialized.NameValueCollection MySiteMap =
new System.Collections.Specialized.NameValueCollection(1);
MySiteMap.Add(”siteMapFile”, “Web.sitemap”);

// inicializa o novo objeto SiteMapProvider
xmlSiteMap.Initialize(”MyProvider”, MySiteMap);
xmlSiteMap.BuildSiteMap();

// cria o DataSource para o objeto SiteMap
SiteMapDataSource siteMap = new SiteMapDataSource();

// atribui o nó principal de acordo com o perfil do usuário
siteMap.StartingNodeUrl = URL;

// oculta o nó principal (opcional)
siteMap.ShowStartingNode = false;

// retorna o objeto para o procedimento de chamada
return siteMap;
}
}

Listagem 4. A Classe DynamicSiteMap.cs

Conclusões
Neste artigo, o desenvolvedor acompanhou como utilizar as classes XmlSiteMapProvider e SiteMapDataSource para modificar arquivo Web.sitemap dinamicamente. Este recurso combinado com os controles de navegação do ASP.NET 2.0 permite a implementação de menus de acordo com o perfil dos usuários.

ASP.NET 2.0 Forms Authentication Cross Application

Introdução
O processo de autenticação de usuários em aplicações ASP.NET é simples e eficiente. Você pode utilizar as novas classes Membership, Roles e Profiles combinadas com os novos controles de Login para implementar uma solução completa para gestão dos seus usuários. Não é raro o desenvolvedor ter duas (ou mais) aplicações Web distintas rodando sob o mesmo domínio. Em algumas situações você pode ter uma aplicação escrita em VB.NET e outra em C# e ambas sendo executadas dentro do mesmo domínio. Em cenários WebFarm onde sua aplicação é distribuída em diferentes servidores para implementar balanceamento de carga, a troca de credenciais entre as aplicações é uma necessidade básica. Este artigo explicará como o desenvolvedor deve modificar o Web.config para permitir que as aplicações compartilhem do mesmo processo de autenticação, ou seja, o usuário autentica-se uma única vez e tem acesso às duas (ou mais) aplicações.

Configuração Padrão
O elemento machineKey armazena os atributos necessários para implementarmos a autenticação CrossApplication. A configuração padrão para o elemento <machineKey> é definida no arquivo web.config.comments, localizado na pasta “c:\windows\Microsoft.NET\Framework\v2.0.50727\config\”. O Quadro 1 apresenta as configurações relevantes apenas como referência.

<machineKey
validationKey = "AutoGenerate,IsolateApps" [String]
decryptionKey = "AutoGenerate,IsolateApps" [String]
decryption = "Auto" [AUTO | DES | 3DES | AES]
validation = "SHA1" [MD5 | SHA1 | 3DES | AES]
/>

Quadro 1: Elemento <machineKey>

Modelo de Autenticação
Forms Authentication utiliza as chaves deste elemento para assinar o Ticket de Autenticação. As Roles Manager e Anonymous, se habilitadas, também utilizam essas chaves para assinar seus cookies. Se sua aplicação utiliza Anonymous Authentication no modo Cookiless, os dados enviados através da URL também são assinados com essas chaves.

Os atributos do elemento machineKey
validationKey: Este atributo é utilizado pelo algorítmo HMAC para garantir que o objeto ViewState não seja violado.

decryptionKey: Esta é o atributo utilizado para criptografar e decriptografar os dados.

decryption: Este atributo especifica o algorítmo de criptografia simétrica utilizado para criptografar e decriptografar os tickets de autenticação.

validation: Este atributo determina o algorítimo responsável por gerar os HMACs que garantirão que o ViewState e os Tickets de Autenticação não sejam violados.

Alterando a Configuração Padrão
Para permitir que duas (ou mais) aplicações possam compartilhar os mesmos Tickets de Autenticação, é necessário que os atributos do elemento machineKey tenham os mesmos valores para todas as aplicações envolvidas no processo. Desta forma, é necessário que o desenvolvedor gere manualmente novas chaves randomicas para alimentar esses atributos. É importante esclarecer que o atributo validationKey suporta chaves de 64 bytes (128 caracteres hexadecimais), e o atributo decryptionKey suporta chaves de 32 bytes (64 caracteres hexadecimais). O Quadro 2 apresenta uma classe de exemplo utilizada para gerar novas chaves randomicamente.

using System;
using System.Text;
using System.Security;
using System.Security.Cryptography;

class App
{
static void Main(string[] argv)
{
int len = 64;
if (argv.Length > 0)
len = int.Parse(argv[0]);
byte[] buff = new byte[len / 2];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(buff);
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < buff.Length; i++)
sb.Append(string.Format("{0:X2}", buff[i]));

string k = sb.ToString();

Console.WriteLine(sb);
Console.ReadLine();
}
}

Quadro 2: Aplicação Console para gerar as chaves

Após gerar as novas chaves para os atributos do elemento machineKey, suas aplicações poderão compartilhar os mesmos Tickets de Autenticação mesmo que estejam instaladas em diferentes diretórios virtuais. O Quadro 3 apresenta o fragmento do arquivo Web.config alterado manualmente.

<machineKey validationKey="8AD952A432FC678710394955A1C4E5A76F123F1671E8956D5492C8776EBC3BA1BC4F5A19176BD3C269C165556D93D6853224AC25BE56B9301F5501861E535614"

decryptionKey="A0BF0BD093D2ED0B8E69AEE1392DBC2F953A4FB108C65808D15CD541960C9031"

validation="SHA1"

decryption="AES"
/>

Quadro 3: O elemento machineKey alterado.

Conclusão
Neste artigo, o leitor acompanhou os passos para alterar o arquivo web.config das suas aplicações para permitir a implementação do modelo de autenticação CrossApplication. Os cenários mais comuns para esta necessidade são os WebFarms e aplicações instaladas em diretórios virtuais diferentes e que necessitam compartilhar a mesma autenticação de usuários.

Configurando permissões de acesso em nível de arquivo no ASP.NET

Um problema comum em aplicações Web refere-se à necessidade de configurarmos diferentes permissões de acesso para pastas e até mesmo arquivos de uma mesma aplicação. No ASP.NET o desenvolvedor encontrará uma solução fácil para este problema. O elemento <Location/&gt; pode ser utilizado para definir permissões para pastas ou arquivos específicos. Ele pode ser configurado tanto no arquivo Machine.Config quanto no Web.Config. A diferença é que no Machine.Config o parâmetro PATH deve conter o endereço absoluto da pasta ou arquivo, enquanto no Web.Config o elemento <Location/> utiliza o endereço relativo. O exemplo abaixo mostra como limitar o acesso ao Website apenas para os usuários autenticados e ao mesmo tempo permitir o acesso à pasta PROMO e ao arquivo PUBLIC.ASPX para todos os usuários:

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

<!--Permissão de acesso para a pasta PROMO na seção COMERCIAL-->
<location path="Comercial/Promo" >
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>

<!--Permissão de acesso para o arquivo PUBLIC.ASPX na seção ARTIGOS-->
<location path="Artigos/Public.aspx" >
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>

<!--Configurações gerais do website-->
<system.web>

<!--Define tipo de autenticação utilizado pelo website e página para Login-->
<authentication mode="Forms">
<forms loginUrl="/Default.aspx"></forms>
</authentication>

<!--Permite acesso apenas para usuários autenticados-->
<authorization>
<deny users="?" />
</authorization>

</system.web>

</configuration>