前言

开发授权服务框架一般使用OAuth2.0授权框架,而开发Webapi的授权更应该使用OAuth2.0授权标准,OAuth2.0授权框架文档说明参考:https://tools.ietf.org/html/rfc6749

.NET Core开发OAuth2.0的项目需要使用IdentityServer4,可参考:https://identityserver4.readthedocs.io/en/dev/

IdentityServer4源码:https://github.com/IdentityServer

如果在.NET中开发OAuth2.0的项目可使用OWIN,可参考实例源码:https://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server

实现ResourceOwnerPassword和client credentials模式:

授权服务器:

Program.cs --> Main方法中:需要调用UseUrls设置IdentityServer4授权服务的IP地址

             var host = new WebHostBuilder()
.UseKestrel()
//IdentityServer4的使用需要配置UseUrls
.UseUrls("http://localhost:4537")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();

Startup.cs -->ConfigureServices方法中:

             //RSA:证书长度2048以上,否则抛异常
//配置AccessToken的加密证书
var rsa = new RSACryptoServiceProvider();
//从配置文件获取加密证书
rsa.ImportCspBlob(Convert.FromBase64String(Configuration["SigningCredential"]));
//IdentityServer4授权服务配置
services.AddIdentityServer()
.AddSigningCredential(new RsaSecurityKey(rsa)) //设置加密证书
//.AddTemporarySigningCredential() //测试的时候可使用临时的证书
.AddInMemoryScopes(OAuth2Config.GetScopes())
.AddInMemoryClients(OAuth2Config.GetClients())
//如果是client credentials模式那么就不需要设置验证User了
.AddResourceOwnerValidator<MyUserValidator>() //User验证接口
//.AddInMemoryUsers(OAuth2Config.GetUsers()) //将固定的Users加入到内存中
;

Startup.cs --> Configure方法中:

             //使用IdentityServer4的授权服务
app.UseIdentityServer();

Client配置

在Startup.cs中通过AddInMemoryClients(OAuth2Config.GetClients())设置到内存中,配置:

                 new Client
{
//client_id
ClientId = "pwd_client",
//AllowedGrantTypes = new string[] { GrantType.ClientCredentials }, //Client Credentials模式
AllowedGrantTypes = new string[] { GrantType.ResourceOwnerPassword }, //Resource Owner Password模式
//client_secret
ClientSecrets =
{
new Secret("pwd_secret".Sha256())
},
//scope
AllowedScopes =
{
"api1",
//如果想带有RefreshToken,那么必须设置:StandardScopes.OfflineAccess
//如果是Client Credentials模式不支持RefreshToken的,就不需要设置OfflineAccess
StandardScopes.OfflineAccess.Name,
},
//AccessTokenLifetime = 3600, //AccessToken的过期时间, in seconds (defaults to 3600 seconds / 1 hour)
//AbsoluteRefreshTokenLifetime = 60, //RefreshToken的最大过期时间,in seconds. Defaults to 2592000 seconds / 30 day
//RefreshTokenUsage = TokenUsage.OneTimeOnly, //默认状态,RefreshToken只能使用一次,使用一次之后旧的就不能使用了,只能使用新的RefreshToken
//RefreshTokenUsage = TokenUsage.ReUse, //可重复使用RefreshToken,RefreshToken,当然过期了就不能使用了
}

Scope设置

在Startup.cs中通过AddInMemoryScopes(OAuth2Config.GetScopes())设置到内存中,配置:

         public static IEnumerable<Scope> GetScopes()
{
return new List<Scope>
{
new Scope
{
Name = "api1",
Description = "My API",
},
//如果想带有RefreshToken,那么必须设置:StandardScopes.OfflineAccess
StandardScopes.OfflineAccess,
};
}

账号密码验证

Resource Owner Password模式需要对账号密码进行验证(如果是client credentials模式则不需要对账号密码验证了):

方式一:将Users加入到内存中,IdentityServer4从中获取对账号和密码进行验证:

  .AddInMemoryUsers(OAuth2Config.GetUsers())

方式二(推荐):实现IResourceOwnerPasswordValidator接口进行验证:

  .AddResourceOwnerValidator<MyUserValidator>()

IResourceOwnerPasswordValidator的实现:

     public class MyUserValidator : IResourceOwnerPasswordValidator
{
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
if (context.UserName == "admin" && context.Password == "")
{
//验证成功
//使用subject可用于在资源服务器区分用户身份等等
//获取:资源服务器通过User.Claims.Where(l => l.Type == "sub").FirstOrDefault();获取
context.Result = new GrantValidationResult(subject: "admin", authenticationMethod: "custom");
}
else
{
//验证失败
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");
}
return Task.FromResult();
}
}

设置加密证书

通过AddSigningCredential方法设置RSA的加密证书(注意:默认是使用临时证书的,就是AddTemporarySigningCredential(),无论如何不应该使用临时证书,因为每次重启授权服务,就会重新生成新的临时证书),RSA加密证书长度要2048以上,否则服务运行会抛异常

Startup.cs -->ConfigureServices方法中的配置:

             //RSA:证书长度2048以上,否则抛异常
//配置AccessToken的加密证书
var rsa = new RSACryptoServiceProvider();
//从配置文件获取加密证书
rsa.ImportCspBlob(Convert.FromBase64String(Configuration["SigningCredential"]));
services.AddIdentityServer()
.AddSigningCredential(new RsaSecurityKey(rsa)) //设置加密证书

如何生成RSA加密证书(将生成的PrivateKey配置到IdentityServer4中,可以设置到配置文件中):

             using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
{
//Console.WriteLine(Convert.ToBase64String(provider.ExportCspBlob(false))); //PublicKey
Console.WriteLine(Convert.ToBase64String(provider.ExportCspBlob(true))); //PrivateKey
}

资源服务器

Program.cs -> Main方法中:

             var host = new WebHostBuilder()
.UseKestrel()
//IdentityServer4的使用需要配置UseUrls
.UseUrls("http://localhost:4823")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();

Startup.cs --> Configure方法中的配置:

            //使用IdentityServer4的资源服务并配置
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:4537/",
ScopeName = "api1",
SaveToken = true,
AdditionalScopes = new string[] { "offline_access" }, //添加额外的scope,offline_access为Refresh Token的获取Scope
RequireHttpsMetadata = false,
});

需要进行授权验证的资源接口(控制器或方法)上设置AuthorizeAttribute:

     [Authorize]
[Route("api/[controller]")]
public class ValuesController : Controller

测试

resource owner password模式测试代码:

         public static void TestResourceOwnerPassword()
{
var client = new HttpClientHepler("http://localhost:4537/connect/token");
string accessToken = null, refreshToken = null;
//获取AccessToken
client.PostAsync(null,
"grant_type=" + "password" +
"&username=" + "admin" +
"&password=" + "" +
"&client_id=" + "pwd_client" +
"&client_secret=" + "pwd_secret" +
"&scope=" + "api1 offline_access", //scope需要用空格隔开,offline_access为获取RefreshToken
hd => hd.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"),
rtnVal =>
{
var jsonVal = JsonConvert.DeserializeObject<dynamic>(rtnVal);
accessToken = jsonVal.access_token;
refreshToken = jsonVal.refresh_token;
},
fault => Console.WriteLine(fault),
ex => Console.WriteLine(ex)).Wait(); if (!string.IsNullOrEmpty(refreshToken))
{
//使用RefreshToken获取新的AccessToken
client.PostAsync(null,
"grant_type=" + "refresh_token" +
"&client_id=" + "pwd_client" +
"&client_secret=" + "pwd_secret" +
"&refresh_token=" + refreshToken,
hd => hd.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"),
rtnVal => Console.WriteLine("refresh之后的结果: \r\n" + rtnVal),
fault => Console.WriteLine(fault),
ex => Console.WriteLine(ex)).Wait();
} if (!string.IsNullOrEmpty(accessToken))
{
//访问资源服务
client.Url = "http://localhost:4823/api/values";
client.GetAsync(null,
hd => hd.Add("Authorization", "Bearer " + accessToken),
rtnVal => Console.WriteLine("\r\n访问资源服: \r\n" + rtnVal),
fault => Console.WriteLine(fault),
ex => Console.WriteLine(ex)).Wait();
}
}

client credentials模式测试代码:

         public static void TestClientCredentials()
{
var client = new HttpClientHepler("http://localhost:4537/connect/token");
string accessToken = null;
//获取AccessToken
client.PostAsync(null,
"grant_type=" + "client_credentials" +
"&client_id=" + "credt_client" +
"&client_secret=" + "credt_secret" +
"&scope=" + "api1", //不要加上offline_access,因为Client Credentials模式不支持RefreshToken的,不然会授权失败
hd => hd.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"),
rtnVal =>
{
var jsonVal = JsonConvert.DeserializeObject<dynamic>(rtnVal);
accessToken = jsonVal.access_token;
},
fault => Console.WriteLine(fault),
ex => Console.WriteLine(ex)).Wait(); if (!string.IsNullOrEmpty(accessToken))
{
//访问资源服务
client.Url = "http://localhost:4823/api/values";
client.GetAsync(null,
hd => hd.Add("Authorization", "Bearer " + accessToken),
rtnVal => Console.WriteLine("访问资源服: \r\n" + rtnVal),
fault => Console.WriteLine(fault),
ex => Console.WriteLine(ex)).Wait();
}
}

注意

1.RefreshToken默认实现是存储在内存中的,因此如果授权服务重启了那么这些RefreshToken就清空了,也就无法通过RefreshToken获取新的AccessToken了,那么需要实现IPersistedGrantStore接口对Refresh Token等等这些数据进行存储到数据库或者NoSql(Redis)中,如果实现IPersistedGrantStore接口可参考http://www.cnblogs.com/skig/p/AspNetCoreAuthCode.html中的源码;

2.资源服务器在第一次解析AccessToken的时候会先到授权服务器获取配置数据(例如会访问:http://localhost:4537/.well-known/openid-configuration 获取配置的,http://localhost:4537/.well-known/openid-configuration/jwks 获取jwks)),之后解析AccessToken都会使用第一次获取到的配置数据,因此如果授权服务的配置更改了(加密证书等等修改了),那么应该重启资源服务器使之重新获取新的配置数据;

3.调试IdentityServer4框架的时候应该配置好ILogger,因为授权过程中的访问(例如授权失败等等)信息都会调用ILogger进行日志记录,可使用NLog,例如:

  在Startup.cs --> Configure方法中配置:loggerFactory.AddNLog();//添加NLog

源码:http://files.cnblogs.com/files/skig/OAuth2CredentialsAndPassword.zip

ASP.NET Core实现OAuth2.0的ResourceOwnerPassword和ClientCredentials模式的更多相关文章

  1. NET Core实现OAuth2.0的ResourceOwnerPassword和ClientCredentials模式

    NET Core实现OAuth2.0的ResourceOwnerPassword和ClientCredentials模式 前言 开发授权服务框架一般使用OAuth2.0授权框架,而开发Webapi的授 ...

  2. ASP.NET Core实现OAuth2.0的AuthorizationCode模式

    前言 在上一篇中实现了resource owner password credentials和client credentials模式:http://www.cnblogs.com/skig/p/60 ...

  3. ASP.NET Core 1.1.0 Release Notes

    ASP.NET Core 1.1.0 Release Notes We are pleased to announce the release of ASP.NET Core 1.1.0! Antif ...

  4. Asp.net Core 1.0.1升级到Asp.net Core 1.1.0 Preview版本发布到Windows Server2008 R2 IIS中的各种坑

    Asp.net Core 1.0.1升级到Asp.net Core 1.1.0后,程序无法运行了 解决方案:在project.json中加入runtime节点 "runtimes" ...

  5. ASP.NET CORE MVC 2.0 项目中引用第三方DLL报错的解决办法 - InvalidOperationException: Cannot find compilation library location for package

    目前在学习ASP.NET CORE MVC中,今天看到微软在ASP.NET CORE MVC 2.0中又恢复了允许开发人员引用第三方DLL程序集的功能,感到甚是高兴!于是我急忙写了个Demo想试试,我 ...

  6. [翻译] ASP.NET Core 2.1.0 发布

    原文: ASP.NET Core 2.1.0 now available 今天,我们很高兴可以发布 ASP.NET Core 2.1.0!这是我们 .NET平台下开源的.跨平台的 Web 框架的最新版 ...

  7. ASP.NET WebApi 基于OAuth2.0实现Token签名认证

    一.课程介绍 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将是我们需要思考的问题.为了保护我们的WebApi数 ...

  8. 在IIS上部署Asp.Net Core 2.2.0

    1. .NET Core与Windows环境 Asp.Net Core 2.2.0 Windows 10 2. 先决条件   下载并安装.Net Core Hosting Bundle. 3. 部署过 ...

  9. asp.net core 从 3.0 到 3.1

    asp.net core 从 3.0 到 3.1 Intro 今天 .net core 3.1 正式发布了,.net core 3.1 正式版已发布,3.1 主要是对 3.0 的 bug 修复,以及一 ...

随机推荐

  1. [.net 面向对象编程基础] (21) 委托

    [.net 面向对象编程基础] (20)  委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1. 什么是委托? 学习委托,我想说,学会了就感觉简 ...

  2. [nRF51822] 2、D-BUG之诗

    4线SPI彩屏局部刷屏偏移解决 ——原来我早已经在成功的旁边了 最近在研究用低速.低RAM的单片机来驱动小LCD或TFT彩屏实现动画效果 首先我用一个16MHz晶振的m0内核的8位单片机nRF5182 ...

  3. mysql: error while loading shared libraries: libmysqlclient.so.16

    [root@host_41 mysql]# mysqlmysql: error while loading shared libraries: libmysqlclient.so.16: cannot ...

  4. 使用 PowerShell 自动登录Azure

    PowerShell 是管理 Azure 的最好方式之一,通过使用 PowerShell 脚本可以把很多的工作自动化.比如对于 Azure 上的虚拟机,可以设置定时关机操作,并在适当的时间把它开机,这 ...

  5. xamarin UWP设置HUD加载功能

    使用xamarin开发的时候经常用到加载HUD功能,就是我们常见的一个加载中的动作,Android 下使用 AndHUD , iOS 下使用 BTProgressHUD, 这两个在在 NuGet 上都 ...

  6. github无法访问?试试修改hosts

    github国内无法访问时,可以试试如下修改hosts,亲测有效: 204.232.175.78 http://documentcloud.github.com 207.97.227.239 http ...

  7. java之设计模式

    一.代理模式 a.抽象角色(接口):声明真实对象和代理对象的共同接口 b.代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能替 ...

  8. gulp/grunt和browserify/webpack的区别

    Gulp应该和Grunt比较,他们的区别我就不说了,说说用处吧.Gulp / Grunt 是一种工具,能够优化前端工作流程.比如自动刷新页面.combo.压缩css.js.编译less等等.简单来说, ...

  9. Unity5.x在WP8.1中无法使用Reflection API的解决方法

    下班前随便写点,虽然花了不少时间但是最终得到的解决方法还是比较简单的. 第一种方法:使用WinRTLegacy.dll中的类.这个dll在生成的WP project中是自带的无需在unity工程中添加 ...

  10. 分享系列--面试JAVA架构师--链家网

    本月7日去了一趟链家网面试,虽然没有面上,但仍有不少收获,在此做个简单的分享,当然了主要是分享给自己,让大家见笑了.因为这次是第一次面试JAVA网站架构师相关的职位,还是有些心虚的,毕竟之前大部分时间 ...