.NETCore微服务探寻(二) - 认证与授权
前言
一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目。正好由于最近刚好辞职,有了时间可以写写自己感兴趣的东西,所以在此想把自己了解的微服务相关的概念和技术框架使用实现记录在一个完整的工程中,由于本人技术有限,所以错误的地方希望大家指出。
目录
为什么需要认证授权服务
作为对外曝露的企业接口服务,当然不可能直接不需要任何的认证就可以随意访问接口,不然会面临巨大的安全隐患。由于单纯的认证和授权不需要与业务逻辑耦合并且访问频繁,所以可以单独划分为一个服务并根据具体业务做单独的服务优化(负载均衡等)
怎么创建认证与授权服务
.NETCore中除了官方的认证授权框架外,比较流行的是 IdentityServer4
什么是IdentityServer4
IdentityServer是用于ASP.NET Core 的免费,开源OpenID Connect和OAuth 2.0框架。IdentityServer4 由Dominick Baier和Brock Allen创建和维护,它整合了将基于令牌的身份验证,单点登录和API访问控制集成到您的应用程序中所需的所有协议实现和可扩展性点。IdentityServer4 已由OpenID Foundation正式认证,因此符合规范且可互操作。它是.NET Foundation的一部分,并根据其行为准则进行操作。它已获得Apache 2(OSI批准的许可证)的许可。
如何接入IdentityServer4
一、认证中心
新建 Auth Web项目作为认证中心
Auth项目 通过 Nuget 安装 IdentityServer

Auth项目 定义Api资源(ApiResource),身份资源(IdentityResource),客户端(Client)信息。
IdentityServer4可以通过持久化的方式定义这些信息,这里方便演示采用的是内存中定义
IdentityServerConfig.cs
using IdentityServer4.Models;
using System.Collections.Generic;
namespace ForDotNet.Auth.Config
{
/// <summary>
/// IdentityServer4配置信息
/// </summary>
public static class IdentityServerConfig
{
private const string AuthClientId = "Auth";
private const string Api1ClientId = "Api1";
/// <summary>
/// 获取api资源
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>()
{
new ApiResource("Auth","AuthApi"),
// new ApiResource("Api1","Api1"),
new ApiResource()
{
Name = "Api1",
DisplayName = "Api1Display",
Scopes = new Scope[]
{
new Scope("Api1","This Api1 Scope"),
new Scope ("Business","This is Business Scope"),
new Scope ("Admin","This Admin Scope")
}
}
};
}
/// <summary>
/// 获取客户端
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
Client authClient = new Client()
{
ClientId = AuthClientId,
ClientSecrets = new List<Secret>()
{
GetSecret(AuthClientId)
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = new string[]
{
"Auth"
}
};
Client api1Client = new Client()
{
ClientId = Api1ClientId,
ClientSecrets = new List<Secret>()
{
GetSecret(Api1ClientId)
},
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AllowedScopes = new string[]
{
"Api1"
}
};
return new List<Client>()
{
authClient,
api1Client
};
}
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>()
{
new IdentityResources.OpenId()
};
}
#region 私有方法
/// <summary>
/// 获取Secret
/// </summary>
/// <param name="clientId">clientId</param>
/// <returns></returns>
private static Secret GetSecret(string clientId)
{
return new Secret(clientId.Sha256());
}
#endregion 私有方法
}
}
Startup.cs中注入这些服务
using ForDotNet.Auth.Config;
using ForDotNet.Common.Consul.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Collections.Generic;
namespace ForDotNet.Auth
{
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
.AddIdentityServer() // 注册IdentityServer4
.AddDeveloperSigningCredential() // 采用开发者凭证
.AddInMemoryApiResources(IdentityServerConfig.GetApiResources()) // 添加Api资源
.AddInMemoryClients(IdentityServerConfig.GetClients()) // 添加客户端
.AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources()) // 添加身份资源
.AddTestUsers(new List<IdentityServer4.Test.TestUser>() // 添加测试用户
{
new IdentityServer4.Test.TestUser ()
{
Username = "admin",
Password = "123",
SubjectId = "999"
}
});
// 注册服务发现服务
services.AddConsulServiceDiscovery();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime life)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 注册IdentityServer
app.UseIdentityServer();
// 注册服务发现
app.UseConsulServiceDiscovery(life);
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
这里我们为了方便测试直接使用的是 AddTestUsers 添加的测试用户,如果我们需要自定义验证逻辑的话需要使用 AddResourceOwnerValidator<T>,该方法接收一个实现了 IResourceOwnerPasswordValidator接口的 T类型。
定义一个 MyResourceOwnerValidator.cs
using IdentityServer4.Models;
using IdentityServer4.ResponseHandling;
using IdentityServer4.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace ForDotNet.Auth
{
/// <summary>
/// 我的校验逻辑
/// </summary>
public class MyResourceOwnerValidator : IResourceOwnerPasswordValidator
{
public MyResourceOwnerValidator()
{
// 可以注入服务
}
/// <summary>
/// 校验方法
/// </summary>
/// <param name="context">上下文信息(包含了用户名密码等信息)</param>
/// <returns></returns>
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
await Task.Run(()=>
{
//校验逻辑...
// 校验成功
if (DateTime.Now.Minute % 2 == 0)
{
context.Result = new GrantValidationResult(Guid.NewGuid().ToString(), "DIY",new List<Claim>()
{
new Claim ("DIYClaim","This is DIYClaim")
});
}
else
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "认证失败",
new Dictionary<string, object>()
{
{ "Test","This Is Test" }
});
}
});
}
}
}
如果使用了AddTestUsers的话则AddResourceOwnerValidator中实现的逻辑不会生效
public void ConfigureServices(IServiceCollection services)
{
services
.AddIdentityServer() // 注册IdentityServer4
.AddDeveloperSigningCredential() // 采用开发者凭证
.AddInMemoryApiResources(IdentityServerConfig.GetApiResources()) // 添加Api资源
.AddInMemoryClients(IdentityServerConfig.GetClients()) // 添加客户端
.AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources()) // 添加身份资源
.AddResourceOwnerValidator<MyResourceOwnerValidator>()
//.AddTestUsers(new List<IdentityServer4.Test.TestUser>() // 添加测试用户
//{
// new IdentityServer4.Test.TestUser ()
// {
// Username = "admin",
// Password = "123",
// SubjectId = "999"
// }
//});
// 注册服务发现服务
services.AddConsulServiceDiscovery();
services.AddControllers();
}
二、客户端(多个)
1.新建 Api 项目作为客户端
2.通过 Nuget安装 IdentityServer4.AccessTokenValidation

3.Api项目客户端 Startup.cs 中 注册认证
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ForDotNet.Common.Consul.Extensions;
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;
namespace ForDotNet.Web.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
.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5800"; // issuer地址
options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Both;
options.ApiName = "Api1";
options.RequireHttpsMetadata = false; // 启用http
});
services.AddConsulServiceDiscovery();
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IHostApplicationLifetime life)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 启用认证
app.UseAuthentication();
app.UseConsulServiceDiscovery(life);
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
三、运行并测试
1.客户端需要权限访问的接口添加 Authorize 特性

2.直接访问 http://localhost:5500/api/test ,响应401未授权

3.访问 http://localhost:5800/connect/token 请求token
由于我们的自定义逻辑 在当前时间分钟数 % 2 == 0 的时候 认证通过,反之失败
成功:

失败:

4.将获取到的token 添加到请求头中 访问 http://localhost:5500/api/test 200 响应请求成功

使用Ocelot 接入IdentityServer4
上面讲述的是没有使用网关的时候使用IdentityServer4,如果我们使用Ocelot的时候,下游服务运行在内网中与公网隔离时如何使用IdentityServer做统一的认证呢。
1.移除下游Api项目的认证代码


2.Ocelot添加认证配置
Startup中添加IdentityServer4认证配置信息

Ocelot.json配置文件中添加认证信息
AllowScopes限制可以访问的范围 为空表示不限制

3.测试访问
直接通过网关访问 http://localhost:5000/api/v1/test,响应401 未授权

通过 http://localhost:5000/auth/token 获取token ,访问 http://localhost:5000/api/v1/test 200 请求成功

尝试更改 AllowScopes 为 Business 再次访问,显示 403 禁止访问


将 AllowScopes 改为 Api1 再次访问,显示 200 请求成功


然后查看我们请求token所属客户端的 AllowScopes 为

与我们的限制范围 一致
.NETCore微服务探寻(二) - 认证与授权的更多相关文章
- .NETCore微服务探寻(三) - 分布式日志
前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...
- .NETCore微服务探寻(三) - 远程过程调用(RPC)
前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...
- .NETCore微服务探寻(一) - 网关
前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...
- 7.【Spring Cloud Alibaba】微服务的用户认证与授权
有状态 vs 无状态 有状态 那么Session在何时创建呢? 当然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中是通过调用HttpServ ...
- .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)
原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图: 以上可以看出项目结构可以划分为4大块,1是surging的核心底层,2,3,4都可以 ...
- (1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型
开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题, ...
- (1).NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型
开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点:1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块:2)系统耦合性强,一旦其中一个模块有问题,整个 ...
- 什么是微服务架构,.netCore微服务选型
什么是微服务架构,.netCore微服务选型 https://www.cnblogs.com/uglyman/p/9182485.html 开发工具:VS2017 .Net Core 2.1 什么是微 ...
- .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一)
原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一) 写下此文章只为了记录Surging微服务学习过程,并且分享给广大想学习surging的基友,方便广大 ...
随机推荐
- Parrot os安装docker及docker-compose
一.安装docker 1.添加Docker pgp key curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key ...
- 学习scrapy框架爬小说
一.背景:近期学习python爬虫技术,感觉挺有趣.由于手动自制爬虫感觉效率低,了解到爬虫界有先进的工具可用,尝试学学scrapy爬虫框架的使用. 二.环境:centos7,python3.7,scr ...
- Redis 的原理与应用场景及数据库关系
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一. Redis 是什么? Redis是一个开源的使用ANSIC语言编写.支持网络.单进程单线程.可基于 ...
- Java实现 LeetCode 804 唯一摩尔斯密码词 (暴力)
804. 唯一摩尔斯密码词 国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: "a" 对应 ".-", " ...
- Java实现 蓝桥杯 算法训练 排序
算法训练 排序 时间限制:1.0s 内存限制:512.0MB 问题描述 编写一个程序,输入3个整数,然后程序将对这三个整数按照从大到小进行排列. 输入格式:输入只有一行,即三个整数,中间用空格隔开. ...
- Java实现 蓝桥杯VIP 算法训练 完数
问题描述 一个数如果恰好等于它的因子之和,这个数就称为"完数".例如,6的因子为1.2.3,而6=1+2+3,因此6就是"完数".又如,28的因子为1.2.4. ...
- Java实现John's trip(约翰的小汽车)
1 问题描述 John's trip Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8998 Accepted: 3018 Sp ...
- Java实现 蓝桥杯 历届试题 最大子阵
问题描述 给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大. 其中,A的子矩阵指在A中行和列均连续的一块. 输入格式 输入的第一行包含两个整数n, m,分别表示矩阵A的行数和 ...
- java代码(11) ---java代码的优化
java代码的优化 参考了一些Java开发手册有关代码的规范,觉得一段好的代码可以从三个维度去分析.1)性能,2)可扩展性,3)可读性 让我们看看别人是怎么去分析,还有值得我们去学习的地方,也是我正在 ...
- java代码(6) ---guava之multimap
guava之multimap 上一篇说的是Multiset它可以对存入的相同元素做一个计数的功能,那multimap呢? 一.概述 1.基本介绍和案例说明 multimap和Multiset的继承结果 ...