2021,祝大家新年快乐!!!

2021年了,新的一年应该有新的计划,我的计划是准备去学习微服务,所以我将我自己的博客项目拆分成了一个微服务项目,用来给自己学习,项目地址:http://www.ttblog.site/。之前做的一个认证服务比较简单,只是单纯的生成jwtToken,所以想改造下。看了很多资料,发现了.net下的一个基于 OpenID Connect 和 OAuth 2.0 认证框架:IdentityServer4,所以就查了一下资料就用在了我的项目上。因为我也是刚刚学习IdentityServer4,所以如果有说错的地方希望大家指出。

我的想法是不想让我的api裸奔,所以要加个认证,不是谁都可以调用我的apide1,这里我选择了IdenttiyServer4的客户端凭据许可模式(client_credentials),这里推荐一个文章:https://www.cnblogs.com/ddrsql/p/7789064.html。因为我觉得这种模式正好符合自己的要求,对于其它的模式我也只是了解过,但是并没有实践过。

第一步:搭建一个IdentityServer服务器

添加IdentityServer4的nuget包。然后我添加了一个idenityserver.json的文件用来配置一些认证资源

{
"WebApi": {
"ApiResource": "",
"ApiScope": "",
"Client": {
"ClientId": "",
"ClientName": "",
"ClientSecrets": "",
"AllowedGrantTypes": "",
"AllowedScopes": "",
"AccseeTokenTime": "" //单位秒
}
}
}

  然后添加ApiConfig类:

using Core.Configuration;
using IdentityServer4;
using IdentityServer4.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static IdentityServer4.IdentityServerConstants; namespace Core.Auth.IdentityServer4
{
public class ApiConfig
{
public static IConfiguration Configuration = ConfigureProvider.configuration;
private static string[] ApiNames = { "WebApi" };
/// <summary>
/// Define which APIs will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApiResources()
{
List<ApiResource> apiResources = new List<ApiResource>();
foreach (var item in ApiNames)
{
var configuration = Configuration.GetSection(item);
string name = configuration.GetSection("ApiResource").Value;
string[] scopes = configuration.GetSection("ApiScope").Value.Split(",");
ApiResource apiResource = new ApiResource(name, name) {
Scopes = scopes
};
apiResources.Add(apiResource);
}
return apiResources;
}
public static IEnumerable<ApiScope> GetApiScopes()
{
List<ApiScope> apiScopes = new List<ApiScope>();
foreach (var item in ApiNames)
{
var configuration = Configuration.GetSection(item);
string[] names = configuration.GetSection("ApiScope").Value.Split(",");
foreach(var name in names)
{
ApiScope apiScope = new ApiScope(name, name);
apiScopes.Add(apiScope);
}
}
return apiScopes;
}
/// <summary>
/// Define which Apps will use thie IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
List<Client> clients = new List<Client>();
foreach(var item in ApiNames)
{
var configuration = Configuration.GetSection(item);
string clientId = configuration["Client:ClientId"];
string clientName = configuration["Client:ClientName"];
Secret[] secrets = configuration["Client:ClientSecrets"].Split(',').Select(s=>new Secret(s.Sha256())).ToArray() ;
string[] grantTypes = configuration["Client:AllowedGrantTypes"].Split(",");
List<string> allowedScopes = configuration["Client:AllowedScopes"].Split(',').ToList();
//allowedScopes.Add(IdentityServerConstants.StandardScopes.OpenId);
//allowedScopes.Add(IdentityServerConstants.StandardScopes.Profile);
allowedScopes.Add(StandardScopes.OfflineAccess);
int accseeTokenTime =Convert.ToInt32(configuration["Client:AccseeTokenTime"]);
Client client = new Client();
client.ClientId = clientId;
client.ClientName = clientName;
client.ClientSecrets = secrets;
client.AllowedGrantTypes = grantTypes;
client.AllowedScopes = allowedScopes;
client.AccessTokenLifetime = accseeTokenTime;
client.AllowOfflineAccess = true;
clients.Add(client); }
return clients;
} /// <summary>
/// Define which IdentityResources will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
}
}

  这里有个问题,我网上百度的很多人写的就是  ApiResource apiResource = new ApiResource(name, name);就好了,但是我这里不行,如果不像下面这样写的话就一直401,然后看控制台提示“

IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler[7]
WebApiAuthKey was not authenticated. Failure message: IDX10214: Audience validation failed. Audiences: 'System.String'. Did not match: validationParameters.ValidAudience: 'System.String' or validationParameters.ValidAudiences: 'System.String'.

”不知道是不是因为我集成了Ocelot的原因,也有人说是IdentityServer4的4.0版本之后都要这样写

ApiResource apiResource = new ApiResource(name, name) {
Scopes = scopes
};

  然后开始添加服务认

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions; namespace Core.Auth.IdentityServer4
{
public static class ConfigureIdentityServer4
{
public static IServiceCollection AddIdentityServer4(this IServiceCollection services)
{
string basePath = PlatformServices.Default.Application.ApplicationBasePath;
IIdentityServerBuilder identityServerBuilder = services.AddIdentityServer();
identityServerBuilder.AddDeveloperSigningCredential();
identityServerBuilder.AddInMemoryIdentityResources(ApiConfig.GetIdentityResources())
.AddInMemoryApiResources(ApiConfig.GetApiResources())
.AddInMemoryClients(ApiConfig.GetClients())
.AddInMemoryApiScopes(ApiConfig.GetApiScopes());
return services;
}
}
}

  以上步骤弄完之后,你用postman访问http://localhost:5003/.well-known/openid-configuration,出现一下内容代表搭建成功:

第二步:网关添加认证:

最开始我想的是每一个服务都取添加注册认证服务,这样当然可以实现,但是这样网关悠悠什么用的。我对微服务的理解是,应该所有的请求通过网关,网关应该是是对外的,任何人都不应该知道具体的服务地址,包括认证授权,网关就应该起到一个对api的保护作用。所以我放弃了这种想法,转而去针对网关实现对认证服务的注册,很幸运,ocelot也支持identityserver,只需要简单的配置,如下部分截图:

然后添加配置节点:

  "IdentityService": {
"Uri": "http://localhost:5003",//自己的认证服务地址,网关的地址为5000
"UseHttps": false,
"ApiName": {
"WebApi": "WebApi"
}
}

  并且在Startup里面添加如下代码:

 #region IdentityServerAuthenticationOptions => need to refactor
Action<IdentityServerAuthenticationOptions> webOption = option =>
{
option.Authority = Configuration["IdentityService:Uri"];
option.ApiName = Configuration["IdentityService:ApiName:WebApi"];
option.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityService:UseHttps"]);
};
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication("WebApiAuthKey", webOption);

 到此,可以说已经是初步完成了服务的认证,我这里并没有授权操作,所以就不谈授权。

第三步 :请求获取token

我们通过postman直接访问http://localhost:5003/connect/token,然后输入必要的参数,具体的操作可以网上百度,当然可以获取token。当是我这里是要结合我自己的项目,其中肯定不想让自己设定的一些关键信息如ClientId和Secret让别人知道,放到js里面,别人直接f12查看源码就知道了,所以我的想法是通过nginx设置header的方式,这样就可以了。如下:

       location /auth/credentials/token {
proxy_set_header client_secret "";
proxy_set_header client_id "";
proxy_pass http://127.0.0.1:5000;
}
//我这里是通过Ocelot转发到认证服务的,不要盲目复制

  然后认证服务新加一个api

using Core.Auth;
using Core.Common.EnumExtensions;
using Core.Common.Http;
using IdentityModel.Client;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks; namespace BlogAuthApi
{
[Route("api/auth")]
[ApiController]
public class AuthController: ControllerBase
{
private IHttpClientFactory _httpClientFactory;
public AuthController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
//[Route("token")]
//[HttpGet]
//public ApiResult GetToken()
//{
// DateTime now = DateTime.Now;
// var claims = new Claim[]
// {
// // 声明主题
// new Claim(JwtRegisteredClaimNames.Sub, "Blog"),
// //JWT ID 唯一标识符
// new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
// // 发布时间戳 issued timestamp
// new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64)
// };
// JwtToken jwtToken = Jwt.GetToken(claims);
// return ApiResult.Success(jwtToken);
//} [Route("credentials/token")]
[HttpGet]
public async Task<ApiResult> GetToken()
{
Request.Headers.TryGetValue("client_id", out StringValues clientId);
Request.Headers.TryGetValue("client_secret", out StringValues clientSecret);
HttpClient httpClient= _httpClientFactory.CreateClient();
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("client_id", clientId);
headers.Add("client_secret", clientSecret);
headers.Add("grant_type", "client_credentials");
FormUrlEncodedContent content = new FormUrlEncodedContent(headers);
var response=await httpClient.PostAsync("http://localhost:5003/connect/token",content);
if (!response.IsSuccessStatusCode)
return ApiResult.Error(HttpStatusCode.BAD_REQUEST, response.StatusCode.GetEnumText());
var responseString=await response.Content.ReadAsStringAsync();
dynamic result = JsonConvert.DeserializeObject<dynamic>(responseString);
int expires = result.expires_in;
int minutes = new TimeSpan(0, 0, expires).Minutes;
string token = result.access_token;
JwtToken jwtToken = new JwtToken(token, minutes, DateTime.Now);
return ApiResult.Success(jwtToken);
}
}
}

  这一样,我算是完成了自己的认证保护api的功能。

接下来我们测试一下,访问接口,我这里nginx代理我本地的80端口

获取到token,然后请求自己的接口,成功请求了:

接下来输入一个错误的token,我在token上加上23456,或者不加token,发现直接401了

,

然后过一段时间在访问,因为token已经过期了,所以也会401,我们看控制台输出

这里有个问题,因为我设置的两分钟过期,但是两分钟过后并不会真的立刻过期,具体的原因参考:https://www.cnblogs.com/stulzq/p/8998274.html.

我还没有具体的用在我的站点上面,因为我还没有弄IdentityServer4的证书,只是我本地用postman测试了下,我也是初步学习IdentityServer4,如果有不合理或错误的地方希望大家指出,欢迎大家访问我的站点:天天博客

我的微服务项目之IdentityServer4的更多相关文章

  1. .Net Core 商城微服务项目系列(一):使用IdentityServer4构建基础登录验证

    这里第一次搭建,所以IdentityServer端比较简单,后期再进行完善. 1.新建API项目MI.Service.Identity,NuGet引用IdentityServer4,添加类InMemo ...

  2. SpringCloud(1)---基于RestTemplate微服务项目案例

    基于RestTemplate微服务项目 在写SpringCloud搭建微服务之前,我想先搭建一个不通过springcloud只通过SpringBoot和Mybatis进行模块之间额通讯.然后在此基础上 ...

  3. Docker部署golang微服务项目

    这篇博客是为了记录一下部署步骤. 因为实训需要,我要在服务器上用docker部署我们小组的微服务项目.我们的微服务有Gateway,User,Scene,Device四个部分,分别占用不同的端口,其中 ...

  4. docker微服务部署之:三,搭建Zuul微服务项目

    docker微服务部署之:二.搭建文章微服务项目 一.新增demo_eureka模块,并编写代码 右键demo_parent->new->Module->Maven,选择Module ...

  5. docker微服务部署之:二、搭建文章微服务项目

    docker微服务部署之:一,搭建Eureka微服务项目 一.新增demo_article模块,并编写代码 右键demo_parent->new->Module->Maven,选择M ...

  6. docker微服务部署之:一,搭建Eureka微服务项目

    先说明一下docker需要搭建的微服务的基本情况: 项目情况:一个demo_parent项目,下面三个子模块:demo_eureka(eureka服务).demo_article(文章服务).demo ...

  7. 【spring colud】spring cloud微服务项目搭建【spring boot2.0】

    spring cloud微服务项目搭建 =================================== 示例版本: 1.spring boot 2.0版本 2.开发工具 IntellJ IDE ...

  8. Docker 搭建 ELK 读取微服务项目的日志文件

    思路: 在docker搭建elasticsearch与kibana来展示日志,在微服务部署的机子上部署logstash来收集日志传到elasticsearch中,通过kibana来展示,logstas ...

  9. 毕业设计代做,各种系统微服务项目ssm项目,员工管理系统,微信小程序,购物商城,二手商城系统,销售系统,等等

    毕业设计代做,各种系统,微服务项目,ssm项目 小程序,商城等,期末作业等都可以,价格好说,长期接单, 有项目说明书,软件介绍相关文档,答辩的时候包过,知识点对接好,给你讲解等, 毕业设计代做,各种系 ...

  10. springboot+springcloud微服务项目全套资料(笔记+源码+代码)

    最近好几天没有写博客了,由于时间的太忙了,项目要做.各种资格证要考试,实在没有时间写了,今天正好赶上有闲暇的一刻,应许多的爱好者的要求发一份微服务项目的资料,此资料十分完整,且是最新的.希望各位读者能 ...

随机推荐

  1. 实测分享,瑞芯微RK3588八核国产处理器性能测评!确实“遥遥领先”!

    RK3588处理器优势 RK3588是瑞芯微最新一代旗舰工业处理器,它具有如下卓越优势: 超强运算能力:四核ARM Cortex-A76@2.4GHz + 四核ARM Cortex-A55@1.8GH ...

  2. vulnhub - hackme1

    vulnhub - hackme1 信息收集 端口扫描 详细扫描 目录扫描跟漏洞探测没发现什么可用信息,除了登录还有一个uploads目录应该是进入后台之后才能使用 web主页是个登录注册页面,爆了一 ...

  3. Oracle 日期减年数、两日期相减

    -- 日期减年数 SELECT add_months(DEF_DATE,12*USEFUL_LIFE) FROM S_USER --两日期相减 SELECT round(sysdate-PEI.STA ...

  4. 透视开源生态,OSGraph——GitHub全域数据图谱的智能洞察工具

    "透视开源生态,OSGraph--GitHub全域数据图谱的智能洞察工具 OSGraph (Open Source Graph) 是一个开源图谱关系洞察工具,基于GitHub开源数据全域图谱 ...

  5. 洛谷P2864

    来一发在洛谷的第一篇题解 解析 首先从原点出发回到原点会形成一个环 要计算在环上的路程,首先我们要破环 利用建墙法破环 可以选取任意一个边缘上的树,往上或者往下或者往左往右建立一堵墙'|' 这样利用b ...

  6. 全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系,你知道多少?

    全网最适合入门的面向对象编程教程:09 类和对象的 Python 实现-类之间的关系,你知道多少? 摘要: 本文主要对类之间的关系进行了基本介绍,包括继承.组合.依赖关系,并辅以现实中的例子加以讲解, ...

  7. UE5 射线检测排除隐藏的Actor

    0x00 Unreal Engine 5(UE5)以其卓越的性能和直观的开发工具在游戏开发领域占据了重要地位.本系列将深入探讨UE5中射线检测的关键概念,着重介绍处理隐藏Actor的技巧. 0x01. ...

  8. mysql大数据表添加字段

    方案一.老表数据迁移四部曲方案1.新建老表t_order_goods的备份表t_order_goods_bak,同时加一个字段:isVirtual 并给默认值2.迁移老表t_order_goods数据 ...

  9. [oeasy]python0030_设置路径_export_PATH_zsh_系统路径设置_export

    ​ 放入路径 回忆上次内容 我们要在任意路径下直接执行 sleep.py 把 sleep.py 放在 /usr/bin/ 下面 最终可以在任意位置执行程序sleep.py 但是 /usr/bin 里面 ...

  10. 题解:AT_abc357_f [ABC357F] Two Sequence Queries

    题意 维护一个数据结构,支持两个数列的区间求和,和查询区间内两数列各元素积的和. 分析 线段树万岁! 这道题要维护两个序列,所以线段树中要同时存储两个区间和.但还要在维护一个信息,是该区间内两序列元素 ...