写在前面

1、源码(.Net Core 2.2)

  git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git

2、相关章节

  2.1、《IdentityServer4 (1) 客户端授权模式(Client Credentials)
  2.2、《IdentityServer4 (2) 密码授权(Resource Owner Password)
  2.3、《IdentityServer4 (3) 授权码模式(Authorization Code)》
  2.4、《IdentityServer4 (4) 静默刷新(Implicit)》
  2.5、《IdentityServer4 (5) 混合模式(Hybrid)》

3、参考资料

  IdentityServer4 中文文档 http://www.identityserver.com.cn/
  IdentityServer4 英文文档 https://identityserver4.readthedocs.io/en/latest/

4、流程图

  客户端授权模式是最基本的使用场景,我们需要做一个API(受保护的资源),一个客户端(访问的应用),一个IdentityServer(用来授权)

  

一、创建IdentityServer

1、用VS创建一个Web 项目

  

2、添加引用 IdentityServer4 包,下图是我已经安装好了的截图

3、添加一个配置文件(这里也可以使用json文件)

    public class IdpConfig
{
/// <summary>
/// 用户认证信息
/// </summary>
/// <returns></returns>
public static IEnumerable<IdentityResource> GetApiResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Address(),
new IdentityResources.Email(),
new IdentityResources.Phone()
};
}
/// <summary>
/// API 资源
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApis()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
} /// <summary>
/// 客户端应用
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
// 客户端ID 这个很重要
ClientId = "client",
//AccessToken 过期时间,默认3600秒,注意这里直接设置5秒过期是不管用的,解决方案继续看下面 API资源添加JWT
//AccessTokenLifetime=5,
// 没有交互性用户,使用 clientid/secret 实现认证。
AllowedGrantTypes = GrantTypes.ClientCredentials,
// 用于认证的密码
ClientSecrets =
{
new Secret("secret".Sha256())
},
// 客户端有权访问的范围(Scopes)
AllowedScopes = { "api1" }
}
};
}
}

4、在StartUp.cs 里注册 IdentityServer4

  ConfigureServices()

    services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddDeveloperSigningCredential()//解决Keyset is missing 错误
//.AddTestUsers(TestUsers.Users)
//.AddInMemoryIdentityResources(IdpConfig.GetApiResources())
.AddInMemoryApiResources(IdpConfig.GetApis())
.AddInMemoryClients(IdpConfig.GetClients());

  Configure()方法添加使用 IdentityServer4 中间件

app.UseIdentityServer();

5、配置完成

  启动项目,访问 http://localhost:5002/.well-known/openid-configuration (我的端口号是5002) ,可以浏览 发现文档,参考下图,说明已经配置成功。

  后面客户端会使用里面的数据进行请求toke

  项目第一次启动根目录也会生成一个文件 tempkey.rsa

  

二、客户端

1、新建一个.Net Core Web 项目

  这里可以使用其他建立客户端 。例如:控制台程序、wpf 等等。需要添加 NuGet 包 IdentityModel

  

2、新建一个 Controller 用来测试访问上面的IdentityServer

  获取token,访问 http://localhost:5003/Idp/token ,提示访问成功

    public class IdpController : Controller
{
private static readonly string _idpBaseUrl = "http://localhost:5002";
public async Task<IActionResult> Token()
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(_idpBaseUrl);
if (disco.IsError)
{
return Content("获取发现文档失败。error:" + disco.Error);
}
var token = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
{
Address = disco.TokenEndpoint,
//ClientId、ClientSecret、Scope 这里要和 API 里定义的Client一模一样
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (token.IsError)
{
return Content("获取 AccessToken 失败。error:" + disco.Error);
}
return Content("获取 AccessToken 成功。Token:" + token.AccessToken);
}
}

三、添加API资源

1、新建一个API项目

  我把API项目和IdentityServer 放到同一个解决方案,这个自己定,无所谓的

  API资源指的是IdentityServer IdpConfig.GetApis() 里面添加的 api1(这个api1名称随便起,但是要注意一定要保持一致)

  添加认证之后就可以测试用 AccessToken 请求资源了

2、添加JWT 认证

  StartUp.ConfigureServices()

       services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
// IdentityServer 地址
options.Authority = "http://localhost:5002";
//不需要https
options.RequireHttpsMetadata = false;
//这里要和 IdentityServer 定义的 api1 保持一致
options.Audience = "api1";
//token 默认容忍5分钟过期时间偏移,这里设置为0,
//这里就是为什么定义客户端设置了过期时间为5秒,过期后仍可以访问数据
options.TokenValidationParameters.ClockSkew = TimeSpan.Zero;
options.Events = new JwtBearerEvents
{
//AccessToken 验证失败
OnChallenge = op =>
{
//跳过所有默认操作
op.HandleResponse();
//下面是自定义返回消息
//op.Response.Headers.Add("token", "401");
op.Response.ContentType = "application/json";
op.Response.StatusCode = StatusCodes.Status401Unauthorized;
op.Response.WriteAsync(JsonConvert.SerializeObject(new
{
status = StatusCodes.Status401Unauthorized,
msg = "token无效"
}));
return Task.CompletedTask;
}
};
});

3、添加认证中间件

//这里注意 一定要在 UseMvc前面,顺序不可改变
app.UseAuthentication();

4、Controller 添加特性认证 [Authorize]

    [Route("api/[controller]")]
[Authorize]
public class SuiBianController : Controller
{
[HttpGet]
public string Get()
{
var roles = User.Claims.Where(l => l.Type == ClaimTypes.Role);
return "访问成功,当前用户角色 " + string.Join(',', roles.Select(l => l.Value));
}
}

5、测试

  访问 http://localhost:5001/api/suibian ,提示 token 无效,证明我们增加认证成功

  

四、客户端测试

1、修改 IdpController, 添加一个action 访问 API资源 /api/suibian

    public class IdpController : Controller
{
//内存缓存 需要提前注册 services.AddMemoryCache();
private IMemoryCache _memoryCache;
private static readonly string _idpBaseUrl = "http://localhost:5002";
private static readonly string _apiBaseUrl = "http://localhost:5001";
public IdpController(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public async Task<IActionResult> Token()
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(_idpBaseUrl);
if (disco.IsError)
{
return Content("获取发现文档失败。error:" + disco.Error);
}
var token = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (token.IsError)
{
return Content("获取 AccessToken 失败。error:" + disco.Error);
}
//将token 临时存储到 缓存中
_memoryCache.Set("AccessToken", token.AccessToken);
return Content("获取 AccessToken 成功。Token:" + token.AccessToken);
} public async Task<IActionResult> SuiBian()
{
string token, apiurl = GetApiUrl("suibian");
_memoryCache.TryGetValue("AccessToken", out token);
if (string.IsNullOrEmpty(token))
{
return Content("token is null");
}
var client = new HttpClient();
client.SetBearerToken(token);
var response = await client.GetAsync(apiurl);
var result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
_memoryCache.Remove("AccessToken");
return Content($"获取 {apiurl} 失败。StatusCode:{response.StatusCode} \r\n Token:{token} \r\n result:{result}");
}
return Json(new
{
code = response.StatusCode,
data = result
});
} private string GetApiUrl(string address)
{
return _apiBaseUrl + "/api/" + address;
}
}

2、请求 AccessToken

  http://localhost:5003/Idp/token ,请求成功后会将 token 存储到 cache 中

3、请求 API 资源

  http://localhost:5003/Idp/suibian ,token是直接在缓存里面取出来的

五、项目目录

IdentityServer4 (1) 客户端授权模式(Client Credentials)的更多相关文章

  1. (转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

  2. 基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

  3. IdentityServer4(客户端授权模式)

    1.新建三个项目 IdentityServer:端口5000 IdentityAPI:端口5001 IdentityClient: 2.在IdentityServer项目中添加IdentityServ ...

  4. IdentityServer4[3]:使用客户端认证控制API访问(客户端授权模式)

    使用客户端认证控制API访问(客户端授权模式) 场景描述 使用IdentityServer保护API的最基本场景. 我们定义一个API和要访问API的客户端.客户端从IdentityServer请求A ...

  5. IdentityServer(二)客户端授权模式

    前言 客户端授权模,客户端直接向Identity Server申请token并访问资源.客户端授权模式比较适用于服务之间的通信. 搭建Identity服务 新建名为 IdentityServer 的W ...

  6. 基于 IdentityServer3 实现 OAuth 2.0 授权服务【客户端模式(Client Credentials Grant)】

    github:https://github.com/IdentityServer/IdentityServer3/ documentation:https://identityserver.githu ...

  7. IdentityServer4系列 | 客户端凭证模式

    一.前言 从上一篇关于 快速搭建简易项目中,通过手动或者官方模板的方式简易的实现了我们的IdentityServer授权服务器搭建,并做了相应的配置和UI配置,实现了获取Token方式. 而其中我们也 ...

  8. IdentityServer4(7)- 使用客户端认证控制API访问(客户端授权模式)

    一.前言 本文已更新到 .NET Core 2.2 本文包括后续的Demo都会放在github:https://github.com/stulzq/IdentityServer4.Samples (Q ...

  9. OAuth2:客户端证书授权(Client Credentials)类型的开放授权

    适应范围 认证服务器不提供像用户数据这样的重要资源,仅仅是有限的只读资源或者一些开放的API.例如使用了第三方的静态文件服务,如Google Storage或Amazon S3.这样,你的应用需要通过 ...

随机推荐

  1. shells学习

    shells 脚本 Shell是在Linux内核与用户之间的解释器程序,通常指的是bash,负责向内核翻译及传达用户/程序指令 是liunx系统中的翻译管,解释器类型: ~]#cat /etc/she ...

  2. CSS学习之选择器优先级与属性继承

    CSS学习之选择器优先级与属性继承 选择器优先级 其实选择器是具有优先级的,我们来看下面这一组案例: <!DOCTYPE html> <html lang="en" ...

  3. mongodb(二):数据库安装,部署(linux)

    1.下载安装包 wget http://fastdl.mongodb.org/linux/mongodb-linux-i686-1.8.2.tgz 下载完成后解压缩压缩包 tar zxf mongod ...

  4. POJ 1050 To the Max 最详细的解题报告

    题目来源:To the Max 题目大意:给定一个N*N的矩阵,求该矩阵中的某一个矩形,该矩形内各元素之和最大,即最大子矩阵问题. 解题方法:最大子序列之和的扩展 解题步骤: 1.定义一个N*N的矩阵 ...

  5. bzoj3442学习小组

    bzoj3442学习小组 题意: 共有n个学生,m个学习小组,每个学生只愿意参加其中的一些学习小组,且一个学生最多参加k个学习小组.每个学生参加学习小组财务处都收一定的手续费,不同的学习小组有不同的手 ...

  6. JavaScript 基础 学习 (二)

    JavaScript 基础 学习 节点属性 ​ 每一个节点都有自己的特点 ​ 这个节点属性就记录着属于自己节点的特点 1. nodeType(以一个数字来表示这个节点类型) ​ 语法:节点.nodeT ...

  7. Go的100天之旅-常量

    常量 简介 道可道,非常道.这里常道指的永恒不变的道理,常有不变的意思.顾名思义和变量相比,常量在声明之后就不可改变,它的值是在编译期间就确定的. 下面简单的声明一个常量: const p int = ...

  8. P2060 马步距离(洛谷)

    我们无论遇到什么困难,都不要拖,微笑着面对他,战胜拖延的最好方法就是面对拖延. 今天又拖延了…… 早晨听完老师讲课,本想做一道题练练手的,结果因为懒,瘫了一上午.最后在固定的刷题时间去面对了这道题,然 ...

  9. 《Head First 设计模式》:工厂方法模式

    正文 一.定义 工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类把实例化推迟到子类. PS:在设计模式中,"实现一个接口"泛指实现某个超类型(可 ...

  10. [日常摘要] -- ThreadLocal篇

    简介 ThreadLocal,即线程变量,是一个以ThreadLocal对象为键.任意对象为值的存储结构.这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个 ...