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. VBA-合并多个工作簿

    '合并多个工作薄,并以工作薄的名字给sheet表命名(每个工作薄只有一张表) Sub test() Dim str As String Dim wb As Workbook str = Dir(&qu ...

  2. AJAX基础+Axios快速入门+JSON使用+综合案例

    目录 1. AJAX 1.1 概述 1.1.1 作用 1.1.2 同步和异步 1.2 快速入门 1.2.1 服务端实现 1.2.2 客户端实现 1.3 案例 1.3.1 需求 1.3.2 分析 1.3 ...

  3. yb课堂 首页home开发 《三十七》

    Home模块开发 拆分子组件 Home banner videoList 指令属性里面取data里面的数据不用加{{}},html标签内容体中间则需要加双花括号 创建component文件夹 在src ...

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

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

  5. [oeasy]python0133_[趣味拓展]好玩的unicode字符_另类字符_上下颠倒英文字符

    另类字符 回忆上次内容 上次再次输出了大红心<span style="font-size:64px;color:red"></span> 找到了红心对应的编 ...

  6. [oeasy]python0031_挂起进程_恢复进程_进程切换

    ​ 查看进程 回忆上次内容 上次修改了 $PATH 路径 把当前用户shiyanlou的宿主文件夹 ~ 添加到 $PATH 中 这样 sleep.py 就可以被找到 于是就可以被执行了 还可以把配置 ...

  7. [oeasy]python0024_ 输出时间_time_模块_module_函数_function

    ​ 输出时间 回忆上次内容 ​print​​函数 有个默认的 ​​end参数​ ​​end参数​​ 的值可以是任意字符串 ​​end参数​​ 的值会输出到结尾位置 ​​end参数​​ 的默认值是 ​​ ...

  8. 工作单元(UnitOfWork) 模式 (1)

    在开始UnitOfWork模式之前有必要回顾下我们耳熟能详的Data Access Object(DAO)模式,即数据访问对象.DAO是一种简单的模式,我们构建应用的时候经常会使用到它,它的功能就是将 ...

  9. 关于Script的猜想和代码设计

    由于现在接触的是蓝图,而之前接触的脚本,这两者有些不一样. 对脚本的设计如果是代码的解析的话, 对蓝图的设计则需要提供一些底层的API. 变量分为:  基础类型 ,复合类型 ,容器类型 NewGlob ...

  10. 分享一个idea的文档汉化插件

    展示效果: 如何获取: