ASP.NET MVC Entendendo Action Filters

Entendendo Action Filters

Um Action Filter é um atributo que você pode aplicar a uma Action de um controller,ou a um controller diretamente – o que modifica a maneira como essa action é executada.O framework MVC ja inclui muitos Action Filters

  • OutputCache – Esse action filter coloca o resultado de uma action em cache por um tempo determinado
  • Handle Error – Esse action filter manipula erros disparados durante a execução da action de um controller
  • Authorize – Esse action filter permite a você restringir acesso a um usuario ou role em particular

Você pode também criar seu próprio action filter.Por exemplo,você pode querer criar seu próprio action filter para implementar um sistema de autenticação personalizado.Ou,você pode querer criar um action filter que modifique a viewdata retornado por uma action de um controller.

Usando um action filter

Um action filter é um atributo.Você pode aplicar a maioria dos action filters para uma action ou diretamente para um controller inteiro.Por exemplo,o DataController abaixo contém uma action chamada Index que retorna a data atual.Essa action está decorada com o action filter OutputCache .Esse action filter faz com que o valor retornado pela action seja colocado em cache por 10 segundos.

using System;
using System.Web.Mvc; 

namespace MvcApplication1.Controllers
{
public class DataController : Controller
{
[OutputCache(Duration=10)]
public string Index()
{
return DateTime.Now.ToString(“T”);

}
}
}

Se você chamar repetidamente a action Index digitando a URL /Data/Index na barra de endereços do seu navegador e pressionando o botão de refresh varias vezes,você verá o mesmo horário durante 10 segundos.A saída da action Index é colocada em cache durante 10 segundos (figura 1):

No exemplo acima,um unico action filter – o OutputCache – é aplicado ao método Index.Se você precisar,você pode aplicar vários action filters diferentes a mesma action.Por exemplo,você pode querer aplicar os action filters OutputCache e HandleError para a mesma action.

No exemplo acima,o action filter está sendo aplicado diretamente na action Index,mas você também poderia ter aplicado diretamente ao controller.Nesse caso,todas as actions deste controller teriam suas saídas colocadas em cache durante 10 segundos.

Os diferentes tipos de filters

O ASP.NET MVC suporta quatro diferentes tipos de filters:

  1. Authorization filters – Implementa o atributo IAuthorizationFilter
  2. Action filters – Implementa o atributo IActionFilter
  3. Result filters – Implementa o atributo IResultFilter
  4. Exception filters – Implementa o atributo IExceptionFilter

Os filters são executados na ordem listada acima.Por exemplo,authorization filters são sempre executados antes de action filters e exception filters são sempre executados após qualquer outro tipo de filter.

Authorization Filters são usados para implementar autenticação e autorização para actions de um controller.Por exemplo,o Authorize filter é um exemplo do Authorization Filter.

Action Filters contém lógica que é executada antes e depois que a action de um controller é executada.Você pode usar um action filter,por exemplo,para modificar a ViewData que uma action retorna.

Result Filters contém lógica que é executada antes e depois da execução de um View Result.Por exemplo,você pode querer modificar um ViewResult um pouco antes da view ser renderizada para o navegador.

Exception Filters são os últimos tipo de filter que são executados.Você pode usar exception filters para tratar erros disparados pela suas actions ou pelo resultado das suas actions.Você também pode usa-lo para criar um log de erros.

Cada tipo diferente de filter é executado numa ordem em particular.Se você quiser modificar a ordem de execução de cada filter do mesmo tipo você pode setar a propriedade ORDER do filter.

A classe base para todos os filters é System.Web.Mvc.FilterAttribute.Se você quiser implementar um tipo particular de filter,então você precisa criar uma classe que herde dessa classe base e implemente uma ou mais das seguintes intefaces:IAuthorizationFilter,IActionFilter,IResultFilter ou IExceptionFilter.

<h3>A classe base ActionFilterAttribute</h3>

Pra facilitar a sua vida quando você quiser implementar um filter próprio,o ASP.NET MVC inclui uma classe base <em>ActionFilterAttribute</em>.Essa classe implementa as interfaces <em>IResultFilter</em> e <em>IActionFilter</em> herda da classe filter.

A terminologia aqui não é perfeitamente consistente.Tecnicamente,uma classe que herda da classe <em>ActionFilterAttribute</em> é tanto um Action Filter quanto um Result Filter.O termo Action Filter é usado para se referir a qualquer tipo de filter no ASP.NET MVC.

A classe <em>ActionFilterAttribute</em> possui os seguintes métodos que você pode sobrescrever:

  • OnActionExecuting – Esse método é chamado antes de uma action ser executada
  • OnActionExecuted – Esse método é chamado depois de uma action ser executada
  • OnResultExecuting – Esse método é chamado antes de uma action result ser executada
  • OnResultExecuting – Esse método é chamado depois de uma action result ser executada

Vamos ver como implementar esses métodos.

Criando um Action Filter de Log

Pra ilustrar como criar nosso próprio action filter,nós iremos criar um filter que registre os estágios de processo de uma action para a janela Output do Visual Studio.Segue abaixo:

using System;
using System.Diagnostics;
using System.Web.Mvc;
using System.Web.Routing; 

namespace MvcApplication1.ActionFilters
{
public class LogActionFilter : ActionFilterAttribute

{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log(“OnActionExecuting”, filterContext.RouteData);
}

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log(“OnActionExecuted”, filterContext.RouteData);
}

public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log(“OnResultExecuting”, filterContext.RouteData);
}

public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log(“OnResultExecuted”, filterContext.RouteData);
}

private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values[“controller”];
var actionName = routeData.Values[“action”];
var message = String.Format(“{0} controller:{1} action:{2}”, methodName, controllerName, actionName);
Debug.WriteLine(message, “Action Filter Log”);
}

}
}

Os métodos OnActionExecuting, OnActionExecuted,OnResultExecuting e OnResultExecuted chamam o método Log.O nome do método e a RouteData são passados para o método Log.O método Log escreve uma mensagem na janela output do Visual Studio conforme abaixo:

O HomeController abaixo ilustra como podemos aplicar o Log Action Filter para um controle inteiro.Quando qualquer das actions deste controle for chamada,(Index ou About) os estagios do processo de execução serão mostrados na janela Output do Visual Studio.

using System.Web.Mvc;
using MvcApplication1.ActionFilters; 

namespace MvcApplication1.Controllers
{
[LogActionFilter]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}

public ActionResult About()
{

return View();
}
}
}

É isso aí!Até a próxima pessoal!

Use autenticação windows em seu website usando forms authentication

Aprenda a autenticar contas usando windows authentication.

Introdução

No último mês eu trabalhei em uma pequena tarefa de autenticar uma conta windows usando forms
Authentication.O propósito dessa tarefa era facilitar o logon de nossos usuários usando qualquer
conta válida do windows.Foi uma tarefa interessante,que eu decidi compartilhar com vocês.

Requerimentos

A aplicação deve autenticar usuários windows,usando forms authentication e o usuario logado
na conta do windows não deve se incomodar tendo que fazer um novo login na aplicação.Ele
deve poder se logar com qualquer conta windows válida.

Solução

Nós devemos seguir os seguintes passos para atingir nosso objetivo:

-Configurar as seções Authorization e Authentication no web.config
-Criar uma página de login e executar a lógica para autenticar usando a conta do windows
-Se as credenciais são válidas no passo anterior,gerar um “token” de autenticação assim o
usario poderá navegar nas diferentes páginas da sua aplicação.

Vamos lá,

Configurar Authorization e Authentication no web.config

Nós precisamos usar Forms authentication.O usuario vai entrar com suas credenciais do windows
no formulario e nós iremos valida-los:

1.<authentication mode=”Forms”>
2.   <forms loginUrl=”login.aspx” name=”.ASPXFORMSAUTH”></forms>
3.</authentication>

Para restringir acesso de usuários não logados,modifique a seção authorization no web.config:

1.<authorization>
2.   <deny users=”?”/>
3.</authorization>

Criar uma página de login para executar a lógica para autenticar as credencias do usuário do windows.

Nós precisamos criar uma página de login,para pegar informações de usuario e senha do usuario
e valida-las.

Nós temos diferentes formas de validar credenciais do Windows.A maneira que eu escolhi foi
o método LogonUser() de uma API Win32 chamada Advapi32.dll

O método LogonUser() tentar logar um usuario na máquina local.Esse método recebe um nome de usuario
uma senha e outras informações como parâmetros e retorna um boolean indicando se o usuario foi
logado ou não.Se retornar true significa que usuarios e senhas estão corretos.

Para usar esse método na sua classe,inclua o seguinte namespace:

1.using System.Runtime.InteropServices;

Depois adicione a seguinte declaração no método com o atributo DLLImport:

1.[DllImport(“ADVAPI32.dll”, EntryPoint = “LogonUserW”, SetLastError = true,
2.   CharSet = CharSet.Auto)]
3.public static extern bool LogonUser(string lpszUsername, string lpszDomain,
4.    string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

De acordo com a documentção MSDN:

lpszUsername[in]: string que especifica o nome de usuario.Esse é o nome da conta do logon.
Se você usar o formato “user principal name”(UPN) User@DnsDomainName,o parâmetro lpzszDomain
deve ficar nulo.

lpszDomain [in,opcional]:especifica o nome do dominio ou servidor que contém a conta de usuario
especificada no lpszUsername.

lpszPassword [in]:especifica a senha para a conta de usuario passada no lpszUsername.

dwLogonType[in]:O tipo de operação de logon.

dwLogonProvider[in]:especicifa o provedor de logon.

phToken[out]:Um ponteiro para uma variavel que contém um “token” que representa o usuario especifico.

Gerar um token de autenticação,se as credenciais forem autenticadas

Se as credenciais forem autenticadas no método LogoUser() então nós precisamos gerar um “token”
para que o usuario possa navegar nas paginas autorizadas da aplicação.
FormsAuthentication.RedirectFromLoginPage() ou FormsAuthentication.SetAuthCookie() podem ser usados para isso.

Aqui o código do clique do botão btnLogin pra autenticar e gerar o token de autenticação.
Comentários irão lhe ajudar a enteder o código:

01.protected void btnLogin_Click(object sender, EventArgs e)
02.{
03.   // Extract domain name from provided DomainUsername e.g Domainname\Username
04.   string domainName = GetDomainName(txtUserName.Text);
05.   // Extract user name from provided DomainUsername e.g Domainname\Username
06.   string userName = GetUsername(txtUserName.Text);
07.        IntPtr token = IntPtr.Zero;
08. //userName, domainName and Password parameters are very obvious.
09. /* dwLogonType (3rd parameter): I used LOGON32_LOGON_INTERACTIVE, This logon type is
10. intended for users who will be interactively using the computer, such as a user being
11. logged on by a terminal server, remote shell, or similar process. This logon type has
12. the additional expense of caching logon information for disconnected operations. For
13. more details about this parameter please see http://msdn.microsoft.com/en-
14. us/library/aa378184(VS.85).aspx */

15. /* dwLogonProvider (4th parameter) : I used LOGON32_PROVIDER_DEFAUL, This provider
16. uses the standard logon provider for the system. The default security provider is
17. negotiate, unless you pass NULL for the domain name and the user name is not in UPN
18. format. In this case, the default provider is NTLM. For more details about this
19. parameter please see http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx */

20. /* phToken (5th parameter): A pointer to a handle variable that receives a handle to
21. a token that represents the specified user. We can use this handler for impersonation
22. purpose. */

23.    bool result = LogonUser(userName, domainName, txtPassword.Text, 2, 0, ref token);
24.    if (result)
25.    {
26.       //If Sucessfully authenticated
27. /* When an unatuthenticated user try to visit any page of your application that is
28. only allowed to view by authenticated users then ASP.NET automatically redirect that
29. user to login form and add ReturnUrl query string parameter that contain the url of a
30. page that user want to visit, So that we can redirect the user to that page after
31. authenticated. FormsAuthentication.RedirectFromLoginPage() method not only redirect
32. the user to that page but also genrate an authentication token for that user. */

33.       if (string.IsNullOrEmpty(Request.QueryString[“ReturnUrl”]))
34.       {
35.          FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
36.       }
37. /* If ReturnUrl query string parameter is not present , then we need to generate
38. authentication token and redirect the user to any page ( acording to your application
39. need). FormsAuthentication.SetAuthCookie() method will generate Authentication
40. token*/

41.       else
42.       {
43.          FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
44.          Response.Redirect(“default.aspx”);
45.       }
46.   }
47.   else
48.   {
49.      //If not authenticated then display an error message

50.      Response.Write(“Invalid username or password.”);
51.   }
52.}

Lista 1: Login.aspx

<%@ Page Language=”C#” AutoEventWireup=”true” CodeFile=”Login.aspx.cs”
02.     Inherits=”Login” %>
03.<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
04. “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
05.<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
06.<head runat=”server”>
07.    <title>Windows Authentication Using Form Authentication</title>
08.    <style type=”text/css”>
09.        .style1
10.        {
11.            width: 100%;
12.        }
13.    </style>
14.</head>
15.<body>
16.    <form id=”form1″ runat=”server”>
17.    <div>
18.        <table>
19.            <tr>
20.                <td>
21.                    <asp:Label ID=”lblUserName” runat=”server”
22.                         Text=”User Name:”></asp:Label>
23.                </td>
24.                <td>
25.                    <asp:TextBox ID=”txtUserName” runat=”server”></asp:TextBox>
26.                </td>
27.            </tr>
28.            <tr>
29.                <td>
30.                    <asp:Label ID=”lblPassword” runat=”server”
31.                         Text=”Password:”></asp:Label>
32.                </td>
33.                <td>
34.                    <asp:TextBox ID=”txtPassword” runat=”server”></asp:TextBox>
35.                </td>
36.            </tr>
37.            <tr>
38.                <td>
39.                     </td>
40.                <td>
41.                    <asp:Button ID=”btnLogin” runat=”server” onclick=”btnLogin_Click”
42.                        Text=”Login” />
43.                </td>
44.            </tr>
45.        </table>
46.    </div>
47.    <p>
48.         </p>
49.    </form>
50.</body>
51.</html>

Lista 2: Login.aspx.cs

using System;
002.using System.Collections;
003.using System.Configuration;
004.using System.Data;
005.using System.Linq;
006.using System.Web;
007.using System.Web.Security;
008.using System.Web.UI;
009.using System.Web.UI.HtmlControls;
010.using System.Web.UI.WebControls;
011.using System.Web.UI.WebControls.WebParts;
012.using System.Runtime.InteropServices;
013.public partial class Login : System.Web.UI.Page
014.{
015.    [DllImport(“ADVAPI32.dll”, EntryPoint = “LogonUserW”, SetLastError = true,
016.       CharSet = CharSet.Auto)]
017.    public static extern bool LogonUser(string lpszUsername, string lpszDomain,
018.       string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
019.    /// <summary>
020.    /// Parses the string to pull the domain name out.
021.    /// </summary>
022.    /// <param name=”usernameDomain”>The string to parse that must contain the domain
023.    /// in either the domain\username or UPN format username@domain</param>
024.    /// <returns>The domain name or “” if not domain is found.</returns>

025.    public static string GetDomainName(string usernameDomain)
026.    {
027.        if (string.IsNullOrEmpty(usernameDomain))
028.        {
029.            throw (new ArgumentException(“Argument can’t be null.”,
030.               “usernameDomain”));
031.        }
032.        if (usernameDomain.Contains(“\\”))
033.        {
034.            int index = usernameDomain.IndexOf(“\\”);
035.            return usernameDomain.Substring(0, index);
036.        }
037.        else if (usernameDomain.Contains(“@”))
038.        {
039.            int index = usernameDomain.IndexOf(“@”);
040.            return usernameDomain.Substring(index + 1);
041.        }
042.        else
043.        {
044.            return “”;
045.        }
046.    }
047.    /// <summary>
048.    /// Parses the string to pull the user name out.
049.    /// </summary>
050.    /// <param name=”usernameDomain”>The string to parse that must contain the
051.    /// username in either the domain\username or UPN format username@domain</param>
052.    /// <returns>The username or the string if no domain is found.</returns>

053.    public static string GetUsername(string usernameDomain)
054.    {
055.        if (string.IsNullOrEmpty(usernameDomain))
056.        {
057.            throw (new ArgumentException(“Argument can’t be null.”,
058.               “usernameDomain”));
059.        }
060.        if (usernameDomain.Contains(“\\”))
061.        {
062.            int index = usernameDomain.IndexOf(“\\”);
063.            return usernameDomain.Substring(index + 1);
064.        }
065.        else if (usernameDomain.Contains(“@”))
066.        {
067.            int index = usernameDomain.IndexOf(“@”);
068.            return usernameDomain.Substring(0, index);
069.        }
070.        else
071.        {
072.            return usernameDomain;
073.        }
074.    }
075.protected void btnLogin_Click(object sender, EventArgs e)
076.{
077.   // Extract domain name from provided DomainUsername e.g Domainname\Username
078.      string domainName = GetDomainName(txtUserName.Text);
079.   // Extract user name from provided DomainUsername e.g Domainname\Username
080.      string userName = GetUsername(txtUserName.Text);
081.      IntPtr token = IntPtr.Zero;
082. //userName, domainName and Password parameters are very obvious.
083. /* dwLogonType (3rd parameter): I used LOGON32_LOGON_INTERACTIVE, This logon type is
084. intended for users who will be interactively using the computer, such as a user being
085. logged on by a terminal server, remote shell, or similar process. This logon type has
086. the additional expense of caching logon information for disconnected operations. For
087. more details about this parameter please see http://msdn.microsoft.com/en-
088. us/library/aa378184(VS.85).aspx */
089. /* dwLogonProvider (4th parameter) : I used LOGON32_PROVIDER_DEFAUL, This provider
090. uses the standard logon provider for the system. The default security provider is
091. negotiate, unless you pass NULL for the domain name and the user name is not in UPN
092. format. In this case, the default provider is NTLM. For more details about this
093. parameter please see http://msdn.microsoft.com/en-us/library/aa378184(VS.85).aspx */
094. /* phToken (5th parameter): A pointer to a handle variable that receives a handle to
095. a token that represents the specified user. We can use this handler for impersonation
096. purpose. */

097.    bool result = LogonUser(userName, domainName, txtPassword.Text, 2, 0, ref token);
098.    if (result)
099.    {
100.       //If Sucessfully authenticated
101. /* When an unatuthenticated user try to visit any page of your application that is
102. only allowed to view by authenticated users then ASP.NET automatically redirect that
103. user to login form and add ReturnUrl query string parameter that contain the url of a
104. page that user want to visit, So that we can redirect the user to that page after
105. authenticated. FormsAuthentication.RedirectFromLoginPage() method not only redirect
106. the user to that page but also genrate an authentication token for that user. */

107.       if (string.IsNullOrEmpty(Request.QueryString[“ReturnUrl”]))
108.       {
109.          FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
110.       }
111. /* If ReturnUrl query string parameter is not present , then we need to generate
112. authentication token and redirect the user to any page ( acording to your application
113. need). FormsAuthentication.SetAuthCookie() method will generate Authentication
114. token*/

115.       else
116.       {
117.          FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
118.          Response.Redirect(“default.aspx”);
119.       }
120.   }
121.   else
122.   {
123.      //If not authenticated then display an error message
124.      Response.Write(“Invalid username or password.”);
125.   }
126.}

Espero que aproveitem a dica,até a próxima!