前言

一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目。正好由于最近刚好辞职,有了时间可以写写自己感兴趣的东西,所以在此想把自己了解的微服务相关的概念和技术框架使用实现记录在一个完整的工程中,由于本人技术有限,所以错误的地方希望大家指出。

目录

项目地址:https://github.com/yingpanwang/fordotnet/tree/dev

为什么需要认证授权服务

作为对外曝露的企业接口服务,当然不可能直接不需要任何的认证就可以随意访问接口,不然会面临巨大的安全隐患。由于单纯的认证和授权不需要与业务逻辑耦合并且访问频繁,所以可以单独划分为一个服务并根据具体业务做单独的服务优化(负载均衡等)

怎么创建认证与授权服务

.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

一、认证中心

  1. 新建 Auth Web项目作为认证中心

  2. Auth项目 通过 Nuget 安装 IdentityServer

  3. 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 请求成功

尝试更改 AllowScopesBusiness 再次访问,显示 403 禁止访问

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



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

与我们的限制范围 一致

.NETCore微服务探寻(二) - 认证与授权的更多相关文章

  1. .NETCore微服务探寻(三) - 分布式日志

    前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...

  2. .NETCore微服务探寻(三) - 远程过程调用(RPC)

    前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...

  3. .NETCore微服务探寻(一) - 网关

    前言 一直以来对于.NETCore微服务相关的技术栈都处于一个浅尝辄止的了解阶段,在现实工作中也对于微服务也一直没有使用的业务环境,所以一直也没有整合过一个完整的基于.NETCore技术栈的微服务项目 ...

  4. 7.【Spring Cloud Alibaba】微服务的用户认证与授权

    有状态 vs 无状态 有状态 那么Session在何时创建呢? 当然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中是通过调用HttpServ ...

  5. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---结构简介(二) 先上项目解决方案图: 以上可以看出项目结构可以划分为4大块,1是surging的核心底层,2,3,4都可以 ...

  6. (1)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

    开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点: 1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块: 2)系统耦合性强,一旦其中一个模块有问题, ...

  7. (1).NET CORE微服务 Micro-Service ---- 什么是微服务架构,.netCore微服务选型

    开发工具:VS2017 .Net Core 2.1 什么是微服务?单体结构: 缺点:1)只能采用同一种技术,很难用不同的语言或者语言不同版本开发不同模块:2)系统耦合性强,一旦其中一个模块有问题,整个 ...

  8. 什么是微服务架构,.netCore微服务选型

    什么是微服务架构,.netCore微服务选型 https://www.cnblogs.com/uglyman/p/9182485.html 开发工具:VS2017 .Net Core 2.1 什么是微 ...

  9. .NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一)

    原文:.NetCore微服务Surging新手傻瓜式 入门教程 学习日志---先让程序跑起来(一) 写下此文章只为了记录Surging微服务学习过程,并且分享给广大想学习surging的基友,方便广大 ...

随机推荐

  1. [Python基础]009.os模块(1)

    os模块(1) 介绍 os 常量 文件目录操作 文件属性操作 遍历文件夹 介绍 os模块是系统服务应用程序接口,是Python最常用的模块之一. os模块包含了对文件和文件夹的操作,操作系统相关的操作 ...

  2. Vim入门教程——转

    简书: https://www.jianshu.com/p/bcbe916f97e1

  3. 【RT-Thread笔记】OneNet软件包的使用

    去年,RT-Thread发布了RT-Thread Studio初版RT-ThreadStudio的使用体验,经过不断更新迭代之后,来到了V1.1.0,咱也来拥抱一下新版本. 本篇笔记咱们以接入OneN ...

  4. Vuex原理实现

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 思考问题 Vuex 只在更实例引入了,那么 ...

  5. 又发现一款纯js开源电子表格Luckysheet

    据官网介绍这个电子表格插件,是一款纯前端类似excel的在线表格,功能强大.配置简单.完全开源. 官网链接: Luckysheet官网 在线DEMO 特性包含: 表格设置,包括冻结行列.合并单元格.筛 ...

  6. Java实现 LeetCode 211 添加与搜索单词 - 数据结构设计

    211. 添加与搜索单词 - 数据结构设计 设计一个支持以下两种操作的数据结构: void addWord(word) bool search(word) search(word) 可以搜索文字或正则 ...

  7. Java实现 蓝桥杯VIP 算法提高 三角形面积

    算法提高 三角形面积 时间限制:1.0s 内存限制:256.0MB 问题描述 由三角形的三边长,求其面积. 提示:由三角形的三边a,b,c求面积可以用如下的公式: s=(a+b+c)/2 输入格式 由 ...

  8. MyBatis整合双数据源

    有时候在项目中会遇到需要连接两个数据库的情况.本文就结合Spring和Mybatis来讲下怎么使用双数据源(或者是多数据源). 背景知识介绍 本文中实现多数据源的关键是Spring提供的Abstrac ...

  9. iOS -UIColor随机生成颜色的方法

    在iOS 中的UIColor拥有这么多关于颜色的类方法,对于一般常见的UI控件,我们可以通过[UIColorblackColor]设置背景色 eg:设置button 的背景色为红色 UIButton ...

  10. 记 Centos zabbix-agent启动失败解决思路

    一. 环境介绍 系统版本:Centos7.4 zabbix-agent 版本:zabbix-agent 3.4.7 二. 问题现象 启动zabbix-agent时启动失败 查看zabbix-agent ...