寫在前面
是這樣的,我們現在接口使用了Ocelot做網關,Ocelot里面集成了基於IdentityServer4開發的授權中心用於對Api資源的保護。問題來了,我們的Api用了SwaggerUI做接口的自文檔,那就蛋疼了,你接入了IdentityServer4的Api,用SwaggerUI調試、調用接口的話,妥妥的401,未授權啊。那有小伙伴就會說了,你SwaggerUI的Api不經過網關不就ok了?誒,好辦法。但是:
- 我不想改變Url規則啊,我是
/api
開頭的Url都是經過網關的,如果不經過網關要加端口或者改變Url規則,會給其他部門的同事帶來麻煩(多個Url規則容易混淆); - 另外是,因為生產環境是接入了IdentityServer4,我想測試環境從一開始就需要調用方熟悉接口的接入,避免平時用沒有經過授權中心的Url調試,一到生產就出問題。
ok,廢話講得有點多,我們就直奔主題。
下面我們需要創建兩個示例項目:
1、IdentityServer4的授權中心;
2、使用SwaggerUI做自文檔的WebApi項目;
寫得有點亂,本文源碼地址:
https://github.com/gebiWangshushu/cnblogs-demos/tree/master/SwggerUI.IdentityServer4.Example
構建基於IdentityServer4授權中心
1、新建空白解決方案,並添加一個空的WebApi項目,IdentityServer
2、引用包。
Install-Package IdentityServer4
3、添加配置類:Config.cs
using IdentityServer4;
using IdentityServer4.Models;
using IdentityServer4.Test;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace IdentityServer
{
public static class Config
{
public static List<TestUser> GetUsers()
{
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "alice"
}
};
}
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
/// <summary>
/// API信息
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApis()
{
return new[]
{
new ApiResource("swagger_api", "Demo SwaggerUI integrat Idp")
};
}
/// <summary>
/// 客服端信息
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "swagger_client",//客服端名稱
ClientName = "Swagger UI client",//描述
AllowedGrantTypes = GrantTypes.Implicit,//Implicit 方式
AllowAccessTokensViaBrowser = true,//是否通過瀏覽器為此客戶端傳輸訪問令牌
RedirectUris =
{
"http://localhost:5001/swagger/oauth2-redirect.html"
},
AllowedScopes = { "swagger_api" }
}
};
}
}
}
4、修改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.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace IdentityServer
{
public class Startup
{
public IHostingEnvironment Environment { get; }
public Startup(IHostingEnvironment environment)
{
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var builder = services.AddIdentityServer()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApis())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers());
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
}
}
ok,跑起來了
使用SwaggerUI做自文檔的WebApi項目
1、添加WebApi項目,SwaggerUIApi
現在項目結構這樣:
2、先添加SwaggerUI,先不接入IdentityServer
修改Startup.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Swashbuckle.AspNetCore.Swagger;
namespace SwggerUIApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "ToDo API",
Description = "A simple example ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "Shayne Boyer",
Email = string.Empty,
Url = "https://twitter.com/spboyer"
},
License = new License
{
Name = "Use under LICX",
Url = "https://example.com/license"
}
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseMvc();
}
}
}
得到這樣的SwaggerUI:
我們調用一下接口:
杠杠的200:
3、接口項目我們接入IdentityServer4
修改:Startup.cs ,ConfigureServices方法,
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000"; // IdentityServer服務器地址
options.ApiName = "swagger_api"; // 用於針對進行身份驗證的API資源的名稱
options.RequireHttpsMetadata = false; // 指定是否為HTTPS
});
修改:Startup.cs ,Configure方法
app.UseAuthentication();
Ok,可以看到我們接口接入IdentityServer了。提示401,未授權;
3、接入IdentityServer
1、添加授權響應操作的過濾器,AuthResponsesOperationFilter.cs
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SwggerUIApi
{
public class AuthResponsesOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
//獲取是否添加登錄特性
var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
.Union(context.MethodInfo.GetCustomAttributes(true))
.OfType<AuthorizeAttribute>().Any();
if (authAttributes)
{
operation.Responses.Add("401", new Response { Description = "暫無訪問權限" });
operation.Responses.Add("403", new Response { Description = "禁止訪問" });
operation.Security = new List<IDictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>> {{"oauth2", new[] { "swagger_api" } }}
};
}
}
}
}
2、修改Startup.cs ,ConfigureServices方法的,services.AddSwaggerGen()
配置成這樣:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "ToDo API",
Description = "A simple example ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "Shayne Boyer",
Email = string.Empty,
Url = "https://twitter.com/spboyer"
},
License = new License
{
Name = "Use under LICX",
Url = "https://example.com/license"
}
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
//接入identityserver
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Flow = "implicit", // 只需通過瀏覽器獲取令牌(適用於swagger)
AuthorizationUrl = "http://localhost:5000/connect/authorize",//獲取登錄授權接口
Scopes = new Dictionary<string, string> {
{ "swagger_api_scopde", "swagger_api access" }//指定客戶端請求的api作用域。 如果為空,則客戶端無法訪問
}
});
c.OperationFilter<AuthResponsesOperationFilter>();
});
3、我們還需給授權中心添加一個登陸界面
下載這個兩個文件夾,復制丟到IdentityServer項目下面:
項目結構:
4、我們運行看看
先啟動Identityserver項目
運行SwaggerUI可以看到,這兩個地方了個小鎖頭,表示已啟用安全保護:
我們點一下上面的按鈕:
哇,我們跳到了這里:
輸入:alice/alice,點登錄:
哇哇:
當然是Yes啦,然后這邊變成這樣了:
這是已獲得授權狀態,我們再次調用看看:
這里我們看到已經調用成功,仔細看請求,與前面簡短的請求不同的是,現在請求里面帶了access_token了,
這才是我們折騰這么久得來的寶貝。
總結
寫得有點匆忙,希望大家能看得懂[捂臉];
源碼地址:https://github.com/gebiWangshushu/cnblogs-demos/tree/master/SwggerUI.IdentityServer4.Example