OiDc能夠說是OAuth的改造版,在最初的OAuth中,咱們須要先請求一下認證服務器獲取下Access_token,而後根據Access_token去Get資源服務器, 何況OAuth1 和 2 徹底不兼容,易用性差,而OIDC能夠在登錄的時候就把信息返回給你,不須要你在請求一下資源服務器。下面咱們根據Oidc來作一個單點登陸。html
新建三個項目(.NET Core Mvc)兩個Client(端口5001,5002),一個Server(5000),首先在Server中添加IdentityServer4的引用。web
在Server中Config.cs用於模擬配置。api
public class Config { public static IEnumerable<ApiResource> GetApiResource() { return new List<ApiResource> { new ApiResource("api","My Api App") }; } public static IEnumerable<Client> GetClients() { return new List<Client> { new Client() { ClientId = "mvc", AllowedGrantTypes = GrantTypes.Implicit, ClientSecrets ={ new Secret("secret".Sha256()) }, RequireConsent = false, RedirectUris = {"http://localhost:5001/signin-oidc", "http://localhost:5002/signin-oidc" } , PostLogoutRedirectUris = {"http://localhost:5001/signout-callback-oidc" , "http://localhost:5002/signout-callback-oidc" }, AllowedScopes = { IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.OpenId } } }; } public static List<TestUser> GetTestUsers() { return new List<TestUser> { new TestUser() { SubjectId = "10000", Username = "zara", Password = "112233" } }; } public static IEnumerable<IdentityResource> GetIdentityResources() { return new List<IdentityResource> { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }; } }
GetClient方法中字段爲RedirectUris是登錄成功返回的地址,而且咱們採用隱式模式(由於只是傳統web中傳遞Access_Token),RequireConsent是否出現贊成受權頁面,這個咱們後續再細說.寫完Config.cs後,咱們須要依賴注入到IdentityServer中。安全
public void ConfigureServices(IServiceCollection services) { 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; });
//config to identityServer Services services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetTestUsers()) .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApiResource()); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
在Configure中添加代碼 app.UseIdentityServer(); .咱們還須要添加一個登錄頁面,名爲Account.cshtml.服務器
@{ ViewData["Title"] = "Index"; } <h2>Index</h2> @using mvcWebFirstSolucation.Models; @model LoginVM; <div class="row"> <div class="col-md-4"> <section> <form method="post" asp-controller="Account" asp-action="Login" asp-route-returnUrl="@ViewData["returnUrl"]"> <h4>Use a local to log in .</h4> <hr /> <div class="from-group"> <label asp-for="UserName"></label> <input asp-for="UserName" class="form-control"> <span asp-validation-for="UserName" class="text-danger"></span> </div> <div class="from-group"> <label asp-for="PassWord"></label> <input asp-for="PassWord" type="password" class="form-control"> <span asp-validation-for="UserName" class="text-danger"></span> </div> <div class="from-group"> <button type="submit" class="btn btn-default">log in </button> </div> </form> </section> </div> </div> @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") }
在控制器中咱們寫一個構造函數,用於將IdentityServer.Text給咱們封裝好的對象傳過來,這個對象是咱們在Config.cs中添加的用戶信息,也就是GetClients的返回值,全都在 TestUserStore 中。其中還有一個提供好的方法,來給咱們用,若是驗證經過咱們直接跳轉到了傳遞過來的ReturnUrl.cookie
public class AccountController : Controller { private readonly TestUserStore _users; public AccountController(TestUserStore ussotre) { _users = ussotre; } [HttpGet] [Route("/Account/Login")] public IActionResult Index(string ReturnUrl = null) { ViewData["returnUrl"] = ReturnUrl; return View(); } private IActionResult RediretToLocal(string returnUrl) { if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction(nameof(HomeController.Index),"Home"); } [HttpPost] public async Task<IActionResult> Login(LoginVM vm,string returnUrl = null) { if (ModelState.IsValid) { ViewData["returnUrl"] = returnUrl; var user = _users.FindByUsername(vm.UserName); if (user==null) { ModelState.AddModelError(nameof(LoginVM.UserName), "userName is exists"); } else { if(_users.ValidateCredentials(vm.UserName, vm.PassWord)) { var props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30)) }; await Microsoft.AspNetCore.Http .AuthenticationManagerExtensions .SignInAsync( HttpContext, user.SubjectId, user.Username, props ); return RediretToLocal(returnUrl); } ModelState.AddModelError(nameof(LoginVM.PassWord), "Wrong Password"); } } return View(); } }
這個時候最基本的服務端已經配置成功了,咱們開始配置受保護的客戶端吧。mvc
在客戶端中咱們不須要引入IdentityServer,由於咱們只是去請求服務端而後看看cookies有沒有在而已,因此咱們只須要給受保護的客戶端的Api作好安全判斷就好.app
在受保護的控制器中添加 [Authorize] 標識。而後再Startup.cs中添加安全驗證。而且在Configure中use下 app.UseAuthentication(); async
public void ConfigureServices(IServiceCollection services) { 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.ClientSecret = "secret"; options.SaveTokens = true; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
在首頁中最好遍歷下Claims對象,這個是經過OIDC直接給咱們返回回來的.(最後另外一個客戶端也這麼幹!)ide
<div> @foreach (var claim in User.Claims) { <dl> <dt>@claim.Type</dt> <dd>@claim.Value</dd> </dl> } </div>
如今咱們啓動項目看一下效果吧。