.Net Core 商城微服务项目系列(一):使用IdentityServer4构建基础登录验证
这里第一次搭建,所以IdentityServer端比较简单,后期再进行完善。
1.新建API项目MI.Service.Identity,NuGet引用IdentityServer4,添加类InMemoryConfiguration用于配置api和客户端资源:
public class InMemoryConfiguration
{
public static IConfiguration Configuration { get; set; }
/// <summary>
/// Define which APIs will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource("MI.Service", "MI.Service"),
};
} /// <summary>
/// Define which Apps will use thie IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "MI.Web",
ClientSecrets = new [] { new Secret("miwebsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes = new [] { "MI.Service" }
}
};
} public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
} /// <summary>
/// Define which uses will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<TestUser> GetUsers()
{
return new[]
{
new TestUser
{
SubjectId = "",
Username = "admin",
Password = "admin"
},
new TestUser
{
SubjectId = "",
Username = "wei",
Password = ""
},
new TestUser
{
SubjectId = "",
Username = "test",
Password = ""
}
};
}
}
简单介绍一下,既然是微服务项目,比如有需要的API,ApiResource即我们要使用的API资源,这里我用“MI.Service”,后面的API项目也需要和这里配置的相同。当前也可以每一个API项目都新建一个ApiResource的名称。
Client是发起调用发,比如我们的Web系统会调用API,那Web系统就是一个Client,也可以理解为一个角色,Client Id是角色标识,这个也需要在发起调用方那边配置,ClientSecrets是私钥,这里使用最简单的自带私钥,AllowedScopes是当前这个Client可以访问的ApiResource。
TestUser是IdentityServer自带的测试用户类,用户使用用户名和密码的方式登录使用。
然后需要在Startup中添加IdentityServer配置:
在ConfigureServices方法中添加如下:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
这里我们使用的均是内存级别的配置,在实际项目里建议改为数据库中读取。
然后在Configure方法中启用IdentityServer:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
到此IdentityServer验证端配置完毕。
2.新建API项目MI.Service.Account,NuGet引用 IdentityServer4.AccessTokenValidation。
在Startup的ConfigureServices方法中进行IdentityServer4配置:
services.AddAuthentication(Configuration["Identity:Scheme"]) //
.AddIdentityServerAuthentication(options =>
{
options.RequireHttpsMetadata = false; // for dev env
options.Authority = $"http://{Configuration["Identity:IP"]}:{Configuration["Identity:Port"]}"; //IdnetityServer项目IP和端口
options.ApiName = Configuration["Service:Name"]; // match with configuration in IdentityServer //当前API项目的ApiResource的名称 即我们上个项目的“MI.Service”
});
在Configure中启用验证:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseAuthentication(); //启用验证
app.UseMvcWithDefaultRoute(); }
我们整理用的是appsettings.json的配置,配置如下:
{
"Service": {
"Name": "MI.Service",
"Port": "",
"DocName": "Account Service",
"Version": "v1",
"Title": "Account Service API",
"Description": "CAS Client Service API provide some API to help you get client information from CAS"
//"XmlFile": "Manulife.DNC.MSAD.IdentityServer4Test.ApiService01.xml"
},
"Identity": {
"IP": "localhost",
"Port": "",
"Scheme": "Bearer"
}
}
我们的IdentityServer项目运行在7000端口,当前API项目运行在70001端口,大家可以根据需要自行配置。
在当前API项目新增控制器MiUserController,并新增一个测试方法和一个登陆方法:
[EnableCors("AllowCors")]
[Authorize] //这里添加验证标签
public class MiUserController : Controller
{
//实体上下文类
public MIContext _context;
public MiUserController(MIContext _context)
{
this._context = _context;
}
//这个方法用来进行测试
public IActionResult Index()
{
return Json("Successful");
}
public async Task<SSOLoginResponse> SSOLogin(SSOLoginRequest request)
{
SSOLoginResponse response = new SSOLoginResponse();
try
{
if (!string.IsNullOrEmpty(request.UserName) && !string.IsNullOrEmpty(request.Password))
{
var user = _context.UserEntities.FirstOrDefault(a => a.CustomerPhone.Equals(request.UserName));
if (user == null)
{
response.Successful = false;
response.Message = "用户名或密码错误!";
return response;
}
if (user.CustomerPwd == request.Password)
{
//将用户名存储硬盘cookie 30分钟 作用域为整个网站
HttpContext.Response.Cookies.Append("MIUserName", user.CustomerPhone, new Microsoft.AspNetCore.Http.CookieOptions
{
Expires = DateTime.Now.AddMinutes(),
Path = "/",
});
return response;
}
}
response.Successful = false;
response.Message = "用户名密码不能为空!";
}
catch (Exception ex)
{
response.Successful = false;
response.Message = ex.Message;
}
return response;
}
}
现在配置完成,我们现在PostMan中测试一下请求IdentityServer项目获取Token,下面请求参数分别是我们之前配置的:

不出意外我们能够获取到对应的Token。
拿到Token后我们可以使用它来请求API项目:MI.Service.Account:

Token前我们必须要有Bearer这个,我们之前在API项目的appsettings.json中也加过这个配置,如果一切正常我们能够获取当测试方法Index返回的“Successful”。
3.新建Web项目MI.Web,毕竟这些API项目需要有调用方,要么是Web端,要么是移动端,既然是商城就要有一个Web端界面。
通过Nuget添加 IdentityModel。

在Web项目的Startup.cs的ConfigureServices方法中注册缓存使用,我们获取的Token需要存储在缓存中重复使用:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddMemoryCache(); //注册缓存
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
} app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); //添加默认的MVC请求路由
}
在Web项目的appsettings.json中配置对应的API项目地址:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"ServiceAddress": {
"Service.Identity": "http://localhost:7000/",
"Service.Account": "http://localhost:7001/"
},
"MehtodName": {
"Account.MiUser.SSOLogin": "MiUser/SSOLogin", //登录
"Identity.Connect.Token": "connect/token" //获取token
}
}
接下来我们需要在Web中获取Token就需要有一个公用的方法,我在ApiHelper中添加了一个方法如下,这里使用了IdentityModel提供的方法来获取Token:
//获取Token
public static async Task<string> GetToken()
{
string token = null;
if (cache.TryGetValue<string>("Token", out token))
{
return token;
}
try
{
//DiscoveryClient类:IdentityModel提供给我们通过基础地址(如:http://localhost:5000)就可以访问令牌服务端;
//当然可以根据上面的restful api里面的url自行构建;上面就是通过基础地址,获取一个TokenClient;(对应restful的url:token_endpoint "http://localhost:5000/connect/token")
//RequestClientCredentialsAsync方法:请求令牌;
//获取令牌后,就可以通过构建http请求访问API接口;这里使用HttpClient构建请求,获取内容;
var dico = await DiscoveryClient.GetAsync("http://localhost:7000");
var tokenClient = new TokenClient(dico.TokenEndpoint, "MI.Web", "miwebsecret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("MI.Service");
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error); }
token = tokenResponse.AccessToken;
cache.Set<string>("Token", token, TimeSpan.FromSeconds(tokenResponse.ExpiresIn));
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return token;
}
有了获取令牌的方法还需要有一个请求API的POST帮助方法,如下:(大家可以根据自己的习惯替换,重点是要加入Token)
private static MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
/// <summary>
/// HttpClient实现Post请求
/// </summary>
public static async Task<T> PostAsync<T>(string url, Dictionary<string, string> dic)
{
//设置HttpClientHandler的AutomaticDecompression
var handler = new HttpClientHandler() { AutomaticDecompression = DecompressionMethods.GZip };
//创建HttpClient(注意传入HttpClientHandler)
using (var http = new HttpClient(handler))
{
//添加Token
var token = await GetToken();
http.SetBearerToken(token);
//使用FormUrlEncodedContent做HttpContent
var content = new FormUrlEncodedContent(dic);
//await异步等待回应
var response = await http.PostAsync(url, content);
//确保HTTP成功状态值
response.EnsureSuccessStatusCode();
//await异步读取最后的JSON(注意此时gzip已经被自动解压缩了,因为上面的AutomaticDecompression = DecompressionMethods.GZip)
string Result = await response.Content.ReadAsStringAsync();
var Item = JsonConvert.DeserializeObject<T>(Result);
return Item;
}
}
有了这些之后我们新建一个登陆控制器 LoginController,新建登陆方法:
public async Task<JsonResult> UserLogin(string UserName, string UserPwd)
{
string url = $"{configuration["ServiceAddress:Service.Account"]}{configuration["MehtodName:Account.MiUser.SSOLogin"]}";
var dictionary = new Dictionary<string, string>();
dictionary.Add("UserName", UserName);
dictionary.Add("Password", MD5Helper.Get_MD5(UserPwd));
SSOLoginResponse response = null;
try
{
response = await ApiHelper.PostAsync<SSOLoginResponse>(url, dictionary);
}
catch(Exception ex)
{
return Json(ex.Message);
}
if(response.Successful)
{
return Json("ok");
}
return Json(response.Message);
}
然后将三个项目分别发布在IIS中,访问Web登陆页面:

输入用户密码登陆测试,这里我们会请求MI.Service.Account这个API项目的登陆方法:

登陆成功即说明通过了验证,下一步将加入Ocelot,结合IdentityServer4实现网关转发请求并验证。
.Net Core 商城微服务项目系列(一):使用IdentityServer4构建基础登录验证的更多相关文章
- .Net Core 商城微服务项目系列(十四):分布式部署携程Apollo构建配置中心
一.开场白 在系统设计里我们有很多配置希望独立于系统之外,而又能够被系统实时读取.但是在传统的系统设计里,配置信息通常是耦合在系统内的,比如.net里通常会放在App.config或者web.conf ...
- .Net Core 商城微服务项目系列(六):搭建自己的Nuget包服务器
当我们使用微服务架构之后,紧接而来的问题便是服务之间的程序集引用问题,可能没接触过的同学不太理解这句话,都已经微服务化了为什么还要互相引用程序集,当然可以不引用.但是我们会有这样一种情况,我们的每个接 ...
- .Net Core 商城微服务项目系列(七):使用消息队列(RabbitMQ)实现服务异步通信
RabbitMQ是什么,怎么使用我就不介绍了,大家可以到园子里搜一下教程.本篇的重点在于实现服务与服务之间的异步通信. 首先说一下为什么要使用消息队列来实现服务通信:1.提高接口并发能力. 2.保证 ...
- .Net Core 商城微服务项目系列(十二):使用k8s部署商城服务
一.简介 本篇我们将会把商城的服务部署到k8s中,同时变化的还有以下两个地方: 1.不再使用Consul做服务的注册和发现,转而使用k8s-dns来实现. 2.不再使用Ocelot作为业务网关,使用T ...
- .Net Core 商城微服务项目系列(五):使用Polly处理服务错误
项目进行微服务化之后,随之而来的问题就是服务调用过程中发生错误.超时等问题的时候我们该怎么处理,比如因为网络的瞬时问题导致服务超时,这在我本人所在公司的项目里是很常见的问题,当发生请求超时问题的时候, ...
- .Net Core 商城微服务项目系列(八):购物车
最近加班有点多,一周五天,四天加班到11点+,心很累.原因是我当前在的这个组比较特殊,相当于业务的架构组,要为其它的开发组提供服务和监控.所以最近更新的也少,不过这个元旦三天假应该会更新三篇. 这篇是 ...
- .Net Core 商城微服务项目系列(十三):搭建Log4net+ELK+Kafka日志框架
之前是使用NLog直接将日志发送到了ELK,本篇将会使用Docker搭建ELK和kafka,同时替换NLog为Log4net. 一.搭建kafka 1.拉取镜像 //下载zookeeper docke ...
- .Net Core 商城微服务项目系列(十):使用SkyWalking构建调用链监控(2019-02-13 13:25)
SkyWalking的安装和简单使用已经在前面一篇介绍过了,本篇我们将在商城中添加SkyWalking构建调用链监控. 顺带一下怎么把ES设置为Windows服务,cd到ES的bin文件夹,运行ela ...
- .Net Core 商城微服务项目系列(十五): 构建定时任务调度和消息队列管理系统
一.系统描述 嗨,好久不见各位老哥,最近有点懒,技术博客写的太少了,因为最近在写小说,写的顺利的话说不定就转行了,哈哈哈哈哈哈哈哈哈. 今天要介绍的是基于.Net Core的定时任务调度和消息队列管理 ...
随机推荐
- Markdown实用技巧整理
标题 段落 列表 引用 代码块 链接 图片 一.标题 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 二.段落 1.换行: 2.加粗: 3.分割线: 4.删除线: deprecated 5 ...
- Nginx总结(五)如何配置nginx和tomcat实现反向代理
前面讲了如何配置Nginx虚拟主机,大家可以去这里看看nginx系列文章:https://www.cnblogs.com/zhangweizhong/category/1529997.html 今天要 ...
- CF1029C Maximal Intersection 暴力枚举
Maximal Intersection time limit per test 3 seconds memory limit per test 256 megabytes input standar ...
- hdu 3966 Aragorn's Story(树链剖分+区间修改+单点查询)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意:给一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路径上 ...
- GIT常见问题及其解决方案
问题: remote: Permission to beijing01/learn_github.git denied to liuhongyang02. fatal: unable to acces ...
- 我真的不想再用 JPA 了
在开发者的圈子里,没当说到一种技术好或者不好,都会引发激烈或者不激烈的争论,直到一个开发者出来说 PHP 是世界上最好的语言,大家伙儿才会纷纷退去继续写代码. 今天说 JPA 的问题不是想引发什么讨论 ...
- FreeSql (二十九)Lambda 表达式
FreeSql 支持功能丰富的表达式函数解析,方便程序员在不了解数据库函数的情况下编写代码.这是 FreeSql 非常特色的功能之一,深入细化函数解析尽量做到满意,所支持的类型基本都可以使用对应的表达 ...
- C#将数据导入到excel文件
最近在做C#对excel的操作程序,简单的与datagridview的交互如下 using System;using System.Collections.Generic;using System.C ...
- 自动更新Aria2中的bt-trackers选项
#!/bin/bash /usr/sbin/service aria2 stop list=`wget -qO- https://raw.githubusercontent.com/ngosang/t ...
- 采用WPF技术,开发OFD电子文档阅读器
前言 OFD是国家标准版式文档格式,于2016年生效.OFD文档国家标准参见<电子文件存储与交换格式版式文档>.既然是国家标准,OFD随后肯定会首先在政务系统使用,并逐步推向社会各个方面. ...