做者:Allen
版权:转载请在文章明显位置注明做者及出处。如发现错误,欢迎批评指正。
上一节讲到Azure AD的一些基础概念,以及Azure AD究竟能够用来作什么?本节就接着讲如何在咱们的项目中集成Azure AD 保护咱们的API资源(其实这里还能够在 SPA单页面应用,Web项目,移动/桌面应用程序集成Azure AD),好了,废话很少说,开始今天的内容。html
上一篇介绍到 Azure AD 实际上是微软基于云的表示和受权访问管理服务,它能够帮助咱们在Azure中登陆和访问资源。咱们能够经过Azure的标识平台生成应用程序,采用微软标识登陆,以及获取令牌来调用受保护的API资源。也就是说这一切功能也是基于包含Oauth 2.0和Open ID Connect的身份验证服务。前端
下面先去了解,熟悉一下关于Identity Server 4的 OpenID 和 OAuth 的区别以及受权模式git
若是以前有了解 Identity Server 4 这种受权验证的框架,能够跳过下面的介绍:github
identityServer4 知多少(圣杰):http://www.javashuo.com/article/p-kwhgfgpj-ba.htmljson
受权服务器identityServer4 开篇(老张的哲学):http://www.javashuo.com/article/p-onvqrhym-hd.html后端
(一) OpenID 和 OAuth 的区别 (如下的介绍来自google和 OAuth官网)api
1,OpenID 是一个以用户为中心的数字身份识别框架,它具备开放、分散性。OpenID 的建立基于这样一个概念:咱们能够经过 URI (又叫 URL 或网站地址)来认证一个网站的惟一身份,简单通俗的理解,OpenID是用来作为身份验证的服务器
2,OAuth 2.0是用于受权的行业标准协议。OAuth 2.0致力于简化客户端开发人员的工做,同时为Web应用程序,桌面应用程序,移动电话和客厅设备提供特定的受权流程。也就是说 OAuth 2.0 是用来进行受权的
app
3,OpenID Connect 是基于OAuth 协议的简单身份层。它容许客户端基于受权服务器执行的身份验证来验证最终用户的身份,并以可互操做且相似于REST的方式获取有关最终用户的基本配置文件信息。OpenID Connect容许全部类型的客户端(包括基于Web的客户端,移动客户端和JavaScript客户端)请求并接收有关通过身份验证的会话和最终用户的信息。规范套件是可扩展的,容许参与者在对他们有意义的时候使用可选功能,例如身份数据加密,OpenID提供程序的发现以及会话管理。框架
OpenID Connect执行许多与OpenID 2.0相同的任务,可是这样作的方式是API友好的,而且可由本机和移动应用程序使用,OpenID Connect定义了用于可靠签名和加密的可选机制。OAuth 1.0和OpenID 2.0的集成须要扩展,而在OpenID Connect中,OAuth 2.0功能与协议自己集成在一块儿。
(二)受权模式
1,隐式模式(Implicit Flow)
2,客户端受权模式(Client Credentials Flow)
3,受权码受权模式(Authorization Code Flow)
4,资源持有者密码模式(Resource Owner Password Credentials ):注意一下,这里的密码翻译的不正确,应该不仅仅指密码,使用证书也是能够的
。。。。。等
这里暂时只了解这四种常见的受权模式。
(三)添加受保护资源
1,VS 建立 “Asp.Net Core WebApi” 项目,而且添加 “OrderController” 控制器,而且新增相应的方法,此步骤暂时省略,详细代码我整理完成后,会添加到github上(文章底部的github连接)
2,安装:Microsoft.AspNetCore.Authentication.AzureAD.UI
3,须要注册验证服务,这个地方默认的是 “AzureADJwtBearer”,AddAzureADBearer方法绑定Azure AD身份验证终结点,租户,租户所在的自定义域,以及客户端Id
services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme) .AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
开启Authentication中间件
// open authentication middleware app.UseAuthentication();
4,在Azure Portal 上添加一个租户
4.1 在Azure Portal 上选择 菜单 “Azure Active Directory”
4.2,点击图中的 “建立目录”
4.3,目录选择默认 “Azure Active Directory”,点击 “下一步-配置”
4.4,添加对应的组织名称和初始域名,
组织名:myCommpany
初始域名:trainingdiscussion
点击 “产看+建立” 进行验证,验证完成后点击 “建立”
5,注册 “应用程序”
5.1,Azure Portal 点击我的头像,切换目录
5.2,选中刚刚建立的 “MyCompany” 的目录,继续在Portal首页左侧选择 “Azure Active Directory”,选中 “应用注册” ,点击 “新注册”
5.3,填写应用注册的一些基本信息
(1)添加受保护的Api资源的名称,也就是咱们在VS中建立的.Net Core 的 WebApi 项目,我这里暂时命名为 “WebApi”,
(2)选择支持的帐户类型,我这里选择的是一个多租户的类型
(3)平台配置,选择 Web API,这里的平台配置怎么理解:就好在Web项目中是在成功验证用户身份后,会携带令牌,咱们做为目标接受的URL,称其为 ”回调地址“
5.4, 点击 ”注册“,而后选择 ”管理“---》”身份验证“,点击”切换到旧体验“
5.5,找到隐式受权模式,勾选 ”访问令牌“,”ID令牌“两个复选框
OK,以上咱们在Azure Portal 就配置好一个客户端的注册,
5.6,在此,咱们真正在代码中开启验证的话,还须要4个参数,也就是上面提到的 ”自定义域(Domain)“,”租户Id(TenantId)“,”客户端Id(ClientId)“,”应用注册终结点(Instance)“
(1)Domain,TenantId (Domain 参数能够在建立目录时,先行复制好)
(2)ClientId:选择刚刚注册好的应用程序,进入应用程序页面后,找到对象Id 进行复制操做。
(3)Instance:每一个国家都有一个单独的Azure门户。若要在应用程序中与Azure AD进行集成,须要在每一个特定环境的Azure门户中单独注册应用程序。
全部用于验证应用程序的Azure AD终结点的URL也是不一样的
适用于美国政府的 Azure AD :https://login.microsoftonline.us
Azure AD 德国 :https://login.microsoftonline.de
由世纪互联运营的 Azure AD 中国:https://login.chinacloudapi.cn
Azure AD(全球服务) :https://login.microsoftonline.com
例如,对于 Azure 中国:
对于单租户应用程序,请将先前 URL 中的“common”替换为你的租户 ID 或名称。 示例为 https://login.chinacloudapi.cn/myCommany。
6,配置文件中的内容以下所示
"AzureAd": {
"Instance": "https://login.chinacloudapi.cn/", "Domain": "trainingdiscussion.partner.onmschina.cn", "TenantId": "53359126-8bcf-455d-a934-5fe72d349207", "ClientId": "f38ec09d-203e-4b2d-a1c1-faf76a608528" },
给须要验证的方法或者控制器加上验证标签[Authorize]
详情请看完整代码
完整代码:
public class Startup { public Startup(IConfiguration configuration, IWebHostEnvironment environment) { Configuration = configuration; Environment = environment; } public IConfiguration Configuration { get; } public IWebHostEnvironment Environment { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddSingleton(new Appsettings(Environment.ContentRootPath)); services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme) .AddAzureADBearer(options => Configuration.Bind("AzureAd", options)); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT受权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意二者之间是一个空格)\"", Type = SecuritySchemeType.OAuth2, In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中) Flows = new OpenApiOAuthFlows() { Implicit = new OpenApiOAuthFlow { Scopes = new Dictionary<string, string> { { "user_impersonation", "Access API" } }, AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/{ Appsettings.app(new string[] { "AzureAD", "TenantId" })}/oauth2/authorize") } } }); // 在header中添加token,传递到后台 c.OperationFilter<SecurityRequirementsOperationFilter>(); }); services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } #region Swagger app.UseSwagger(); app.UseSwaggerUI(c => { //根据版本名称倒序 遍历展现 var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" }); c.SwaggerEndpoint($"/swagger/v1/swagger.json", $"{ApiName} v1"); c.OAuthClientId(Appsettings.app(new string[] { "Swagger", "ClientId" })); c.OAuthClientSecret(Appsettings.app(new string[] { "Swagger", "ClientSecret" })); c.OAuthRealm(Appsettings.app(new string[] { "AzureAD", "ClientId" })); c.OAuthAppName("My API V1"); c.OAuthScopeSeparator(" "); c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", Appsettings.app(new string[] { "AzureAD", "ClientId" }) } }); }); #endregion // open authentication middleware app.UseAuthentication(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } }
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "AzureAd": { "Instance": "https://login.chinacloudapi.cn/", "Domain": "trainingdiscussion.partner.onmschina.cn", "TenantId": "53359126-8bcf-455d-a934-5fe72d349207", "ClientId": "f38ec09d-203e-4b2d-a1c1-faf76a608528" }, "Swagger": { "ClientId": "e15070c3-7e9a-40c0-b73f-2f34fb031641", "ClientSecret": "" // ?fxV/=/pwlRjwQgoIdLRlPNlWBBQ8939 } }
[Route("api/[controller]")] [ApiController] public class OrderController : ControllerBase { // GET: api/Order [HttpGet] [Authorize] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } // GET: api/Order/5 [HttpGet("{id}", Name = "Get")] public string Get(int id) { return "value"; } // POST: api/Order [HttpPost] public void Post([FromBody] string value) { } // PUT: api/Order/5 [HttpPut("{id}")] public void Put(int id, [FromBody] string value) { } // DELETE: api/ApiWithActions/5 [HttpDelete("{id}")] public void Delete(int id) { } }
7,项目添加Swagger的配置,使用Swagger进行接口测试-
7.1:安装 Swashbuckle.AspNetCore,Swashbuckle.AspNetCore.Filters
7.1:配置 Swagger 服务,而且使用隐式受权模式
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT受权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意二者之间是一个空格)\"", Type = SecuritySchemeType.OAuth2, In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中) Flows = new OpenApiOAuthFlows() { Implicit = new OpenApiOAuthFlow { AuthorizationUrl = new Uri($"https://login.chinacloudapi.cn/{ Appsettings.app(new string[] { "AzureAD", "TenantId" })}/oauth2/authorize") } } }); // 在header中添加token,传递到后台 c.OperationFilter<SecurityRequirementsOperationFilter>(); });
7.3,开启中间件
app.UseSwagger(); app.UseSwaggerUI(c => { //根据版本名称倒序 遍历展现 var ApiName = Appsettings.app(new string[] { "Startup", "ApiName" }); c.SwaggerEndpoint($"/swagger/v1/swagger.json", $"{ApiName} v1"); c.OAuthClientId(Appsettings.app(new string[] { "Swagger", "ClientId" })); c.OAuthClientSecret(Appsettings.app(new string[] { "Swagger", "ClientSecret" })); c.OAuthRealm(Appsettings.app(new string[] { "AzureAD", "ClientId" })); c.OAuthAppName("My API V1"); c.OAuthScopeSeparator(" "); c.OAuthAdditionalQueryStringParams(new Dictionary<string, string>() { { "resource", Appsettings.app(new string[] { "AzureAD", "ClientId" }) } }); });
详细代码,请看上面的的完整代码☝☝☝☝☝
7.4,注册应用程序(Swagger)
(1)如今,咱们将为Swagger添加一个 "Azure AD" 应用程序,并授予它向 "Web API" 应用程序发出请求的权限
注意重定向URL的地址,这里须要配置 swagger 的回调地址,localhost:9021 是项目运行的地址
勾选启用隐式受权模式的 ”访问令牌“,”ID令牌“
(2)转到 WebApi 应用添加任意scope(scope名随便定义),那此应用的API将会被公开(暴露),咱们这里添加了一个scope(读)
(3)将应用程序ID复制到appsettings中的Swagger:ClientId
(4)转到 “Swagger” 的应用注册点击”添加权限“---》“委托的权限” 来添加下面绿框架中的两个权限,管理员赞成后,前端应用就拥有调用后端API的权限了。
8,测试效果
启动项目,在项目的 “Swagger” 首页,点击 Try it out 尝试调用 api/order 接口,Response 提示 401 无访问权限
此时,咱们能够在Swagger首页点击 ”Authorize“ ,验证和访问Api资源
登录Azure帐户,进行认证受权
再次调用 api/Order 接口 Response:200 OK
砰🎉🎉🎉🎉🎉🎉🎉🎉,成功!!!!!
三,结尾
今天的文章大概介绍了若是在咱们的项目中集成Azure AD,以及若是在 Swagger中使用隐式受权模式来访问Api资源,
今天,就先分享到这里,上面演示的是若是在Swagger中使用隐式访问模式访问受保护的资源,下一篇继续介绍如何使用其余类型的受权访问模式来访问由Azure AD受保护的API资源。
代码稍等,我会整理一下,上传到github中
github:https://github.com/allentmater/Azure.AD.WebApi.git
做者:Allen
版权:转载请在文章明显位置注明做者及出处。如发现错误,欢迎批评指正。