Asp.Net Core Authentication Middleware And Generate Token
或者应该包含什么信息呢?
1.这个人是谁?
2.这个人可以用此token访问什么样的内容?(scope)
3.token的过期时间 (expire)
4.谁发行的token。
5.其他任何你希望加入的声明(Claims)
那我们为什么要使用token呢?使用session或者用redis来实现stateServer不好吗?
1.token是低(无)状态的,Statelessness
2.token可以与移动端应用紧密结合
3.支持多平台服务器和分布式微服务
拿到token后如何带入HTTP请求传给后台?
答案是两种方式,Cookies和Authorization Header。那么什么时候放到Cookies中,什么时候又放到Authentication中呢?
第一,如果是在Web应用,则放到Cookies当中,并且应该是HttpOnly的,js不能直接对其进行操作,安全性会比将其存在Web Stroage中好一些,因为在Web Storage当中的内容,可以很容的被潜在的XSS脚本攻击并获取。在HttpOnly的cookies当中会相对安全一些,不过也有潜在的CSRF跨站伪造请求的危险,不过这种hack的手段成功率是很低的,有兴趣的朋友可以自行看一下CSRF原理。
第二,如果是手机移动端应用的话,那一定是存储在App本地,并由Authorization Header带到后台并得到身份认证。
WebApp Cookies Authentication
上一段前两周写的最原始的小Demo吧,没有数据库访问等,可根据demo自行改变 ,现在的新代码已经加入了很多业务在其中
startup.cs代码
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Security.Claims;
using Wings.AuthenticationApp.Middleware;
namespace Wings.AuthenticationApp
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.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.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.UseCookieAuthentication(CookieAuthMiddleware.GetOptions());
app.UseOwin();
app.UseCors(a => { a.AllowAnyOrigin(); });
app.UseMvc();
// Listen for login and logout requests
app.Map("/login", builder =>
{
builder.Run(async context =>
{
var name = context.Request.Form["name"];
var pwd = context.Request.Form["pwd"];
if (name == "wushuang" && pwd == "wushuang")
{
var claims = new List<Claim>() { new Claim("name", name), new Claim("role", "admin") };
var identity = new ClaimsIdentity(claims, "password");
var principal = new ClaimsPrincipal(identity);
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
context.Response.Redirect("http://www.baidu.com");
}
else
{
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
context.Response.Redirect("http://www.google.com");
}
});
});
//app.Map("/logout", builder =>
//{
// builder.Run(async context =>
// {
// // Sign the user out / clear the auth cookie
// await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// // Perform a simple redirect after logout
// context.Response.Redirect("/");
// });
//});
}
}
}
下面是Middleware---->CookieAuthMiddleware.cs的代码,
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
namespace Wings.AuthenticationApp.Middleware
{
public class CookieAuthMiddleware
{
public static CookieAuthenticationOptions GetOptions()
{
return new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = new PathString("/login"),
LogoutPath = new PathString("/logout"),
AccessDeniedPath = new PathString("/test"),
CookieHttpOnly = false, //默认就是True了
CookieName = "wings_access_token",
SlidingExpiration = true,
CookieManager = new ChunkingCookieManager()
};
}
}
public static class IdentityExtension
{
public static string FullName(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("name");
return (claim != null) ? claim.Value : string.Empty;
}
public static string Role(this IIdentity identity)
{
var claim = ((ClaimsIdentity)identity).FindFirst("role");
return (claim != null) ? claim.Value : string.Empty;
}
}
}
对应如上demo,简单测试一下,结果如下:
首先使用错误的密码,来请求token endpoint,接下来我们看一下即使窗口,当有请求进入的时候,我用如下代码判断用户的认证情况,拿到的结果必然是false:
接下来,我使用正确的账号密码,来打入token,判断结果一定为true,所以我使用自定义的拓展方法,来获取下,该用户token的信息:
如上demo没有加入一些容错机制,请注意。在用户认证成功后,可以进入带有Authorize Attribute的Action,否则401.如下是几个重要参数的解释
自定义Authentication Middle生产Token
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Wings.TokenAuth.Middleware;
using System.Security.Claims;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.Extensions.Options;
namespace Wings.TokenAuth
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.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.AddMvc();
}
// The secret key every token will be signed with.
// In production, you should store this securely in environment variables
// or a key management tool. Don't hardcode this into your application!
private static readonly string secretKey = "mysupersecret_secretkey!123";
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(LogLevel.Debug);
loggerFactory.AddDebug();
app.UseStaticFiles();
// Add JWT generation endpoint:
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var options = new TokenProviderOptions
{
Audience = "ExampleAudience",
Issuer = "ExampleIssuer",
SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256),
};
app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
app.UseMvc();
}
}
}
TokenProviderOptions.cs
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Wings.TokenAuth.Middleware
{
public class TokenProviderOptions
{
public string Path { get; set; } = "/token";
public string Issuer { get; set; }
public string Audience { get; set; }
public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5);
public SigningCredentials SigningCredentials { get; set; }
}
public class TokenProviderMiddleware
{
private readonly RequestDelegate _next;
private readonly TokenProviderOptions _options;
public TokenProviderMiddleware(
RequestDelegate next,
IOptions<TokenProviderOptions> options)
{
_next = next;
_options = options.Value;
}
public Task Invoke(HttpContext context)
{
// If the request path doesn't match, skip
if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal))
{
//use new JwtSecurityTokenHandler().ValidateToken() to valid token
return _next(context);
}
// Request must be POST with Content-Type: application/x-www-form-urlencoded
if (!context.Request.Method.Equals("POST")
|| !context.Request.HasFormContentType)
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync("Bad request.");
}
return GenerateToken(context);
}
private async Task GenerateToken(HttpContext context)
{
var username = context.Request.Form["username"];
var password = context.Request.Form["password"];
var identity = await GetIdentity(username, password);
if (identity == null)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid username or password.");
return;
}
var now = DateTime.UtcNow;
// Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
// You can add other claims here, if you want:
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(now).ToString(), ClaimValueTypes.Integer64)
};
// Create the JWT and write it to a string
var jwt = new JwtSecurityToken(
issuer: _options.Issuer,
audience: _options.Audience,
claims: claims,
notBefore: now,
expires: now.Add(_options.Expiration),
signingCredentials: _options.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
var response = new
{
access_token = encodedJwt,
expires_in = (int)_options.Expiration.TotalSeconds
};
// Serialize and return the response
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }));
}
private Task<ClaimsIdentity> GetIdentity(string username, string password)
{
// DON'T do this in production, obviously!
if (username == "wushuang" && password == "wushuang")
{
return Task.FromResult(new ClaimsIdentity(new System.Security.Principal.GenericIdentity(username, "Token"), new Claim[] { }));
}
// Credentials are invalid, or account doesn't exist
return Task.FromResult<ClaimsIdentity>(null);
}
public static long ToUnixEpochDate(DateTime date)
=> (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
}
}
下面上测试结果:
使用错误的账户和密码请求token
使用正确的账户和密码来请求,返回结果如下:
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下加【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击下方红色【关注】的。
因为,我的分享热情也离不开您的肯定支持。
感谢您的阅读,我将持续输出分享,我是蜗牛, 保持学习,谨记谦虚。不端不装,有趣有梦。
参考文章和论文,不仅限于如下几篇,感谢国外大佬们有深度的分享:
http://stackoverflow.com/questions/29055477/oauth-authorization-service-in-asp-net-core
https://stormpath.com/blog/token-authentication-asp-net-core
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware#fundamentals-middleware
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie#controlling-cookie-options
https://stormpath.com/blog/token-authentication-asp-net-core
相关文章:
AspNet Identity 和 Owin 谁是谁
ASP.NET Core 之 Identity 入门(一)
ASP.NET Core 之 Identity 入门(二)
ASP.NET Core 之 Identity 入门(三)
原文链接:http://www.cnblogs.com/tdws/p/6536864.html
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
Asp.Net Core Authentication Middleware And Generate Token相关推荐
- ASP.NET Core 实战:基于 Jwt Token 的权限控制全揭露
一.前言 在涉及到后端项目的开发中,如何实现对于用户权限的管控是需要我们首先考虑的,在实际开发过程中,我们可能会运用一些已经成熟的解决方案帮助我们实现这一功能,而在 Grapefruit.VuCore ...
- 利用Asp.Net Core的MiddleWare思想处理复杂业务流程
最近利用Asp.Net Core 的MiddleWare思想对公司的古老代码进行重构,在这里把我的设计思路分享出来,希望对大家处理复杂的流程业务能有所帮助. 背景 一个流程初始化接口,接口中根据传入的 ...
- ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析
ASP.NET Core中间件(Middleware)进阶学习实现SOAP 解析. 本篇将介绍实现ASP.NET Core SOAP服务端解析,而不是ASP.NET Core整个WCF host. 因 ...
- 在ASP.NET Core使用Middleware模拟Custom Error Page功能
一.使用场景 在传统的ASP.NET MVC中,我们可以使用HandleErrorAttribute特性来具体指定如何处理Action抛出的异常.只要某个Action设置了HandleErrorAtt ...
- ASP.NET Core Authentication and Authorization
最近把一个Asp .net core 2.0的项目迁移到Asp .net core 3.1,项目启动的时候直接报错: InvalidOperationException: Endpoint CoreA ...
- ASP.NET Core -中间件(Middleware)使用
ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下一个组件的请求,并能之前和下一组 ...
- ASP.NET Core使用Middleware有条件地允许访问路由
问题 有时,我们可能在Web API中包含一些具有调试功能的请求.比如我们上次的文章中"晕了!这个配置值从哪来的?"使用的获取配置值的功能: endpoints.MapGet(&q ...
- 一张大图了解ASP.NET Core 3.1 中的Authentication与Authorization
下面是一张ASP.NET Core 3.1 中关于Authentication与Authorization的主流程框线图,点击这里查看全图:https://johnnyqian.net/images/ ...
- 为什么应该在业务层实现管道模式,而不用ASP.NET Core Middleware实现 | 2点原因和实现方式...
前言 ASP.NET Core的Middleware(中间件)就是使用了管道模式: Request(请求)在管道中传递,依次经过管道中的每一个MiddleWare进行处理. MiddleWare就像一 ...
最新文章
- 【软件工程】RUP与软件开发5大模型
- 基于Opencv实现眼睛控制鼠标
- 详解JavaScript中的this
- hdu 1788 Chinese remainder theorem again 【crt的具体过程】
- 如何让你的webapp也能跳窗口搜索
- python优雅编程_Python优雅地可视化数据
- python实现将文件夹下文件随机移动指定数量到另一个文件夹下
- ECharts实例开发学习笔记二——时间轴
- LibreOJ 6283 数列分块入门 7(区间加区间乘区间求和)
- 原来做浏览器这么简单
- 学习笔记:模式学习-生成器模式
- 想和华为人一样移动办公?送你一份华为云WeLink入门“说明书”!
- js 打印去掉页眉页脚页码_js页面打印去除页眉页脚
- 计算机视觉教程2-6:八大图像特效算法制作你的专属滤镜(附Python代码)
- 初中数学抽象教学的案例_初中数学教学案例与反思
- wireshark 找不到wifi无线网卡的解决方法
- 软件测试工程师面试如何描述自动化测试是怎么实现的?
- UE4使用蓝图实现自动双开关门
- python视频在线教程_600集Python从小白到大神
- Mysql的基本操作和用户权限----2021(ZSD版)
热门文章
- 关于YoYo.Cms
- SharePoint 2007 Select People and Groups中搜索不到其他Domain账户的问题[已解决]
- ISAPI_Rewrite伪静态配置
- 登陆xp系统,无法显示桌面
- GitHub Copilot 现已登陆 Visual Studio!
- 程序怎么跑着 就卡死,句柄泄漏,内存泄漏了
- C# WPF文本框TextEdit不以科学计数法显示
- .NET 6 平台系列1 .NET Framework发展历程
- WPF 仿QQ登录框翻转效果
- .NET 5 部署在docker上运行