Identity Server 4是IdentityServer的最新版本,它是流行的OpenID Connect和OAuth Framework for .NET,为ASP.NET Core和.NET Core进行了更新和重新设计。认证原理基于JWT,符合JWT流程。关于Identity Server 4的介绍和使用,网上已经很多了,一搜一大堆,本篇文章就不在介绍,这里有几篇文章还不错,作为一个参考:
1,《ASP.NET Core的身份认证框架IdentityServer4--入门》,这篇文章是翻译的国外的一篇文章,对于入门很有启发,英文原文地址:https://www.scottbrady91.com/Identity-Server/Getting-Started-with-IdentityServer-4
2,《使用JWT搭建分布式无状态身份认证系统》,这篇文章介绍JWT,以及怎么不引用Identity Server 4,手动写JWT认证服务。了解一下JWT原理和代码实现即可,如果在实际项目中用,不必再“造轮子”。
3,《IdentityServer4 中文文档与实战》,这是按照官方文档来研究的系列文章,从入门到实践。
本篇文章使用的环境:VS2017,15.9.11,目标框架:.NET Core 2.2
在项目中使用的步骤:
1,新建一个空的解决方案
2,在解决方案里新建项目,选择ASP.NET Core Web应用程序
3,目标框架选择 Core 2.2 (或其他的Core版本),选择“应用程序(模型视图控制器)”
4,打开项目属性,选择“调试”选项,删除默认的启动项
5,创建一个新的启动项,名称定为:WebAPPHost,把端口指定到5002(或者其他不与本地冲突的端口)
6,生成的launchSettings.json如下
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:31509",
"sslPort": 44328
}
},
"profiles": {
"WebAppHost": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5002"
}
}
}
7,删除不必要的文件、文件夹。(作为服务提供的项目,只提供API接口,不需要页面)
8,在NuGet中引入IdentityServer 4
9,新建一个文件夹IdentityServer,用于放IdentityServer相关的文件。
9.1首先创建一个IdentityServerClients类,定义两个方法:IEnumerable<Client> GetClients() 和 List<TestUser> GetTestUsers()
GetClients方法是IdentityServer客户端(访问控制列表)集合,GetTestUsers方法是定义IdentityServer的测试用户。代码如下:


1 public class IdentityServerClients
2 {
3 /// <summary>
4 /// IdentityServer客户端(访问控制列表)
5 /// </summary>
6 /// <returns></returns>
7 public static IEnumerable<Client> GetClients()
8 {
9 var list = new List<Client>
10 {
11 new Client
12 {
13 ClientId = "client",
14 AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
15 AllowedScopes = {"WebAppAPI" },//添加允许访问API范围
16 AllowOfflineAccess = true,
17 AccessTokenLifetime = 60 * 60,
18 RefreshTokenExpiration = TokenExpiration.Sliding,
19 SlidingRefreshTokenLifetime = 60 * 30,
20 RefreshTokenUsage = TokenUsage.ReUse,
21 ClientSecrets =
22 {
23 new Secret("D7896FC26CAC97942DEF0402322524BB".Sha256())
24 }
25 }
26 };
27
28 return list;
29 }
30
31 /// <summary>
32 /// IdentityServer的测试用户
33 /// </summary>
34 /// <returns></returns>
35 public static List<TestUser> GetTestUsers()
36 {
37 List<TestUser> Testers = new List<TestUser>()
38 {
39 new TestUser()
40 {
41 SubjectId = "1",
42 Password ="111111",
43 Username = "admin001"
44 },
45 new TestUser()
46 {
47 SubjectId = "2",
48 Password ="222222",
49 Username = "admin002"
50 }
51 };
52
53 return Testers;
54 }
55 }
9.2 创建一个IdentityServerResources类,定义IdentityServer认证的Resources,代码如下:


1 public class IdentityServerResources
2 {
3 /// <summary>
4 /// 创建允许访问的API资源
5 /// </summary>
6 /// <returns></returns>
7 public static IEnumerable<ApiResource> GetApiResources()
8 {
9 List<ApiResource> resources = new List<ApiResource>()
10 {
11 new ApiResource("WebAppAPI", "测试的API"),//定义API的访问范围名称(Scope)
12 };
13
14 return resources;
15 }
16
17 /// <summary>
18 /// 创建认证的资源
19 /// </summary>
20 /// <returns></returns>
21 public static IEnumerable<IdentityResource> GetIdentityResources()
22 {
23 return new List<IdentityResource>
24 {
25 new IdentityResources.OpenId(),
26 new IdentityResources.Profile(),
27 new IdentityResources.Email(),
28 new IdentityResources.Address(),
29 new IdentityResources.Phone()
30 };
31 }
32 }
9.3 创建一个ResourceOwnerPasswordValidator类,这个类的作用是自定义用户登录:从自己的数据库中验证用户,或者其它项目验证用户。
一个例子:假设项目的用户数据库在别处,并且有登录接口,就可以在这个自定义类中写相应的逻辑来实现。
这个类需要继承IResourceOwnerPasswordValidator接口,才能实现自定义登录。代码如下:


1 public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
2 {
3 public ResourceOwnerPasswordValidator()
4 {
5
6 }
7
8 /// <summary>
9 /// Validates the resource owner password credential
10 /// </summary>
11 /// <param name="context"></param>
12 /// <returns></returns>
13 public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
14 {
15 await Task.Run(() =>
16 {
17 //在此处编写自己的账号密码验证逻辑,若验证通过,写入context的Result,告诉IdentityServer4账号密码是合法的,否则,不添加。
18 var userId = "1";
19 context.Result = new GrantValidationResult(userId, OidcConstants.AuthenticationMethods.Password, DateTime.UtcNow);
20 });
21 }
22 }
创建完成后,我们的IdentityServer文件夹里有三个类:IdentityServerClients.cs,IdentityServerResources.cs,ResourceOwnerPasswordValidator.cs。
在IdentityServer4中还有其他功能可供使用,根据项目实际需求,可以集中放到这个文件夹内。
10,在Startup.cs文件中,引入IdentityServer4服务、配置JWT,以及告诉我们项目允许IdentityServer开始拦截路由并处理请求。
10.1,在ConfigureServices() 中引入IdentityServer4服务。在这里配置IdentityServer的测试用户,或者是自定义用户登录。
当同时设置了测试用户和自定义用户登录,那么IdentityServer是按照测试用户为主的。


1 services.AddIdentityServer()//Ids4服务
2 .AddDeveloperSigningCredential()
3 .AddInMemoryApiResources(IdentityServerResources.GetApiResources())
4 .AddInMemoryIdentityResources(IdentityServerResources.GetIdentityResources())
5 .AddInMemoryClients(IdentityServerClients.GetClients())//把配置文件的Client配置资源放到内存
6 //.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()//自定义用户登录。即自定义用户登录又添加测试用户,则以测试用户为准
7 .AddTestUsers(IdentityServerClients.GetTestUsers())//添加测试用户
8 ;
10.2,在ConfigureServices() 中配置JWT服务


1 //重写JWT,如果不配置JWT,那么IdentityServer4会在未认证身份的时候重定向到Account/Login,从而出错
2 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
3 services.AddAuthentication("Bearer")
4 .AddJwtBearer("Bearer", options =>
5 {
6 options.Authority = Configuration["JWT:Authority"];
7 options.RequireHttpsMetadata = false;
8 options.Audience = "WebAppAPI";
9 options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(0);
10 options.TokenValidationParameters.RequireExpirationTime = true;
11 });
10.3,在Configure() 方法中添加以下内容以将IdentityServer中间件添加到HTTP管道
1 app.UseCors("default");
2 //允许IdentityServer开始拦截路由并处理请求。
3 app.UseIdentityServer();
4 //添加身份验证UseMvc
5 app.UseAuthentication();
10.4,在appsettings.json中加入JWT服务地址,以告诉IdentityServer去哪里寻找JWT服务,端口与我们在第5步,创建WebAPPHost时一致,appsettings.json文件代码如下
1 {
2 "Logging": {
3 "LogLevel": {
4 "Default": "Warning"
5 }
6 },
7 "JWT": {
8 "Authority": "http://localhost:5002"
9 },
10 "AllowedHosts": "*"
11 }
11,改造我们的Controller,增加身份认证。
11.1,创建一个基类控制器:AuthorizeController.cs,控制器中暂时没有内容,只加入一个身份验证的类标签:[Authorize],代码如下
1 [Authorize]
2 public class AuthorizeController : Controller
3 {
4
5 }
当以后需要在所有控制器中做公共的事情,就可以在这个控制器中增加逻辑代码。
11.2,IdentityServer4身份认证已结束,我们把HomeController当做一个测试控制器,修改一下用于测试:
1 public class HomeController : AuthorizeController
2 {
3 [HttpGet]
4 public string Index()
5 {
6 var result = "{\"detail\":\"Hello Word!\"}";
7 return result;
8 }
9 }
12,运行起来,获取token的地址是:http://localhost:5002/connect/token,这是一个标准的获取token接口,无论域名时什么,地址是不变的。
body提交的参数分别是:
grant_type:password
username:admin001
password:111111
scope:WebAppAPI
client_id:client
client_secret:D7896FC26CAC97942DEF0402322524BB
然后,我们用获取到的token访问home/index接口
若不加token访问是被拦截的,返回401:
来源:oschina
链接:https://my.oschina.net/u/4323347/blog/3468032