ASP .NET Core -  Autenticação via Middleware

 Hoje vamos criar um middleware para realizar a autenticação em uma aplicação ASP .NET Core 5.0.

A autenticação é o processo de determinação da identidade de um usuário. A autorização é o processo de determinar se um usuário tem acesso a um recurso.

Na ASP.NET Core, a autenticação é tratada pela interface IAuthenticationService , que é usado pelo middleware de de autenticação. O serviço de autenticação usa handlers de autenticação registrados para concluir as ações relacionadas à autenticação. Os manipuladores de autenticação registrados e suas opções de configuração são chamados de 'schemas' ou  "esquemas" .

Hoje vamos criar um middleware para realizar a autenticação em uma aplicação ASP .NET Core MVC.

Criando o projeto ASP .NET Core 5.0 MVC

Abra o VS 2019 e clique em New Project selecionando o template 'ASP.NET Core Web App (Model-View-Controller)'  informe o nome do projeto, e a seguir selecione as opções conforme mostra a figura abaixo:

No projeto criado crie a pasta Middlewares e nesta pasta crie a classe AuthenticatioMiddleware com o código a seguir:

using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace Mvc5_Autenticacao.Middlewares
{
    public class AuthenticationMiddleware
    {
        private readonly RequestDelegate _next;

        public AuthenticationMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext httpContext)
        {
            var path = httpContext.Request.Path;

            if (path.HasValue && path.Value.StartsWith("/admin"))
            {
                if (httpContext.Session.GetString("username") == null)
                {
                    httpContext.Response.Redirect("/login/index");
                }
            }
            await _next(httpContext);
        }
    }
}

A classe de middleware deve incluir:

- Um construtor público com um parâmetro de tipo RequestDelegate.
- Um método público chamado Invoke ou InvokeAsync. Esse método precisa:
- Retornar um Task.
- Aceitar um primeiro parâmetro do tipo HttpContext.

Os parâmetros adicionais para o construtor e Invoke/InvokeAsync são preenchidos pela DI (injeção de dependência).

Note que definimos a lógica para realizar verificar no path do request a existência da rota 'admin' e a seguir verificamos se existe na sessão o 'username' com valor null. Neste caso redirecionamos para a rota /login/index.

A seguir na mesma pasta crie a classe estática AuthenticationMiddlewareExtension e defina o método de extensão UseAuthenticationMiddleware para incluir o middleware no pipeline do request HTTP.

        public static class AuthenticationMiddlewareExtensions
        {
            public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)
            {
                return builder.UseMiddleware<AuthenticationMiddleware>();
            }
        }

A seguir abra a classe Startup e no método Configure inclua a linha de código em destaque que define a utilização do middleware criado:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseSession();
            app.UseMiddleware<AuthenticationMiddleware>();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

Criando os controladores e views

Vamos agora criar os controladores e views no projeto para mostrar o middleware em ação.

Altere o código do controlador HomeController conforme abaixo:

using Microsoft.AspNetCore.Mvc;

namespace Mvc5_Autenticacao.Controllers
{
    [Route("home")]
    public class HomeController : Controller
    {

        [Route("")]
        [Route("index")]
        [Route("~/")]
        public IActionResult Index()
        {
            return View();
        }
    }
}

A seguir altere o código da View Index.cshtml deste controlador na pasta /Views/Home:

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index - Home</title>
</head>
<body>
    <h3>Home Page do controlador Home</h3>
    <hr />
</body>
</html>

Na pasta Controllers crie o controlador LoginController :

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Mvc5_Autenticacao.Controllers
{
    [Route("login")]
    public class LoginController : Controller
    {
        [Route("")]
        [Route("index")]
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [Route("process")]
        public IActionResult Process(string username, string password)
        {
            if (username != null && password != null && username.Equals("admin")
                   && password.Equals("123"))
            {
                HttpContext.Session.SetString("username", username);
                return View("Welcome");
            }
            else
            {
                ViewBag.error = "Invalid";
                return View("Index");
            }
        }
    }
}

Agora crie a view Index.cshml para este controller na pasta /Views/Login:

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index - Login</title>
</head>
<body>

    <h3>Loging</h3>
    @ViewBag.error
    <form method="post" asp-controller="login" asp-action="process">
        <table cellpadding="2" cellspacing="2">
            <tr>
                <td>Username</td>
                <td>
                    <input type="text" name="username" />
                </td>
            </tr>
            <tr>
                <td>Password</td>
                <td>
                    <input type="password" name="password" />
                </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>
                    <input type="submit" value="Login" />
                </td>
            </tr>
        </table>
    </form>
</body>
</html>

Crie também a página de boas vindas - Welcome.cshtml - na pasta /Views/Login :

 @using Microsoft.AspNetCore.Http;

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Bem-Vindo</title>
</head>
<body>

    Bem-Vindo,  @Context.Session.GetString("username")

</body>
</html

Criando a área Admin

Agora vamos criar uma área no projeto. As áreas são ASP.NET recurso usado para organizar a funcionalidade relacionada em um grupo como um separado:

O uso de áreas cria uma hierarquia para fins de roteamento adicionando outro parâmetro de rota, area , para o controlador ou Action.

Vamos criar agora no projeto uma Area chamada Admin.

Para isto crie no projeto a pasta Areas e dentro desta pasta crie a pasta Admin.

A seguir dentro da pasta Admin crie as pastas Controllers e Views :

Para que as áreas sejam reconhecidas temos que incluir no arquivo Startup do projeto, no método Configure(),  a configuração do endpoint conforme abaixo:

   ...

  app.UseEndpoints(endpoints =>
  {
                endpoints.MapControllerRoute(
                 name: "AdminArea",
                 pattern: "{area:exists}/{controller=Admin}/{action=Index}/{id?}")
;

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });

  }

A seguir crie dentro da pasta Controllers da área Admin o controlador ProductController:

using Microsoft.AspNetCore.Mvc;

namespace Mvc5_Autenticacao.Areas.Admin.Controllers
{
    [Area("admin")]
    [Route("admin/product")]
    public class ProductController : Controller
    {
        [Route("")]
        [Route("index")]
        public IActionResult Index()
        {
            return View();
        }
    }
}

Crie também a view Index.cshtml para este controlador na pasta /Views/Product

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index - Product</title>
</head>
<body>

    <h2>Página dos Produtos - Área Admin</h2>
    <hr />
</body>
</html>

Finalmente defina no arquivo  _Layout.cshml da pasta /Views/Shared o código para exibir o menu Product:

 <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
        <ul class="navbar-nav flex-grow-1">
             <li class="nav-item">
                  <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
              </li>
            <li class="nav-item">
               <a class="nav-link text-dark" asp-area="Admin" asp-controller="Product" asp-action="Index">Product</a>
           </li>
  </ul>
</div>

Agora é só alegria...

Inicie o projeto e tente acessar a url : http://localhost:xxxx/admin/product/index

Seremos direcionados para a página de login:


Informe as credenciais : admin e senha 123 e clique em Login. 
Seremos direcionados para a página Welcome:

Pegue o projeto aqui:    Mvc5_Autenticacao.zip

"E Jesus clamou, e disse: Quem crê em mim, crê, não em mim, mas naquele que me enviou.
E quem me vê a mim, vê aquele que me enviou."
João 12:44,45

Referências:


José Carlos Macoratti