Heim >Backend-Entwicklung >C#.Net-Tutorial >Implementieren Sie eine Token-basierte Identitätsauthentifizierungsinstanz in ASP.NET Core

Implementieren Sie eine Token-basierte Identitätsauthentifizierungsinstanz in ASP.NET Core

高洛峰
高洛峰Original
2016-12-26 10:13:473219Durchsuche

In der Vergangenheit basierte die Identitätsauthentifizierung auf der Website auf der Cookie-Sitzungsauthentifizierung Es gibt also ein Problem: Diese Clients wissen nicht, was Cookies sind. (Cookies sind eigentlich kleine Tricks von Browsern, um Sitzungen aufrechtzuerhalten, aber HTTP selbst ist zustandslos und alle verschiedenen Clients, die bereitgestellt werden können, sind APIs für HTTP-Operationen)

Basierend auf Token Die Identitätsauthentifizierung wurde als Reaktion darauf geboren Es ist offener und bietet eine höhere Sicherheit.

Es gibt viele Möglichkeiten, die tokenbasierte Identitätsauthentifizierung zu implementieren, wir verwenden hier jedoch nur die von Microsoft bereitgestellte API.

Das folgende Beispiel führt Sie dazu, eine Identitätsauthentifizierung basierend auf einem Beare-Token mit Microsoft JwtSecurityTokenHandler durchzuführen.

Hinweis: Bei diesem Artikel handelt es sich um eine Schritt-für-Schritt-Anleitung. Damit Ihnen nicht schwindelig wird, ist es sinnvoll, den vollständigen Code herunterzuladen und die Codestruktur zu analysieren.

Projekt erstellen

Erstellen Sie ein neues Projekt in VS, wählen Sie ASP.NET Core Web Application (.NET Core) als Projekttyp aus und geben Sie den Projektnamen als CSTokenBaseAuth ein

Codierung

Erstellen Sie einige Hilfsklassen

Erstellen Sie einen Ordner Auth im Projektstammverzeichnis und fügen Sie zwei Dateien RSAKeyHelper.cs und TokenAuthOption.cs hinzu

In RSAKeyHelper.cs

using System.Security.Cryptography;
 
namespace CSTokenBaseAuth.Auth
{
  public class RSAKeyHelper
  {
    public static RSAParameters GenerateKey()
    {
      using (var key = new RSACryptoServiceProvider(2048))
      {
        return key.ExportParameters(true);
      }
    }
  }
}

in TokenAuthOption.cs

using System;
using Microsoft.IdentityModel.Tokens;
 
namespace CSTokenBaseAuth.Auth
{
  public class TokenAuthOption
  {
    public static string Audience { get; } = "ExampleAudience";
    public static string Issuer { get; } = "ExampleIssuer";
    public static RsaSecurityKey Key { get; } = new RsaSecurityKey(RSAKeyHelper.GenerateKey());
    public static SigningCredentials SigningCredentials { get; } = new SigningCredentials(Key, SecurityAlgorithms.RsaSha256Signature);
 
    public static TimeSpan ExpiresSpan { get; } = TimeSpan.FromMinutes(20);
  }
}

Startup.cs

Fügen Sie den folgenden Code in „ConfigureServices“ hinzu:

services.AddAuthorization(auth =>
{
  auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
    .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌)
    .RequireAuthenticatedUser().Build());
});

Der vollständige Code sollte so aussehen

public void ConfigureServices(IServiceCollection services)
{
  // Add framework services.
  services.AddApplicationInsightsTelemetry(Configuration);
  // Enable the use of an [Authorize("Bearer")] attribute on methods and classes to protect.
  services.AddAuthorization(auth =>
  {
    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
      .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌)
      .RequireAuthenticatedUser().Build());
  });
  services.AddMvc();
}

Fügen Sie den folgenden Code in der Methode „Configure“ hinzu

app.UseExceptionHandler(appBuilder => {
  appBuilder.Use(async (context, next) => {
    var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
    //when authorization has failed, should retrun a json message to client
    if (error != null && error.Error is SecurityTokenExpiredException)
    {
      context.Response.StatusCode = 401;
      context.Response.ContentType = "application/json";
      await context.Response.WriteAsync(JsonConvert.SerializeObject(
        new { authenticated = false, tokenExpired = true }
      ));
    }
    //when orther error, retrun a error message json to client
    else if (error != null && error.Error != null)
    {
      context.Response.StatusCode = 500;
      context.Response.ContentType = "application/json";
      await context.Response.WriteAsync(JsonConvert.SerializeObject(
        new { success = false, error = error.Error.Message }
      ));
    }
    //when no error, do next.
    else await next();
  });
});

Dieser Code wird hauptsächlich für Handle-Fehler verwendet, z Identitätsauthentifizierung Eine Ausnahme wird ausgelöst, wenn sie fehlschlägt. Diese Ausnahme wird hier behandelt.

Fügen Sie als Nächstes den folgenden Code in derselben Methode hinzu:

app.UseExceptionHandler(appBuilder => {
  appBuilder.Use(async (context, next) => {
    var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
 
    //when authorization has failed, should retrun a json message to client
    if (error != null && error.Error is SecurityTokenExpiredException)
    {
      context.Response.StatusCode = 401;
      context.Response.ContentType = "application/json";
 
      await context.Response.WriteAsync(JsonConvert.SerializeObject(
        new { authenticated = false, tokenExpired = true }
      ));
    }
    //when orther error, retrun a error message json to client
    else if (error != null && error.Error != null)
    {
      context.Response.StatusCode = 500;
      context.Response.ContentType = "application/json";
      await context.Response.WriteAsync(JsonConvert.SerializeObject(
        new { success = false, error = error.Error.Message }
      ));
    }
    //when no error, do next.
    else await next();
  });
});

JwtBearerAuthentication anwenden

app.UseJwtBearerAuthentication(new JwtBearerOptions {
  TokenValidationParameters = new TokenValidationParameters {
    IssuerSigningKey = TokenAuthOption.Key,
    ValidAudience = TokenAuthOption.Audience,
    ValidIssuer = TokenAuthOption.Issuer,
    ValidateIssuerSigningKey = true,
    ValidateLifetime = true,
    ClockSkew = TimeSpan.FromMinutes(0)
  }
});

Der vollständige Code sollte so aussehen

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using CSTokenBaseAuth.Auth;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
 
namespace CSTokenBaseAuth
{
  public class Startup
  {
    public Startup(IHostingEnvironment env)
    {
      var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
 
      if (env.IsEnvironment("Development"))
      {
        // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
        builder.AddApplicationInsightsSettings(developerMode: true);
      }
 
      builder.AddEnvironmentVariables();
      Configuration = builder.Build();
    }
 
    public IConfigurationRoot Configuration { get; }
 
    // This method gets called by the runtime. Use this method to add services to the container
    public void ConfigureServices(IServiceCollection services)
    {
      // Add framework services.
      services.AddApplicationInsightsTelemetry(Configuration);
 
      // Enable the use of an [Authorize("Bearer")] attribute on methods and classes to protect.
      services.AddAuthorization(auth =>
      {
        auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
          .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌)
          .RequireAuthenticatedUser().Build());
      });
 
      services.AddMvc();
    }
 
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
      loggerFactory.AddConsole(Configuration.GetSection("Logging"));
      loggerFactory.AddDebug();
 
      app.UseApplicationInsightsRequestTelemetry();
 
      app.UseApplicationInsightsExceptionTelemetry();
 
      #region Handle Exception
      app.UseExceptionHandler(appBuilder => {
        appBuilder.Use(async (context, next) => {
          var error = context.Features[typeof(IExceptionHandlerFeature)] as IExceptionHandlerFeature;
 
          //when authorization has failed, should retrun a json message to client
          if (error != null && error.Error is SecurityTokenExpiredException)
          {
            context.Response.StatusCode = 401;
            context.Response.ContentType = "application/json";
 
            await context.Response.WriteAsync(JsonConvert.SerializeObject(
              new { authenticated = false, tokenExpired = true }
            ));
          }
          //when orther error, retrun a error message json to client
          else if (error != null && error.Error != null)
          {
            context.Response.StatusCode = 500;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(JsonConvert.SerializeObject(
              new { success = false, error = error.Error.Message }
            ));
          }
          //when no error, do next.
          else await next();
        });
      });
      #endregion
 
      #region UseJwtBearerAuthentication
      app.UseJwtBearerAuthentication(new JwtBearerOptions {
        TokenValidationParameters = new TokenValidationParameters {
          IssuerSigningKey = TokenAuthOption.Key,
          ValidAudience = TokenAuthOption.Audience,
          ValidIssuer = TokenAuthOption.Issuer,
          ValidateIssuerSigningKey = true,
          ValidateLifetime = true,
          ClockSkew = TimeSpan.FromMinutes(0)
        }
      });
      #endregion
 
      app.UseMvc(routes =>
      {
        routes.MapRoute(
          name: "default",
          template: "{controller=Login}/{action=Index}");
      });
    }
  }
}

Erstellen Sie eine neue Web-API-Controller-Klasse in Controllers und benennen Sie sie TokenAuthController.cs. Wir vervollständigen die Anmeldeautorisierung hier

Fügen Sie zwei Klassen unter derselben Datei hinzu, die zur Simulation des Benutzermodells und des Benutzerspeichers verwendet werden. Der Code sollte wie folgt aussehen

public class User
{
  public Guid ID { get; set; }
  public string Username { get; set; }
  public string Password { get; set; }
}
 
public static class UserStorage
{
  public static List<User> Users { get; set; } = new List<User> {
    new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" },
    new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" },
    new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" }
  };
}

Als nächstes fügen Sie die folgende Methode in TokenAuthController.cs hinzu

private string GenerateToken(User user, DateTime expires)
{
  var handler = new JwtSecurityTokenHandler();
   
  ClaimsIdentity identity = new ClaimsIdentity(
    new GenericIdentity(user.Username, "TokenAuth"),
    new[] {
      new Claim("ID", user.ID.ToString())
    }
  );
 
  var securityToken = handler.CreateToken(new SecurityTokenDescriptor
  {
    Issuer = TokenAuthOption.Issuer,
    Audience = TokenAuthOption.Audience,
    SigningCredentials = TokenAuthOption.SigningCredentials,
    Subject = identity,
    Expires = expires
  });
  return handler.WriteToken(securityToken);
}

Diese Methode generiert als nächstes nur ein Auth-Token Fügen wir eine weitere Methode zum Aufrufen hinzu

Fügen Sie den folgenden Code in derselben Datei hinzu

[HttpPost]
public string GetAuthToken(User user)
{
  var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password);
 
  if (existUser != null)
  {
    var requestAt = DateTime.Now;
    var expiresIn = requestAt + TokenAuthOption.ExpiresSpan;
    var token = GenerateToken(existUser, expiresIn);
 
    return JsonConvert.SerializeObject(new {
      stateCode = 1,
      requertAt = requestAt,
      expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds,
      accessToken = token
    });
  }
  else
  {
    return JsonConvert.SerializeObject(new { stateCode = -1, errors = "Username or password is invalid" });
  }
}

Der vollständige Code der Datei sollte sein Auf diese Weise

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.IdentityModel.Tokens;
using CSTokenBaseAuth.Auth;
 
namespace CSTokenBaseAuth.Controllers
{
  [Route("api/[controller]")]
  public class TokenAuthController : Controller
  {
    [HttpPost]
    public string GetAuthToken(User user)
    {
      var existUser = UserStorage.Users.FirstOrDefault(u => u.Username == user.Username && u.Password == user.Password);
 
      if (existUser != null)
      {
        var requestAt = DateTime.Now;
        var expiresIn = requestAt + TokenAuthOption.ExpiresSpan;
        var token = GenerateToken(existUser, expiresIn);
 
        return JsonConvert.SerializeObject(new {
          stateCode = 1,
          requertAt = requestAt,
          expiresIn = TokenAuthOption.ExpiresSpan.TotalSeconds,
          accessToken = token
        });
      }
      else
      {
        return JsonConvert.SerializeObject(new { stateCode = -1, errors = "Username or password is invalid" });
      }
    }
 
    private string GenerateToken(User user, DateTime expires)
    {
      var handler = new JwtSecurityTokenHandler();
       
      ClaimsIdentity identity = new ClaimsIdentity(
        new GenericIdentity(user.Username, "TokenAuth"),
        new[] {
          new Claim("ID", user.ID.ToString())
        }
      );
 
      var securityToken = handler.CreateToken(new SecurityTokenDescriptor
      {
        Issuer = TokenAuthOption.Issuer,
        Audience = TokenAuthOption.Audience,
        SigningCredentials = TokenAuthOption.SigningCredentials,
        Subject = identity,
        Expires = expires
      });
      return handler.WriteToken(securityToken);
    }
  }
 
  public class User
  {
    public Guid ID { get; set; }
 
    public string Username { get; set; }
 
    public string Password { get; set; }
  }
 
  public static class UserStorage
  {
    public static List<User> Users { get; set; } = new List<User> {
      new User {ID=Guid.NewGuid(),Username="user1",Password = "user1psd" },
      new User {ID=Guid.NewGuid(),Username="user2",Password = "user2psd" },
      new User {ID=Guid.NewGuid(),Username="user3",Password = "user3psd" }
    };
  }
}

Als nächstes werden wir den Teil zur Autorisierungsüberprüfung abschließen

Erstellen Sie eine neue Web-API-Controller-Klasse in Controllers und nennen Sie sie ValuesController .cs

Fügen Sie den folgenden Code hinzu

public string Get()
{
  var claimsIdentity = User.Identity as ClaimsIdentity;
 
  var id = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "ID").Value;
 
  return $"Hello! {HttpContext.User.Identity.Name}, your ID is:{id}";
}

Fügen Sie der Methode dekorative Attribute hinzu

[HttpGet]
[Authorize("Bearer")]
 
完整的文件代码应该是这样
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
 
namespace CSTokenBaseAuth.Controllers
{
  [Route("api/[controller]")]
  public class ValuesController : Controller
  {
    [HttpGet]
    [Authorize("Bearer")]
    public string Get()
    {
      var claimsIdentity = User.Identity as ClaimsIdentity;
 
      var id = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "ID").Value;
 
      return $"Hello! {HttpContext.User.Identity.Name}, your ID is:{id}";
    }
  }
}

Schließlich fügen wir die Ansicht hinzu

Erstellen Sie eine neue Web-Controller-Klasse in Controllers und nennen Sie sie LoginController.cs

Der Code sollte so aussehen

using Microsoft.AspNetCore.Mvc;
 
namespace CSTokenBaseAuth.Controllers
{
  [Route("[controller]/[action]")]
  public class LoginController : Controller
  {
    public IActionResult Index()
    {
      return View();
    }
  }
}

Erstellen Sie im Projektansichtsverzeichnis ein neues Verzeichnis mit dem Namen „Login“ und erstellen Sie darin eine neue Datei „Index.cshtml“.

Der Code sollte so aussehen

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
</head>
<body>
  <button id="getToken">getToken</button>
  <button id="requestAPI">requestAPI</button>
 
  <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
  <script>
    $(function () {
      var accessToken = undefined;
 
      $("#getToken").click(function () {
        $.post(
          "/api/TokenAuth",
          { Username: "user1", Password: "user1psd" },
          function (data) {
            console.log(data);
            if (data.stateCode == 1)
            {
              accessToken = data.accessToken;
 
              $.ajaxSetup({
                headers: { "Authorization": "Bearer " + accessToken }
              });
            }
          },
          "json"
        );
      })
 
      $("#requestAPI").click(function () {
        $.get("/api/Values", {}, function (data) {
          alert(data);
        }, "text");
      })
    })
  </script>
</body>
</html>

Bitte unterstützen Sie die chinesische PHP-Website.

Weitere verwandte Artikel zur Implementierung eines Token-basierten Identitätsauthentifizierungsbeispiels in ASP.NET Core finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn