前言

开发授权服务框架一般使用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. 假期实践作业:从IT角度看地铁

    实习时间:2016/02/23——2016/02/26 实习地点:京港地铁14号线 实习报告: 大学四年过得真快,转眼就大三了,大学前两年半的生活可谓多姿多彩,从不懂计算机到对编程感兴趣,期待得最多的 ...

  2. Android setTag方法的key问题

    android在设计View类时,为了能储存一些辅助信息,设计一个一个setTag/getTag的方法.这让我想起在Winform设计中每个Control同样存在一个Tag. 今天要说的是我最近学习a ...

  3. C#反射基础知识和实战应用

    首先来说一下什么是反射? 反射提供了封装程序集.模块和类型的对象(Type类型) 可以使用反射动态的创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后,可以调用类型的方法或访问其字段和 ...

  4. java提高篇(二)-----理解java的三大特性之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  5. 来看看Windows9到底是什么

    今天有新闻一直在说windows 8.2 windows9,还给出了一张很有趣的图 我们就假设这张图是真的. 这张图透漏出两个信息 其一:开始菜单真的回来了. 不过还是不死心,绝不放弃开始屏,确实,开 ...

  6. TCP Server—Linux

    #include <stdio.h> #include <netinet/ip.h> #define BUFF_SIZE 1024 int main(int argc,char ...

  7. [蓝牙] 3、 剖析BLE心率检测工程

    位于:<KEIL path> \ARM\Device\Nordic\nrf51822\Board\pca10001\s110\ble_app_hrs Heart Rate Example ...

  8. MVVM架构~Knockoutjs系列之验证机制的引入

    返回目录 对于Knockoutjs本身来说,没有提供验证模块,不过,有第三方的扩展,就像你为jquery库作extensions一样,这讲中我将介绍一个Knockout插件扩展,knockout.va ...

  9. WebKit技术内幕

    WebKit技术内幕(浏览器内核|渲染引擎| HTML5| Chromium项目Committer重磅作品) 朱永盛 著   ISBN 978-7-121-22964-0 2014年6月出版 定价:7 ...

  10. Unity3D大风暴之入门篇(海量教学视频版)

    智画互动开发团队 编   ISBN 978-7-121-22242-9 2014年2月出版 定价:79.00元 328页 16开 编辑推荐 长达800分钟的高清教学视频,手把手教会初学者 数个开发案例 ...