這裏第一次搭建,因此IdentityServer端比較簡單,後期再進行完善。web
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的名稱。json
Client是發起調用發,好比咱們的Web系統會調用API,那Web系統就是一個Client,也能夠理解爲一個角色,Client Id是角色標識,這個也須要在發起調用方那邊配置,ClientSecrets是私鑰,這裏使用最簡單的自帶私鑰,AllowedScopes是當前這個Client能夠訪問的ApiResource。api
TestUser是IdentityServer自帶的測試用戶類,用戶使用用戶名和密碼的方式登陸使用。緩存
而後須要在Startup中添加IdentityServer配置:restful
在ConfigureServices方法中添加以下:cookie
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
這裏咱們使用的均是內存級別的配置,在實際項目裏建議改成數據庫中讀取。app
而後在Configure方法中啓用IdentityServer:異步
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseIdentityServer(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); }
到此IdentityServer驗證端配置完畢。async
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實現網關轉發請求並驗證。