php 身份认证 claim,在ASP.NET Core中实现一个Token base的身份认证实例

以前在web端的身份认证都是基于Cookie | Session的身份认证, 在没有更多的终端出现之前,这样做也没有什么问题,但在Web API时代,你所需要面对的就不止是浏览器了,还有各种客户端,这样就有了一个问题,这些客户端是不知道cookie是什么鬼的。 (cookie其实是浏览器搞出来的小猫腻,用来保持会话的,但HTTP本身是无状态的, 各种客户端能提供的无非也就是HTTP操作的API)

而基于Token的身份认证就是应对这种变化而生的,它更开放,安全性也更高。

基于Token的身份认证有很多种实现方式,但我们这里只使用微软提供的API。

接下来的例子将带领大家完成一个使用微软JwtSecurityTokenHandler完成一个基于beare token的身份认证。

注意:这种文章属于Step by step教程,跟着做才不至于看晕,下载完整代码分析代码结构才有意义。

创建项目

在VS中新建项目,项目类型选择ASP.NET Core Web Application(.NET Core), 输入项目名称为CSTokenBaseAuth

Coding

创建一些辅助类

在项目根目录下创建一个文件夹Auth,并添加RSAKeyHelper.cs以及TokenAuthOption.cs两个文件

在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);

}

}

}

}

在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

在ConfigureServices中添加如下代码:services.AddAuthorization(auth =>

{

auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()

.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌)

.RequireAuthenticatedUser().Build());

});

完整的代码应该是这样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();

}

在Configure方法中添加如下代码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();

});

});

这段代码主要是Handle Error用的,比如当身份认证失败的时候会抛出异常,而这里就是处理这个异常的。

接下来在相同的方法中添加如下代码,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();

});

});

应用JwtBearerAuthenticationapp.UseJwtBearerAuthentication(new JwtBearerOptions {

TokenValidationParameters = new TokenValidationParameters {

IssuerSigningKey = TokenAuthOption.Key,

ValidAudience = TokenAuthOption.Audience,

ValidIssuer = TokenAuthOption.Issuer,

ValidateIssuerSigningKey = true,

ValidateLifetime = true,

ClockSkew = TimeSpan.FromMinutes(0)

}

});

完整的代码应该是这样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}");

});

}

}

}

在Controllers中新建一个Web API Controller Class,命名为TokenAuthController.cs。我们将在这里完成登录授权

在同文件下添加两个类,分别用来模拟用户模型,以及用户存储,代码应该是这样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 Users { get; set; } = new List {

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" }

};

}

接下来在TokenAuthController.cs中添加如下方法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);

}

该方法仅仅只是生成一个Auth Token,接下来我们来添加另外一个方法来调用它

在相同文件中添加如下代码[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" });

}

}

该文件完整的代码应该是这样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 Users { get; set; } = new List {

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" }

};

}

}

接下来我们来完成授权验证部分

在Controllers中新建一个Web API Controller Class,命名为ValuesController.cs

在其中添加如下代码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}";

}

为方法添加装饰属性[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}";

}

}

}

最后让我们来添加视图

在Controllers中新建一个Web Controller Class,命名为LoginController.cs

其中的代码应该是这样using Microsoft.AspNetCore.Mvc;

namespace CSTokenBaseAuth.Controllers

{

[Route("[controller]/[action]")]

public class LoginController : Controller

{

public IActionResult Index()

{

return View();

}

}

}

在项目Views目录下新建一个名为Login的目录,并在其中新建一个Index.cshtml文件。

代码应该是这个样子

getToken

requestAPI

$(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");

})

})

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持php中文网。

更多在ASP.NET Core中实现一个Token base的身份认证实例相关文章请关注PHP中文网!

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值