采用 .net cli 创建一个API应用
$ dotnet new webapi -n api1 -o E:\github\dotnet\refine-api1
-n 指明API的名称
-o 输出目录
注意修改Properties/launchSettings.json 中的绑定地址,修改之前的 http://localhost:5001 为 http://0.0.0.0:5001, 也是之前在Docker容器运行出现绑定异常无法处理,才如此处理。
{
"profiles": {
"api1": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "http://0.0.0.0:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
创建控制器
[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
该REST方法会在后面用来测试用户权限验证,验证通过输出用户信息中的所有声明事项。
应用配置
最后一步就是在HTTP管道中添加验证服务和授权中间件,这些主要处理两方面的事情:
- 校验令牌是颁发自合法的授权服务
- 校验令牌拥有使用API的权限
更新 Startup.cs 代码
using Microsoft.AspNetCore.Authentication.JwtBearer;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = IdentityServerBaseUrl; //验证服务地址
options.RequireHttpsMetadata = false;
options.Audience = "UserAPI"; // API 的名字
});
}
public void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseMvc();
}
}
通过AddAuthentication 依赖注入验证服务,配置Bearer作为默认的验证模式,这样每次调用API时都会进行自动验证。
如果你通过浏览器访问控制器(http://localhost:5001/identity),就是返回401的错误,这表明API的调用需要一个凭证。
这个API就在IdentityServer4的保护下了。
上述代码不支持CORS,但上传Github的源码已经增加CORS支持。
源码地址:https://github.com/daijinming/refine-api1
测试API
第一步请求 access_token
输入参数
POST /connect/token HTTP/1.1
Host: 114.116.96.150:5000
Cache-Control: no-cache
Postman-Token: d7c7d2dd-1f8e-fb68-5e9c-d811f4cd13ed
Content-Type: application/x-www-form-urlencoded
client_id=pwdClient&client_secret=superSecretPassword&grant_type=password&scope=api1&username=demo%40qq.com&password=!1Demo
这里特别强调的是登录时请求 jwt时,务必在 scope 中输入 api1,也就是API的编码
输出参数
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjBBODE1Rjg2RENENzJDNjJCOTVDQjkxQkIwNDk1MTY5QTNERTNDQkQiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJDb0ZmaHR6WExHSzVYTGtic0VsUmFhUGVQTDAifQ.eyJuYmYiOjE1NDcwMjc0MjAsImV4cCI6MTU0NzAzMTAyMCwiaXNzIjoiaHR0cDovLzExNC4xMTYuOTYuMTUwOjUwMDAiLCJhdWQiOlsiaHR0cDovLzExNC4xMTYuOTYuMTUwOjUwMDAvcmVzb3VyY2VzIiwiYXBpMSJdLCJjbGllbnRfaWQiOiJwd2RDbGllbnQiLCJzdWIiOiI5OTdkMjc1Yi1lMTZkLTQxOWQtOGUxNS05ZmIwZjg2M2RjMzgiLCJhdXRoX3RpbWUiOjE1NDcwMjc0MjAsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsiYXBpMSJdLCJhbXIiOlsicHdkIl19.gEKY6BTKcuYA3YKalBNAunZNeSTirYP3qrZ7pjYKrN8PRFs5SRjNJ1dHciI4VBcXA9zQgSynF8B2HTqEr8sZ2h2BjxxdqB3AARkctviFKtUGB-tFiYN2YJOZBid-UgBdHolhKcVLnia4LVKW4u8NXnYVO9dKFncIXiMU6c0Le__txfbLsw2lUW2aza1mz-vJAJy3aZ0U8_eUuA0DiglGsZA3eBmx7f05WadSyreyg0dQOBSjqBFfwVKCdO0jm402PLN3r5W-i2EZpszQYQBgLLrVkfT4MN7ns44gHTpSYOGkXec2-hGe1WM0mnCzDZeI58v_OotyYuaS7ebzcpPZJQ",
"expires_in": 3600,
"token_type": "Bearer"
}
通过 jwt.io 对 access_token 进行解码
{
"nbf": 1547027420,
"exp": 1547031020,
"iss": "http://114.116.96.150:5000",
"aud": [
"http://114.116.96.150:5000/resources",
"api1"
],
"client_id": "pwdClient",
"sub": "997d275b-e16d-419d-8e15-9fb0f863dc38",
"auth_time": 1547027420,
"idp": "local",
"scope": [
"api1"
],
"amr": [
"pwd"
]
}
第二步模拟调用API
输入参数
GET /identity HTTP/1.1
Host: localhost:5001
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjBBODE1Rjg2RENENzJDNjJCOTVDQjkxQkIwNDk1MTY5QTNERTNDQkQiLCJ0eXAiOiJKV1QiLCJ4NXQiOiJDb0ZmaHR6WExHSzVYTGtic0VsUmFhUGVQTDAifQ.eyJuYmYiOjE1NDcwMjczMzAsImV4cCI6MTU0NzAzMDkzMCwiaXNzIjoiaHR0cDovLzExNC4xMTYuOTYuMTUwOjUwMDAiLCJhdWQiOlsiaHR0cDovLzExNC4xMTYuOTYuMTUwOjUwMDAvcmVzb3VyY2VzIiwiYXBpMSJdLCJjbGllbnRfaWQiOiJwd2RDbGllbnQiLCJzdWIiOiI5OTdkMjc1Yi1lMTZkLTQxOWQtOGUxNS05ZmIwZjg2M2RjMzgiLCJhdXRoX3RpbWUiOjE1NDcwMjczMzAsImlkcCI6ImxvY2FsIiwic2NvcGUiOlsiYXBpMSJdLCJhbXIiOlsicHdkIl19.mS_tnJcbRwUedObdit1xoceQaGYc3uzTH3w1VnwWe3CdY7saothpIng_z_zTdgelmDSJNsXxkQiH9mDf-tZZjxxO_wCgvhrw3gKhERlVRMrc7jr_OnvSkvoidH5guqo1CBff4Bp7g6HPRAPx2H7eNk26QXJD2vs_1i8GqD3qLVwPd8sclYjLzXyWXRhOLhQ2B9L5vmJT7ztRbzPa_5FAdq_vfLPxbviDXQ6Py0ne_Ll-U6KHPF-Vq5Y7PbhsDFuQ0tWSYMXHh2aREttbjEdIHPAWeW4WYQFdQxPtpbXabZ1VZ6fLgSBB5OTLwmar3E3eobbHlO6_6RgjdH56COF1Fw
Cache-Control: no-cache
Postman-Token: f3a8d84b-1a37-b160-f729-4c1f0dc892d2
输出结果
[
{
"type": "nbf",
"value": "1547027330"
},
{
"type": "exp",
"value": "1547030930"
},
{
"type": "iss",
"value": "http://114.116.96.150:5000"
},
{
"type": "aud",
"value": "http://114.116.96.150:5000/resources"
},
{
"type": "aud",
"value": "api1"
},
{
"type": "client_id",
"value": "pwdClient"
},
{
"type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
"value": "997d275b-e16d-419d-8e15-9fb0f863dc38"
},
{
"type": "auth_time",
"value": "1547027330"
},
{
"type": "http://schemas.microsoft.com/identity/claims/identityprovider",
"value": "local"
},
{
"type": "scope",
"value": "api1"
},
{
"type": "http://schemas.microsoft.com/claims/authnmethodsreferences",
"value": "pwd"
}
]