.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 = "10001", Username = "admin", Password = "admin" }, new TestUser { SubjectId = "10002", Username = "wei", Password = "123" }, new TestUser { SubjectId = "10003", Username = "test", Password = "123" } }; } }
简单介绍一下,既然是微服务项目,比如有需要的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": "7001", "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": "7000", "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(30), 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实现网关转发请求并验证。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步