在.NET Core中想给API进行安全认证,最简单的无非就是Jwt,悠然记得一年前写的Jwt Demo,现在拿回来改成.NET Core的,但是在编码上的改变并不大,因为Jwt已经足够强大了。在项目中分为 DotNetCore_Jwt_Server 以及 DotNetCore_Jwt_Client ,从名字就可以看出来是啥意思,博客园高手云集,我就不多诉说,这篇博客就当是一篇记录。

  当然本案例是Server&Client双项目,如果你要合成自己发证的形式,那你就自己改下代码玩。

  在Server层都会有分发Token的服务,在其中做了用户密码判断,随后根据 Claim 生成 jwtToken 的操作。

  其生成Token的服务代码:

  1. namespace DotNetCore_Jwt_Server.Services
  2. {
  3. public interface ITokenService
  4. {
  5. string GetToken(User user);
  6. }
  7. public class TokenService : ITokenService
  8. {
  9. private readonly JwtSetting _jwtSetting;
  10. public TokenService(IOptions<JwtSetting> option)
  11. {
  12. _jwtSetting = option.Value;
  13. }
  14. public string GetToken(User user)
  15. {
  16. //创建用户身份标识,可按需要添加更多信息
  17. var claims = new Claim[]
  18. {
  19. new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
  20. new Claim("id", user.Id.ToString(), ClaimValueTypes.Integer32),
  21. new Claim("name", user.Name),
  22. new Claim("admin", user.IsAdmin.ToString(),ClaimValueTypes.Boolean)
  23. };
  24.  
  25. //创建令牌
  26. var token = new JwtSecurityToken(
  27. issuer: _jwtSetting.Issuer,
  28. audience: _jwtSetting.Audience,
  29. signingCredentials: _jwtSetting.Credentials,
  30. claims: claims,
  31. notBefore: DateTime.Now,
  32. expires: DateTime.Now.AddSeconds(_jwtSetting.ExpireSeconds)
  33. );
  34. string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
  35. return jwtToken;
  36. }
  37. }
  38. }

在获取Token中我们依赖注入服务到控制器中,随后依赖它进行认证并且分发Token,

  1. public class ValuesController : ControllerBase
  2. {
  3. private readonly IUserService _userService;
  4. private readonly ITokenService _tokenService;
  5.  
  6. public ValuesController(IUserService userService,
  7. ITokenService tokenService)
  8. {
  9. _userService = userService;
  10. _tokenService = tokenService;
  11. }
  12. [HttpGet]
  13. public async Task<string> Get()
  14. {
  15. await Task.CompletedTask;
  16. return "Welcome the Json Web Token Solucation!";
  17. }
  18. [HttpGet("getToken")]
  19. public async Task<string> GetTokenAsync(string name, string password)
  20. {
  21. var user = await _userService.LoginAsync(name, password);
  22. if (user == null)
  23. return "Login Failed";
  24.  
  25. var token = _tokenService.GetToken(user);
  26. var response = new
  27. {
  28. Status = true,
  29. Token = token,
  30. Type = "Bearer"
  31. };
  32. return JsonConvert.SerializeObject(response);
  33. }
  34. }

  随后,我们又在项目配置文件中填写了几个字段,相关备注已注释,但值得说明的是有位朋友问我,服务器端生成的Token不需要保存吗,比如Redis或者是Session,其实Jwt Token是无状态的,他们之间的对比第一个是你的token解密出来的信息正确与否,第二部则是看看你 SecurityKey 是否正确,就这样他们的认证才会得出结果。

  1. "JwtSetting": {
  2. "SecurityKey": "d0ecd23c-dfdb-4005-a2ea-0fea210c858a", // 密钥
  3. "Issuer": "jwtIssuertest", // 颁发者
  4. "Audience": "jwtAudiencetest", // 接收者
  5. "ExpireSeconds": // 过期时间
  6. }

  随后我们需要DI两个接口以及初始化设置相关字段。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.Configure<JwtSetting>(Configuration.GetSection("JwtSetting"));
  4. services.AddScoped<IUserService, UserService>();
  5. services.AddScoped<ITokenService, TokenService>();
  6. services.AddControllers();
  7. }

  在Client中,我一般会创建一个中间件用于接受认证结果,AspNetCore Jwt 源码中给我们提供了中间件,我们在进一步扩展,其源码定义如下:

  1. /// <summary>
  2. /// Extension methods to expose Authentication on HttpContext.
  3. /// </summary>
  4. public static class AuthenticationHttpContextExtensions
  5. {/// <summary>
  6. /// Extension method for authenticate.
  7. /// </summary>
  8. /// <param name="context">The <see cref="HttpContext"/> context.</param>
  9. /// <param name="scheme">The name of the authentication scheme.</param>
  10. /// <returns>The <see cref="AuthenticateResult"/>.</returns>
  11. public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>
  12. context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme);
      }

  其该扩展会返回一个 AuthenticateResult 类型的结果,其定义部分是这样的,我们就可以将计就计,给他来个连环套。

连环套直接接受 httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme)  返回回来的值,随后进行判断返回相应的Http响应码。

  1. public class AuthMiddleware
  2. {
  3. private readonly RequestDelegate _next;
  4.  
  5. public AuthMiddleware(RequestDelegate next)
  6. {
  7. _next = next;
  8. }
  9. public async Task Invoke(HttpContext httpContext)
  10. {
  11. var result = await httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
  12. if (!result.Succeeded)
  13. {
  14. httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
  15. await httpContext.Response.WriteAsync("Authorize error");
  16. }
  17. else
  18. {
  19. httpContext.User = result.Principal;
  20. await _next.Invoke(httpContext);
  21. }
  22. }
  23. }

  当然你也得在Client中添加认证的一些设置,它和Server端的 IssuerSigningKey 一定要对应,否则认证失败。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddHttpContextAccessor();
  4. services.AddScoped<IIdentityService, IdentityService>();
  5. var jwtSetting = new JwtSetting();
  6. Configuration.Bind("JwtSetting", jwtSetting);
  7.  
  8. services.AddCors(options =>
  9. {
  10. options.AddPolicy("any", builder =>
  11. {
  12. builder.AllowAnyOrigin() //允许任何来源的主机访问
  13. .AllowAnyMethod()
  14. .AllowAnyHeader();
  15.  
  16. });
  17. });
  18.  
  19. services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  20. .AddJwtBearer(options =>
  21. {
  22. options.TokenValidationParameters = new TokenValidationParameters
  23. {
  24. ValidIssuer = jwtSetting.Issuer,
  25. ValidAudience = jwtSetting.Audience,
  26. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecurityKey)),
  27. 默认 300s
  28. ClockSkew = TimeSpan.Zero
  29. };
  30. });
  31. services.AddControllers();
  32. }

  随后,你就可以编写带需认证才可以访问的API了,如果认证失败则会返回401的错误响应。

  1.   [Route("api/[controller]")]
  2. [ApiController]
  3. public class ValuesController : ControllerBase
  4. {
  5. private readonly IIdentityService _identityService;
  6. public ValuesController(IIdentityService identityService)
  7. {
  8. _identityService = identityService;
  9. }
  10. [HttpGet]
  11. [Authorize]
  12. public async Task<string> Get()
  13. {
  14. await Task.CompletedTask;
  15. return $"{_identityService.GetUserId()}:{_identityService.GetUserName()}";
  16. }

  值得一提的是,我们可以根据 IHttpContextAccessor 以来注入到我们的Service或者Api中,它是一个当前请求的认证信息上下文,这将有利于你获取用户信息去做该做的事情。

  1. public class IdentityService : IIdentityService
  2. {
  3. private readonly IHttpContextAccessor _context;
  4. public IdentityService(IHttpContextAccessor context)
  5. {
  6. _context = context;
  7. }
  8. public int GetUserId()
  9. {
  10. var nameId = _context.HttpContext.User.FindFirst("id");
  11.  
  12. return nameId != null ? Convert.ToInt32(nameId.Value) : ;
  13. }
  14. public string GetUserName()
  15. {
  16. return _context.HttpContext.User.FindFirst("name")?.Value;
  17. }
  18. }

  在源码中该类的定义如下,实际上我们可以看到只不过是判断了当前的http上下文吧,所以我们得出,如果认证失败,上下本信息也是空的。

  1. public class HttpContextAccessor : IHttpContextAccessor
  2. {
  3. private static AsyncLocal<HttpContextHolder> _httpContextCurrent = new AsyncLocal<HttpContextHolder>();
  4.  
  5. public HttpContext HttpContext
  6. {
  7. get
  8. {
  9. return _httpContextCurrent.Value?.Context;
  10. }
  11. set
  12. {
  13. var holder = _httpContextCurrent.Value;
  14. if (holder != null)
  15. {
  16. // Clear current HttpContext trapped in the AsyncLocals, as its done.
  17. holder.Context = null;
  18. }
  19.  
  20. if (value != null)
  21. {
  22. // Use an object indirection to hold the HttpContext in the AsyncLocal,
  23. // so it can be cleared in all ExecutionContexts when its cleared.
  24. _httpContextCurrent.Value = new HttpContextHolder { Context = value };
  25. }
  26. }
  27. }
  28.  
  29. private class HttpContextHolder
  30. {
  31. public HttpContext Context;
  32. }
  33. }

  如果要通过js来测试代码,您可以添加请求头来进行认证,beforeSend是在请求之前的事件。

  1. beforeSend : function(request) {
  2.   request.setRequestHeader("Authorization", sessionStorage.getItem("Authorization"));
  3. }

 好了,今天就说到这,代码地址在https://github.com/zaranetCore/DotNetCore_Jwt 中。

在.NET Core中使用Jwt对API进行认证的更多相关文章

  1. 如何简单的在 ASP.NET Core 中集成 JWT 认证?

    前情提要:ASP.NET Core 使用 JWT 搭建分布式无状态身份验证系统 文章超长预警(1万字以上),不想看全部实现过程的同学可以直接跳转到末尾查看成果或者一键安装相关的 nuget 包 自上一 ...

  2. .net core中使用jwt进行认证

    JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息.由于此信息是经过数字签名的,因此可以被验证和信任 ...

  3. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  4. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  5. 如何设计出和 ASP.NET Core 中 Middleware 一样的 API 方法?

    由于笔者时间有限,无法写更多的说明文本,且主要是自己用来记录学习点滴,请谅解,下面直接贴代码了(代码中有一些说明): 01-不好的设计 代码: using System; namespace Desi ...

  6. spring boot 2 集成JWT实现api接口认证

    JSON Web Token(JWT)是目前流行的跨域身份验证解决方案.官网:https://jwt.io/本文使用spring boot 2 集成JWT实现api接口验证. 一.JWT的数据结构 J ...

  7. ASP.NET Core 3.0 WebApi中使用Swagger生成API文档简介

    参考地址,官网:https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/getting-started-with-swashbuckle?view ...

  8. EF Core中通过Fluent API完成对表的配置

    EF Core中通过Fluent API完成对表的配置 设置实体在数据库中的表名 通过ToTable可以为数据模型在数据库中自定义表名,如果不配置,则表名为模型名的复数形式 public class ...

  9. ASP.NET Core系列:JWT身份认证

    1. JWT概述 JSON Web Token(JWT)是目前流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io JWT的实现方式是将用户信息存储在客户端,服务端不进行保存. ...

随机推荐

  1. ios发送短信验证码计时器的swift实现

    转载自:http://www.jianshu.com/p/024dd2d6e6e6# Update: Xcode 8.2.1 Swift 3 先介绍一下 属性观测器(Property Observer ...

  2. Windows 10 中CPU虚拟化已开启,但是docker无法运行

    在管理员模式下的PowerShell中执行: bcdedit /set hypervisorlaunchtype Auto 然后重启电脑即可

  3. 面向云原生的混沌工程工具-ChaosBlade

    作者 | 肖长军(穹谷)阿里云智能事业群技术专家   导读:随着云原生系统的演进,如何保障系统的稳定性受到很大的挑战,混沌工程通过反脆弱思想,对系统注入故障,提前发现系统问题,提升系统的容错能力.Ch ...

  4. (JavaScript) 字符串转16进制

    function strToBase64() { var str = "https://www.baidu.com/"; var val = ""; for ( ...

  5. 11 一步一步Zabbix4.4.0系统教你实现sendEmail邮件报警

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 一步一步Zabbix4.4.0系统教你实现sendEmail邮件报警 sendEmail是一个轻量 ...

  6. NOIP模拟 30

    补坑,很多都忘了. T1 树 像我这种人都能考场A掉当然是道水题辣 求出每条有向边的期望就好了 T2 回文串 当时毫无思路,暴力写挂. 首先把B转过来,那么都变成后缀的前缀拼起来 对于每一个LCP,他 ...

  7. Java中打印日志,这4点很重要!

    目录 一.预先判断日志级别 二.避免无效日志打印 三.区别对待错误日志 四.保证记录完整内容 打印日志,要注意下面4点. 一.预先判断日志级别 对DEBUG.INFO级别的日志,必须使用条件输出或者使 ...

  8. Vue+element UI实现“回到顶部”按钮组件

    介绍 这是一个可以快速回到页面顶部的组件,当用户浏览到页面底部的时候,通过点击按钮,可快速回到页面顶部. 使用方法 由于该组件是基于element-UI进行二次封装的,所以在使用该组件时请务必安装el ...

  9. 使用 vue-element-admin 动态路由渲染

    附上:vue-element-admin 官方文档 vue-element-admin https://panjiachen.github.io/vue-element-admin-site/zh/g ...

  10. 大宇java面试系列(二):jvm组成部分

    1. 说一下 JVM 的主要组成部分?及其作用? 类加载器(ClassLoader) 运行时数据区(Runtime Data Area) 执行引擎(Execution Engine) 本地库接口(Na ...