升级到 Asp.Net Core 2.0 (2017/08/29 更新)

为什么使用 Jwt

最近,移动开发的劲头越来越足,学校搞的各种比赛都需要用手机 APP 来撑场面,所以,作为写后端的,很有必要改进一下以往的基于 Session 的身份认证方式了,理由如下:

  1. 移动端经常要保持长时间(1 到 2 星期)在线,但是 Session 却不好在服务端保存这么久,虽然可以持久化到数据库,但是还是挺费资源
  2. 移动端往往不是使用的网页技术,所以藏在 Cookie 里面的 SessionId 不是很方便的传递给服务端
  3. 服务端暴露给客户端的接口往往是 RESTful 风格的,这是一种无状态的 API 风格,所以身份认证的方式最好也是无状态的才好

所以我选择了使用 Jwt (Json Web Token) 这个技术。Jwt 是一种无状态的分布式的身份验证方式,与 Session 相反,Jwt 将用户信息存放在 Token 的 payload 字段保存在客户端,通过 RSA 加密的方式,保证数据不会被篡改,验证数据有效性。

下面是一个使用 Jwt 的系统的身份验证流程:

可以看出,用户的信息保存在 Token 中,而 Token 分布在用户的设备中,所以服务端不再需要在内存中保存用户信息了

身份认证的 Token 传递时以一种相当简单的格式保存在 header 中,方便客户端对其进行操作

Jwt 简介

Jwt 形式的 token 一般分为 3 个部分,分别是 Header,Payload,Signature,这三个部分使用 . 分隔。其中前两部分使用 Base64 编码,未经加密处理,第三个部分使用 RSA 加密。

所以一个 Jwt 看起来大概是这个样子:

  1. header.payload.signature

下面是一个真实的 Jwt:

  1. eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6InplZWtvIiwicm9sZSI6IiIsIm5hbWVpZCI6MSwianRpIjoiNjNjN2Q3OWY2N2VhMDhjYjRiYzNjMmNkOTJiY2JkNTgiLCJuYmYiOjE0OTQ0MDMwMjQsImV4cCI6MTQ5NTAwNzgyMywiaWF0IjoxNDk0NDAzMDI0LCJpc3MiOiJUZXN0SXNzdWVyIiwiYXVkIjoiVGVzdEF1ZGllbmNlIn0.V7Mfi3FGOTLYV0O5DmOWju7LkDJwZNO6HZN19CHb3ekYxcoVbP51YjYAr0fUHc3RPIp3gxITzziHY-07xZ2swCaV0K-hiF5IbwpDuvyxsnlgaRxS94wKDGKSJkArC82KukCtm7IuFBxnNr6kxe7tGcebVhqtaqgnxEUg5lKtDtVI85kd17YtzBp9Vxnc3Ie0r-6KPgUa2HacCf2Pc3hkvY7tZdWZ6ininZlZ-EbcyZI2KTx-vOqdK63MS2JYSw7W2qwf89tsRsORwbB2P4dOBBFK8YSXJpeyGeJWFEMjAMkiH3AeMmW2w_H7r_6Pn-jh5gozzBei4JoHTU6RVDUg1A

Header

Header 部分一般用来记录加密算法跟 Token 类型

举个例子:

  1. {
  2. "alg": "HS256",
  3. "typ": "JWT"
  4. }

Payload

Payload 存放的是一些不敏感的用户数据,因为这一部分仅仅只是使用 Base64 加密,所以不应该用来保存用户的密码之类的信息。

一个例子:

  1. {
  2. "sub": "1234567890",
  3. "name": "John Doe",
  4. "admin": true
  5. }

Signature

这一部分是 Jwt 最重要的部分,使用 header 中记录的算法进行了加密,加密方式如下:

  1. HMACSHA256(
  2. base64UrlEncode(header) + "." +
  3. base64UrlEncode(payload),
  4. secret)

所以这个部分可以用来保证用户信息不被篡改,起到验证用户身份的作用

在开发过程中,可以访问 https://jwt.io 来调试 Token

当然,为了更快的访问速度,还可以使用 这个网站

在 ASP.NET Core 中使用 Jwt

因为 Jwt 本身的特点,所以用来签发 Token 的服务器可以跟应用服务器不是同一台,这样就可以搞微服务之类的东西(反正我不懂。。。)

因此,在这篇博客中,将会创建两个 Web 应用:

  • JwtIssuer // 用来签发 Token
  • JwtAudience // 应用服务器,提供 API 的

首先来搭建我们的 Token 签发服务器吧!

签发第一个 Token

由于要使用到 RSA 加密,所以先创建一个辅助类来帮助简化调用:

RSAUtils.cs

  1. using System.IO;
  2. using System.Security.Cryptography;
  3. using Newtonsoft.Json;
  4. namespace JwtUtils
  5. {
  6. public static class RSAUtils
  7. {
  8. /// <summary>
  9. /// 从本地文件中读取用来签发 Token 的 RSA Key
  10. /// </summary>
  11. /// <param name="filePath">存放密钥的文件夹路径</param>
  12. /// <param name="withPrivate"></param>
  13. /// <param name="keyParameters"></param>
  14. /// <returns></returns>
  15. public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters)
  16. {
  17. string filename = withPrivate ? "key.json" : "key.public.json";
  18. keyParameters = default(RSAParameters);
  19. if (Directory.Exists(filePath) == false) return false;
  20. keyParameters = JsonConvert.DeserializeObject<RSAParameters>(File.ReadAllText(Path.Combine(filePath, filename)));
  21. return true;
  22. }
  23. /// <summary>
  24. /// 生成并保存 RSA 公钥与私钥
  25. /// </summary>
  26. /// <param name="filePath">存放密钥的文件夹路径</param>
  27. /// <returns></returns>
  28. public static RSAParameters GenerateAndSaveKey(string filePath)
  29. {
  30. RSAParameters publicKeys, privateKeys;
  31. using (var rsa = new RSACryptoServiceProvider(2048))
  32. {
  33. try
  34. {
  35. privateKeys = rsa.ExportParameters(true);
  36. publicKeys = rsa.ExportParameters(false);
  37. }
  38. finally
  39. {
  40. rsa.PersistKeyInCsp = false;
  41. }
  42. }
  43. File.WriteAllText(Path.Combine(filePath, "key.json"), JsonConvert.SerializeObject(privateKeys));
  44. File.WriteAllText(Path.Combine(filePath, "key.public.json"), JsonConvert.SerializeObject(publicKeys));
  45. return privateKeys;
  46. }
  47. }
  48. }

这个工具类能够帮助我们生成 RSA 密钥,并把生成的私钥跟公钥保存在两个文件中,还能从文件中读取密钥。

然后定义一个数据类,用来帮助我们在应用的各个地方获取加密相关的信息:

JWTTokenOptions.cs

  1. using Microsoft.IdentityModel.Tokens;
  2. namespace JwtUtils
  3. {
  4. public class JWTTokenOptions
  5. {
  6. public string Audience { get; set; }
  7. public RsaSecurityKey Key { get; set; }
  8. public SigningCredentials Credentials { get; set; }
  9. public string Issuer { get; set; }
  10. }
  11. }

接下来在 Startup.cs 中配置 Jwt 的加密选项:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. // 省略了其他的东西
  4. // 从文件读取密钥
  5. string keyDir = PlatformServices.Default.Application.ApplicationBasePath;
  6. if (RSAUtils.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false)
  7. {
  8. keyParams = RSAUtils.GenerateAndSaveKey(keyDir);
  9. }
  10. _tokenOptions.Key = new RsaSecurityKey(keyParams);
  11. _tokenOptions.Issuer = "TestIssuer"; // 签发者名称
  12. _tokenOptions.Credentials = new SigningCredentials(_tokenOptions.Key, SecurityAlgorithms.RsaSha256Signature);
  13. // 添加到 IoC 容器
  14. services.AddSingleton(_tokenOptions);
  15. services.AddMvc();
  16. }

接下来创建一个控制器,用来提供签发 Token 的 API

TokenController.cs

  1. namespace JwtIssuer.Controllers
  2. {
  3. [Route("api/[controller]")]
  4. public class TokenController : Controller
  5. {
  6. private readonly JWTTokenOptions _tokenOptions;
  7. private readonly AuthDbContext _dbContext;
  8. public TokenController(JWTTokenOptions tokenOptions, AuthDbContext dbContext)
  9. {
  10. _tokenOptions = tokenOptions;
  11. _dbContext = dbContext;
  12. }
  13. /// <summary>
  14. /// 生成一个新的 Token
  15. /// </summary>
  16. /// <param name="user">用户信息实体</param>
  17. /// <param name="expire">token 过期时间</param>
  18. /// <param name="audience">Token 接收者</param>
  19. /// <returns></returns>
  20. private string CreateToken(User user, DateTime expire, string audience)
  21. {
  22. var handler = new JwtSecurityTokenHandler();
  23. string jti = audience + user.Username + expire.GetMilliseconds();
  24. jti = jti.GetMd5(); // Jwt 的一个参数,用来标识 Token
  25. var claims = new[]
  26. {
  27. new Claim(ClaimTypes.Role, user.Role ?? string.Empty), // 添加角色信息
  28. new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(), // 用户 Id ClaimValueTypes.Integer32),
  29. new Claim("jti",jti,ClaimValueTypes.String) // jti,用来标识 token
  30. };
  31. ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(user.Username, "TokenAuth"), claims);
  32. var token = handler.CreateEncodedJwt(new SecurityTokenDescriptor
  33. {
  34. Issuer = "TestIssuer", // 指定 Token 签发者,也就是这个签发服务器的名称
  35. Audience = audience, // 指定 Token 接收者
  36. SigningCredentials = _tokenOptions.Credentials,
  37. Subject = identity,
  38. Expires = expire
  39. });
  40. return token;
  41. }
  42. /// <summary>
  43. /// 用户登录
  44. /// </summary>
  45. /// <param name="user">用户登录信息</param>
  46. /// <param name="audience">要访问的网站</param>
  47. /// <returns></returns>
  48. [HttpPost("{audience}")]
  49. public IActionResult Post([FromBody]User user, string audience)
  50. {
  51. DateTime expire = DateTime.Now.AddDays(7);
  52. // 在这里来验证用户的用户名、密码
  53. var result = _dbContext.Users.First(u => u.Username == user.Username && u.Password == user.Password);
  54. if (result == null)
  55. {
  56. return Json(new { Error = "用户名或密码错误" });
  57. }
  58. return Json(new { Token = CreateToken(result, expire, audience) });
  59. }
  60. }
  61. }

现在,访问这个 API(http://localhost:port/api/token/TestAudience) 就可以获取用户的 Token 了

在应用服务器验证 Token

在 Startup.cs 中注册 Jwt 相关的服务:

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. // 省略了其他的内容
  4. // 从文件读取密钥
  5. string keyDir = PlatformServices.Default.Application.ApplicationBasePath;
  6. if (RSAUtils.TryGetKeyParameters(keyDir, false, out RSAParameters keyparams) == false)
  7. {
  8. _tokenOptions.Key = default(RsaSecurityKey);
  9. }
  10. else
  11. {
  12. _tokenOptions.Key = new RsaSecurityKey(keyparams);
  13. }
  14. _tokenOptions.Issuer = "TestIssuer"; // 设置签发者
  15. _tokenOptions.Audience = "TestAudience"; // 设置签收者,也就是这个应用服务器的名称
  16. _tokenOptions.Credentials = new SigningCredentials(_tokenOptions.Key, SecurityAlgorithms.RsaSha256Signature);
  17. services.AddAuthorization(auth =>
  18. {
  19. auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
  20. .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
  21. .RequireAuthenticatedUser()
  22. .Build());
  23. });
  24. // Add framework services.
  25. services.AddMvc();
  26. }

然后在 Startup.cs 添加 Jwt 认证中间件:

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3. // 省略了其他的内容
  4. app.UseJwtBearerAuthentication(new JwtBearerOptions
  5. {
  6. TokenValidationParameters = new TokenValidationParameters
  7. {
  8. IssuerSigningKey = _tokenOptions.Key,
  9. ValidAudience = _tokenOptions.Audience, // 设置接收者必须是 TestAudience
  10. ValidIssuer = _tokenOptions.Issuer, // 设置签发者必须是 TestIssuer
  11. ValidateLifetime = true
  12. }
  13. });
  14. }

接着随便创建一个 API 控制器

  1. namespace JwtAudience.Controllers
  2. {
  3. [Route("api/[controller]")]
  4. public class ValuesController : Controller
  5. {
  6. // GET api/values
  7. [HttpGet]
  8. [Authorize]
  9. public IEnumerable<string> Get()
  10. {
  11. return new string[] { "value1", "value2" };
  12. }
  13. }
  14. }

首先编译一下应用服务器,但是不要急着运行。因为应用服务器验证 Token 是需要公钥的,所以现在去之前的签发服务器的 build 目录

可以看到生成了两个json文件,将其中的 key.public.json 拷贝到应用服务器的对应的目录下面,然后运行应用服务器。

如果我们直接访问应用服务器的 API,就会被挡在外面:

所以现在去把之前拿到的 token 复制出来,然后给这个请求加个请求头——Authorization

值是 Bearer 你的Token



这样,基本的身份验证就完成了~

有兴趣的话还可以把这个 Token 放在前面提到的用来调试 Jwt 网站上,我的 Token 的解析结果是:

这里面的 iss 指的就是签发者,aud 指的是接收者,对于我们的应用服务器来说,这两个参数错了任意一个都将无法通过验证(这里就不演示了,等会儿会有测试代码~)

真的足够安全?

至此,我们已经把 Jwt 的身份认证基本实现了,但是仔细想想,却发现存在一个很严重的问题————用户的 Token 在过期时间之内根本无法手动设置失效,随之而来的还有重放攻击等等问题!

Jwt官方也没有提供很好的应对方法,现在就只有一条路可以走,就是把失效的 Token 加入黑名单。只要能够让 Token 失效,之后应对这些安全问题就只是策略上的选择。

在 Jwt 的官方说明中,jti 这个参数就是用来标识 Token 的。所以,让一个 Token 失效只需要把这个 Token 中的 jti 加入应用服务器的数据库的黑名单就好了。

得益于微软对 Identity 良好的设计,我们可以很容易的拓展默认的 Jwt 认证规则

首先创建一个 ValidJtiRequirement 类

  1. public class ValidJtiRequirement : IAuthorizationRequirement
  2. {
  3. }

嗯,他的结构就是这么简单。。。

然后创建一个用来验证这个 Requirement 的 ValidJtiHandler

  1. public class ValidJtiHandler : AuthorizationHandler<ValidJtiRequirement>
  2. {
  3. private readonly AudienceDbContext _dbContext;
  4. public ValidJtiHandler(AudienceDbContext dbContext)
  5. {
  6. _dbContext = dbContext;
  7. }
  8. protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidJtiRequirement requirement)
  9. {
  10. // 检查 Jti 是否存在
  11. var jti = context.User.FindFirst("jti")?.Value;
  12. if (jti == null)
  13. {
  14. context.Fail(); // 显式的声明验证失败
  15. return Task.CompletedTask;
  16. }
  17. // 检查 jti 是否在黑名单
  18. var tokenExists = _dbContext.BlackRecords.Any(r => r.Jti == jti);
  19. if (tokenExists)
  20. {
  21. context.Fail();
  22. }
  23. else
  24. {
  25. context.Succeed(requirement); // 显式的声明验证成功
  26. }
  27. return Task.CompletedTask;
  28. }
  29. }

最后,稍微的修改一下注册服务时的代码

  1. services.AddAuthorization(auth =>
  2. {
  3. auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
  4. .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
  5. .RequireAuthenticatedUser()
  6. .AddRequirements(new ValidJtiRequirement()) // 添加上面的验证要求
  7. .Build());
  8. });
  9. // 注册验证要求的处理器,可通过这种方式对同一种要求添加多种验证
  10. services.AddSingleton<IAuthorizationHandler, ValidJtiHandler>();

最后再来提供一个使 Token 失效的 API

  1. namespace JwtAudience.Controllers
  2. {
  3. [Route("api/[controller]")]
  4. public class TokenController : Controller
  5. {
  6. private readonly AudienceDbContext _dbContext;
  7. public TokenController(AudienceDbContext dbContext)
  8. {
  9. _dbContext = dbContext;
  10. }
  11. [HttpGet]
  12. public IActionResult Get() => Json(_dbContext.BlackRecords);
  13. /// <summary>
  14. /// 使用户的 Token 失效
  15. /// </summary>
  16. /// <returns></returns>
  17. [Authorize("Bearer")]
  18. [HttpDelete]
  19. public IActionResult Delete()
  20. {
  21. // 从 payload 中提取 jti 字段
  22. var jti = User.FindFirst("jti")?.Value;
  23. var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
  24. if (jti == null)
  25. {
  26. HttpContext.Response.StatusCode = 400;
  27. return Json(new { Result = false });
  28. }
  29. // 把这个 jti 加入数据库
  30. _dbContext.BlackRecords.Add(new BlackRecord { Jti = jti, UserId = userId });
  31. _dbContext.SaveChanges();
  32. return Json(new {Result = true});
  33. }
  34. }
  35. }

这里需要注意的是,因为拓展了默认的验证策略,所以需要在 Authorize 这个特性钦定使用 Bearer 策略:

  1. [Authorize("Bearer")]

但是这样就容易在编码的时候出现拼写错误,所以来创建一个继承自这个特性的BearerAuthorize类。

  1. namespace JwtAudience
  2. {
  3. /// <summary>
  4. /// Jwt 验证
  5. /// </summary>
  6. public class BearerAuthorizeAttribute : AuthorizeAttribute
  7. {
  8. public BearerAuthorizeAttribute() : base("Bearer") { }
  9. }
  10. }

现在我们就可以使用[BearerAuthorize]来替代[Authorize]

至此,使 token 失效的能力就具备了。

然后附带一份测试代码,用来检验认证过程是否符合我们的预期:

https://coding.net/u/zeeko/p/JwtApplication/git/blob/master/Test/Test.cs


升级到 Asp.Net Core 2.0 (2017/08/29)

花了一天时间来把项目升级到 2.0,并不是因为 API 变化很大,而是之前的 bug 有些多,修起来有些慢。

首先要升级 Program.cs 里面的 Main 函数:

  1. public class Program
  2. {
  3. public static void Main(string[] args)
  4. {
  5. BuildWebHost(args).Run();
  6. }
  7. public static IWebHost BuildWebHost(string[] args) =>
  8. WebHost.CreateDefaultBuilder(args)
  9. .UseStartup<Startup>()
  10. .Build();
  11. }

看起来更简短了一些。

接下来升级认证配置,按照官方的说明,所有的 app.UseXxxAuthentication 方法都变成了 service.AddAuthentication(XxxSchema).AddXxx(),所以改动不是很大:

JwtIssuer/Startup.cs/ConfigureServices

  1. services.AddAuthentication().AddJwtBearer(jwtOptions =>
  2. {
  3. jwtOptions.TokenValidationParameters = new TokenValidationParameters
  4. {
  5. IssuerSigningKey = _tokenOptions.Key,
  6. ValidAudience = _tokenOptions.Audience,
  7. ValidIssuer = _tokenOptions.Issuer,
  8. ValidateLifetime = true
  9. };
  10. });

JwtAudience/Startup.cs/ConfigureServices

  1. services.AddAuthentication().AddJwtBearer(jwtOptions =>
  2. {
  3. jwtOptions.TokenValidationParameters = new TokenValidationParameters
  4. {
  5. IssuerSigningKey = _tokenOptions.Key,
  6. ValidAudience = _tokenOptions.Audience,
  7. ValidIssuer = _tokenOptions.Issuer,
  8. ValidateLifetime = true
  9. };
  10. });

至此,需要升级的地方就修改好了,但是到目前为止还是无法运行,因为有些在 1.0 里面没有严格检验的地方开始报错了。

第一个地方是 ValidJtiHandler,之前在注册的时候,生命周期选的是单例并没有报错,但是因为这个类依赖了一个生命周期是 Scoped 的对象—— AudienceDbContext,这会引发一个异常,解决方法是把 ValidJtiHandler 也改成 Scoped:

  1. services.AddScoped<IAuthorizationHandler, ValidJtiHandler>();

第二个地方是 RSAParameters 在 2.x 里面,它的私钥属性不能被 Json.Net 序列化,解决方法也很简单,加一个对应的类似 DTO 的类:

  1. class RsaParameterStorage
  2. {
  3. public byte[] D { get; set; }
  4. public byte[] DP { get; set; }
  5. public byte[] DQ { get; set; }
  6. public byte[] Exponent { get; set; }
  7. public byte[] InverseQ { get; set; }
  8. public byte[] Modulus { get; set; }
  9. public byte[] P { get; set; }
  10. public byte[] Q { get; set; }
  11. }

然后在导出私钥前将 RSAParameters 映射成一个 RsaParameterStorage对象,然后使用 Json.Net 来序列化,映射使用的是我自己写的一个 Mapper(所以升级项目只花了几十分钟,调教 Mapper 花了一天),代码更改如下:

  1. // 转换成 json 字符串
  2. static string ToJsonString(this RSAParameters parameters)
  3. {
  4. var content = parameters.Map().To<RsaParameterStorage>();
  5. return JsonConvert.SerializeObject(content);
  6. }
  7. // 从文件中读取
  8. keyParameters = JsonConvert.DeserializeObject<RsaParameterStorage>(File.ReadAllText(filePath)).Map().To<RSAParameters>();

ASP.NET Core 使用 JWT 搭建分布式无状态身份验证系统的更多相关文章

  1. 教你如何实现微信小程序与.net core应用服务端的无状态身份验证

    随着.net core2的发布,越来越多人使用.net core2开发各种应用服务端,下面我就结合自己最近开发的一款小程序,给大家分享下,怎么使用小程序登录后,小程序与服务端交互的权限控制. .net ...

  2. 在ASP.NET Core 2.0中使用Facebook进行身份验证

    已经很久没有更新自己的技术博客了,自从上个月末来到天津之后把家安顿好,这个月月初开始找工作,由于以前是做.NET开发的,所以找的还是.NET工作,但是天津这边大多还是针对to B(企业)进行定制开发的 ...

  3. ASP.NET Core的无状态身份认证框架IdentityServer4

    Identity Server 4是IdentityServer的最新版本,它是流行的OpenID Connect和OAuth Framework for .NET,为ASP.NET Core和.NE ...

  4. ASP.NET Core 基于JWT的认证(一)

    ASP.NET Core 基于JWT的认证(一) Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计 ...

  5. asp.net core 集成JWT(一)

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT ...

  6. asp.net core 集成JWT(二)token的强制失效,基于策略模式细化api权限

    [前言] 上一篇我们介绍了什么是JWT,以及如何在asp.net core api项目中集成JWT权限认证.传送门:https://www.cnblogs.com/7tiny/p/11012035.h ...

  7. ASP.NET Core 使用 Redis 实现分布式缓存:Docker、IDistributedCache、StackExchangeRedis

    ASP.NET Core 使用 Redis 实现分布式缓存:Docker.IDistributedCache.StackExchangeRedis 前提:一台 Linux 服务器.已安装 Docker ...

  8. ASP.NET Core 基于JWT的认证(二)

    ASP.NET Core 基于JWT的认证(二) 上一节我们对 Jwt 的一些基础知识进行了一个简单的介绍,这一节我们将详细的讲解,本次我们将详细的介绍一下 Jwt在 .Net Core 上的实际运用 ...

  9. Asp.Net Core基于JWT认证的数据接口网关Demo

    近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...

随机推荐

  1. nginx+lua安装配置

    1.选定源码目录选定目录 /usr/local/ cd /usr/local/ 2.安装PCRE库cd /usr/local/wget ftp://ftp.csx.cam.ac.uk/pub/soft ...

  2. 自己开发图表插件,脱离echart

    前言 由于公司业务需要做一些图标来展示一些数据,之前都是用百度的echart.js.这次放弃使用它转而自己开发是有几个原因1.echart文件太大,有些功能用不到2.echart样式不易扩展3.需求简 ...

  3. react+webpack开发环境配置

    react是目前非常热门的前端框架,提倡组件化开发.所谓的组件,简单理解,就是一个独立的页面部件(包括页面模版,样式,逻辑等),它是一个独立的整体. webpack,是一个模块打包工具,其主要功能,就 ...

  4. dotweb——go语言的一个微型web框架(二)启动dotweb

    以上的代码截图表示启动一个dotweb服务,在浏览器里输入127.0.0.1:8080,将会得到一个"index"的页面. app := dotweb.New() dotweb.N ...

  5. C++ 拷贝构造函数、拷贝赋值运算符、析构函数

    每一次都会忘,做个笔记吧.想到哪里写到哪里. 拷贝构造函数 第一个参数必须是自身类类型的引用,且任何额外参数都有默认值.(为什么必须是引用?见后解释) 合成拷贝构造函数:如果我们没有为一个类定义拷贝构 ...

  6. 【Java基础 】Java7 NIO Files,Path 操作文件

    从Java1.0到1.3,我们在开发需要I/O支持的应用时,要面临以下问题: 没有数据缓冲区或通道的概念,开发人员要编程处理很多底层细节 I/O操作会被阻塞,扩展能力有限 所支持的字符集编码有限,需要 ...

  7. Unity 3D Framework Designing(8)——使用ServiceLocator实现对象的注入

    对象的 『注入』 是企业级软件开发经常听到的术语.如果你是一个 Java 程序员,一定对注入有着深刻的映像.不管是SSH框架还是SSM框架,Spring 全家桶永远是绕不过去的弯.通过依赖注入,可以有 ...

  8. css远距离链接

    远距离链接主要运用了hover伪类,但是运用了两次 <!DOCTYPE html> <html lang="en"> <head> <me ...

  9. IOS开发创建开发证书及发布App应用(一)——流程说明

    之前在自己做的博客网站上面发布了这个系列的文章,当时还是不错的,帮助了很多跟我一样的新手朋友,不过由于服务器出现问题,丢失了一年了,现在终于找到了,所以发到博客园给大家共享一下,也是为我自己做个参考 ...

  10. [认证授权] 2.OAuth2(续) & JSON Web Token

    0. RFC6749还有哪些可以完善的? 0.1. 撤销Token 在上篇[认证授权] 1.OAuth2授权中介绍到了OAuth2可以帮我们解决第三方Client访问受保护资源的问题,但是只提供了如何 ...