这两天在新公司看了个MIS系统,其中登录用的Oauth2协议的授权码认证模式。感觉蛮有意思的,就去了解了下,写了个小例子:
该模式大致意思就是:首先用户访客户端,客户端将用户导向认证服务器,用户在认证服务器输入用户名密码授权得到认证码,再携带认证码再次请求认证服务器得到token,用户再携带token请求客户端资源。
这里配一张图来对照着看:

从图上可以清晰的看出来,客户端在向资源拥有者(服务端)请求获得许可后,再去向认证服务端获取Token令牌,然后拿着Token令牌去资源服务器请求数据。
这里有三块:
1、API资源端;
2、Client客户端;
3、IdentityServer服务端;
1、首先我们来新建:IdentityServer服务端
首先修改服务端打开端口,这里我用的5001,可以在Properties文件夹下的launchSettings.json中设置:

创建配置类:Config
using IdentityServer4.Models;
using IdentityServer4.Test;
using System.Collections.Generic;
namespace IdentityServer
{
public static class Config
{
/*配置允许登录的用户信息*/
public static List<TestUser> TestUsers =>
new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "password"
},
new TestUser
{
SubjectId = "2",
Username = "200607157",
Password = "bob"
}
};
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId()
};
//定义如下API
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope("api1", "My API")
};
//定义客户端账户client 密码secret 运行接口范围api1
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "roclient",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api1" }
}
};
}
}
这里我配置了认证方式及登录用户信息,具体的代码逻辑大家可以去看看OAuth2.0客户端模式、密码模式,我就不做详细解说了。
然后在Startup下的ConfigureServices注入依赖:
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer(options =>
{
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients).AddTestUsers(Config.TestUsers);
builder.AddDeveloperSigningCredential();
}
这里还需要在Configure下添加验证:
app.UseIdentityServer();
到这里服务端就配置完成了。
2、新建:Client客户端
这里主要就是获取相应Token提供个API使用。
using IdentityModel;
using IdentityModel.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
namespace Client
{
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
string re= Console.ReadLine();
if (re == "123")
{
var client = new HttpClient();
//https://localhost:5001 验证服务器是否开启
var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
//获取token
//var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
//{
// Address = disco.TokenEndpoint,
// ClientId = "client",
// ClientSecret = "secret",
// Scope = "api1"
//});
//if (tokenResponse.IsError)
//{
// Console.WriteLine(tokenResponse.Error);
// return;
//}
var tokenurl = disco.TokenEndpoint;
// 封装请求参数,获取授权码
// 资源服务器会去授权服务器认证
var tokenResponse = client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = tokenurl,
UserName = "alice",
Password = "password",
ClientId = "roclient",
ClientSecret = "secret",
Scope = "api1"
}).Result;
Console.WriteLine(tokenResponse.Json);
//携带token访问API客户端,验证API是否开启 https://localhost:6001/identity
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync("https://localhost:6001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
Console.ReadKey();
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
Console.ReadKey();
}
}
}
}
}
3、新建:API资源管理端
这里我用的端口为6001,端口可以随意设置,但一定要与服务端跟客户端请求的API端口保持一致。
设置端口可以在Properties下的launchSettings.json中设置。
新建IdentityController控制器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Api.Controllers
{
[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 });
}
}
}
这里设置的路由细心的小伙伴应该发现了,我限定的一下,而且还加了需要验证的标签,这里对应这Client客户端的 var response = await client.GetAsync(“https://localhost:6001/identity”);这一步,至于为什么要这样,你可以认为是用户信息的校验。
这里还需要在Startup中配置依赖:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
namespace Api
{
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.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
//这里指向服务端地址 因为token是该服务端颁发的
options.Authority = "https://localhost:5001";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
});
}
// 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();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
到了这里具体的例子就讲完了,可能大家还有很多没理解为什么要这样的,大家可以去看看IdentityServer4、OAuth2.0鉴权模式就会了解。我这里可以作为参照学习。
具体实例大家可以下载下来: