開發環境:
vs2017、net Core 2.1、sqlserver2008、IdentityServer4版本2.4.0。
一、搭建IdentityServer4服務端
打開VS2017,新建 netcore項目: 名字叫:IdentityS4, 然后選擇webMVC這個,如下圖:
引進安裝依賴項:IdentityServer4
設置該項目的地址為:http://localhost:5000
1、新建一個配置文件類:Config.cs 代碼如下:
using IdentityServer4; using IdentityServer4.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IdentityS4 { public class Config { // scopes define the resources in your system public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; } // clients want to access resources (aka scopes) public static IEnumerable<Client> GetClients() { return new List<Client> { // OpenID Connect隱式流客戶端(MVC) new Client { ClientId = "mvc", ClientName = "MVC Client", AllowedGrantTypes = GrantTypes.Implicit,//隱式方式 RequireConsent=false,//如果不需要顯示否同意授權 頁面 這里就設置為false RedirectUris = { "http://localhost:5002/signin-oidc" },//登錄成功后返回的客戶端地址 PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },//注銷登錄后返回的客戶端地址 AllowedScopes =//下面這兩個必須要加吧 不太明白啥意思 { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }; } } }
在Startup.cs的ConfigureServices方法中注入Ids4服務,如下面紅色部分代碼:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddIdentityServer()//Ids4服務 .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients());//把配置文件的Client配置資源放到內存
}
在Startup.cs的Configure方法中添加ids4服務中間件(注意要放在UseMvc之前就可以):
app.UseIdentityServer();
現在ids4本身的基本的配置弄好了,下面我們要開始弄跟數據庫相關的東西了,大家想想,既然要登錄,那肯定需要連接數據庫並且讀取出其中的用戶信息去驗證,比如賬號、密碼。好的,那我們就按照下面一步一步來做
添加DbContext類 名字叫:EFContext.cs ,代碼如下(其中紅色部分是我們待會需要添加的實體類,也就是對應數據庫里面的用戶表Admin,並添加實體類對應紅色部分):
public class EFContext : DbContext { public EFContext(DbContextOptions<EFContext> options) : base(options) { } #region 實體集 public DbSet<Admin> Admin { get; set; }//注意 這里這個Admin不能寫成Admins否則會報錯找不到Admins 因為我們現在數據庫和表是現成的 這里就相當於實體對應的數據庫是Admin #endregion } public class Admin { public int Id { get; set; } public DateTime CreateDate { get; set; } public string UserName { get; set; } public string Password { get; set; } public string Remark { get; set; } }
數據庫建立Admin表
/* Navicat SQL Server Data Transfer Source Server : 輕量應用服務器 Source Server Version : 100000 Source Host : 120.79.21.96 :1433 Source Database : test Source Schema : dbo Target Server Type : SQL Server Target Server Version : 100000 File Encoding : 65001 Date: 2019-05-22 09:56:13 */ -- ---------------------------- -- Table structure for Admin -- ---------------------------- DROP TABLE [dbo].[Admin] GO CREATE TABLE [dbo].[Admin] ( [Id] int NOT NULL , [CreateDate] datetime NULL , [UserName] nvarchar(MAX) NULL , [Password ] nvarchar(MAX) NULL , [Remark] nvarchar(MAX) NULL ) GO -- ---------------------------- -- Records of Admin -- ---------------------------- INSERT INTO [dbo].[Admin] ([Id], [CreateDate], [UserName], [Password ], [Remark]) VALUES (N'1', N'2019-05-21 18:05:52.000', N'test', N'123456', N'測試賬號') GO GO
在Startup.cs的ConfigureServices方法中注入 EFContext,如下面紅色部分代碼:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<EFContext>(options=>options.UseSqlServer(Configuration.GetConnectionString("conn")));//注入DbContext services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddIdentityServer()//Ids4服務 .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients());//把配置文件的Client配置資源放到內存 }
接下來,我們就要寫Admin這個實體類跟數據庫打交道的代碼了,比如增刪改查啊,在net中一般交DAL層,在netCore中 一般交services層,要注意的是 netcore的框架是IOC的框架,依賴注入的,所以這個services層需要接口的形式!
新建一個接口:IAdminService.cs 代碼如下:
public interface IAdminService { Task<Admin> GetByStr(string username, string pwd);//根據用戶名和密碼查找用戶 }
新建實現該接口的類AdminService.cs
public class AdminService:IAdminService { public EFContext db; public AdminService(EFContext _efContext) { db = _efContext; } /// <summary> /// 驗證用戶,成功則返回用戶信息,否則返回null /// </summary> /// <param name="username"></param> /// <param name="pwd"></param> /// <returns></returns> public async Task<Admin> GetByStr(string username, string pwd) { Admin m=await db.Admin.Where(a => a.UserName == username && a.Password == pwd).SingleOrDefaultAsync(); if (m!=null) { return m; } else { return null; } } }
在Startup.cs的ConfigureServices方法中注入 service層,如下面紅色部分代碼:
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<EFContext>(options=>options.UseSqlServer(Configuration.GetConnectionString("conn")));//注入DbContext services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddIdentityServer()//Ids4服務 .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients());//把配置文件的Client配置資源放到內存 services.AddTransient<IAdminService,AdminService>();//service注入 }
在配置文件appsettings.json中添加數據庫連接字符串如下紅色部分代碼:
{ "ConnectionStrings": { "conn": "server=120.79.21.96 ;database=test;user=sa;password=Sa123456;" }, "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*" }
至此,應該是可以正確的連接數據庫的,大家可以去Home控制器中查詢點數據測試下顯示到首頁,保證能連接數據庫成功的查詢出數據就可以。
接下來 我們來做登錄頁面
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using IdentityS4.Models; using IdentityServer4.Services; using IdentityServer4.Stores; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace IdentityS4.Controllers { public class AccountController : Controller { private IAdminService _adminService;//自己寫的操作數據庫Admin表的service private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController(IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, IAdminService adminService) { _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; _adminService = adminService; } /// <summary> /// 登錄頁面 /// </summary> [HttpGet] public async Task<IActionResult> Login(string returnUrl = null) { ViewData["returnUrl"] = returnUrl; return View(); } /// <summary> /// 登錄post回發處理 /// </summary> [HttpPost] public async Task<IActionResult> Login(string userName, string password, string returnUrl = null) { ViewData["returnUrl"] = returnUrl; Admin user = await _adminService.GetByStr(userName, password); if (user != null) { AuthenticationProperties props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromDays(1)) }; await HttpContext.SignInAsync(user.Id.ToString(), user.UserName, props); if (returnUrl != null) { return Redirect(returnUrl); } return View(); } else { return View(); } } } }
添加登錄view視圖,代碼如下:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Login</title> </head> <body> <div align="center"> <h1>統一認證登錄中心</h1> <form action="/Account/Login" method="post"> 用戶名:<input type="text" name="userName" /><br /> 密 碼:<input type="password" name="password" /><input type="hidden" name="returnUrl" value="@ViewData["returnUrl"]" /> <br /> <input type="submit" value="登錄" /> </form> </div> </body> </html>
至此,IdentityServer4服務端的工作完成,粘貼全部代碼即一個重要文件文件代碼(Startup.cs) 主要看擺放位置
Startup.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using IdentityS4.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace IdentityS4 { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext<EFContext>(options => options.UseSqlServer(Configuration.GetConnectionString("conn")));//注入DbContext services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); services.AddIdentityServer()//Ids4服務 .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryClients(Config.GetClients());//把配置文件的Client配置資源放到內存 services.AddTransient<IAdminService, AdminService>();//service注入 } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseIdentityServer(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } }
接下來我們要開始建客戶端了,也就是需要保護的MVC網站
二、搭建客戶端
新建一個名為 MvcClient 的
把地址設置為:http://localhost:5002
在Startup.cs的ConfigureServices方法中添加如下紅色部分代碼(主要用來配置認證中心ids4的及自己作為客戶端的認證信息):
public void ConfigureServices(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.SignInScheme = "Cookies"; options.Authority = "http://localhost:5000"; options.RequireHttpsMetadata = false; options.ClientId = "mvc"; options.SaveTokens = true; });
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
在 Configure方法中添加這行代碼(需要在UseMvc之前就可以):
app.UseAuthentication();
到此,客戶端跟統一認證的信息就配置完了。接下來我們把Home控制器上面加上需要驗證的標志:[Authorize]
我們把默認的Index視圖頁面html代碼去掉,改成如下(主要用來顯示下授權后拿到的用戶信息):
@{ ViewData["Title"] = "Home Page"; } <div align="center"><h1>這里是受保護的客戶端首頁</h1></div> <h3>User claims</h3> <dl> @foreach (var claim in User.Claims) { <dt>@claim.Type</dt> <dd>@claim.Value</dd> } </dl>
到此,客戶端的工作也做完了,下面我們要開始啟動項目了
設置項目為多項目啟動:解決方案上右鍵-屬性
現在我們啟動項目:服務器項目和 客戶端都運行了,但是客戶端會直接跳轉到服務端登錄頁面
服務端
客戶端(5002)跳轉過來的登錄頁面:
然后輸入正確賬號密碼 點擊登錄認證通過之后就跳轉回 客戶端網站去了
至此 ,例子結束!
從這個例子中,咱們可以再加幾個客戶端網站,然后統一到這個ids4認證,這樣就達到了單點登錄統一認證的效果了!