1. 概述
-
上一個實例 中實現了簡單的 ResourceOwnerPassword 授權.
-
本例將使用IdentityServer4 實現內部系統的單點登錄,讓組織內部的MVC 客戶端使用認證中心的登錄頁實現登錄,使用的是OAuth2 的隱式授權碼模式Implicit AthorizationCode
-
MVC 客戶端集成IdentityServer4 認證時,爲簡單起見,將使用cookie存儲客戶端憑據;同時啓用IdentityServer4 內置的 OpenIdConnect(OIDC)身份認證服務。
2. 關鍵概念
-
IdentityResource : OIDC 協議中定義的身份認證資源,這種資源具有唯一名稱,資源聲明後將包含在用戶的身份令牌中。參考: https://docs.identityserver.io/en/3.1.0/topics/resources.html
-
IIdentityServerInteractionService : 這是IdentityServer4提供的認證交互接口。
3. 準備MVC Web客戶端
新建一個MVCClient 項目,設置訪問地址和端口爲:http://localhost:5030。
3.1 引用依賴包
<PackageReference Include="IdentityServer4" Version="4.1.1" />
3.2 註冊OIDC 服務,並啓用認證中間件
// DI容器中註冊服務
services.AddAuthentication(opt =>
{
opt.DefaultScheme = "Cookies";
opt.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5010";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.SaveTokens = true;
});
// ConfigureService 中啓用中間件
app.UseAuthentication();
// 將受保護頁面使用【Authorize】 保護起來,訪問首頁時將重定向到Account/Login
[Authorize]
public class HomeController : Controller
{
…… ……
}
4. 服務端配置
4.1 添加IdentityResource(在server端的Config 類中添加) 和client
public static List<IdentityResource> GetIdentityResources()
{
var result = new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
};
return result;
}
// 新增一個client
new Client
{
ClientId="mvc",
ClientName="MVC Client",
RequireConsent=false,
AllowedGrantTypes= GrantTypes.Implicit, // 隱式授權碼
RedirectUris={"http://localhost:5030/signin-oidc"}, // 這裏的回調地址是MVC客戶端的地址
PostLogoutRedirectUris={"http://localhost:5030/signout-callback-oidc"},
AllowedScopes= new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
// Startup 註冊IdentityResource
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(Config.GetApiResources)
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients)
.AddInMemoryApiScopes(Config.GetApiScopes) // 3.1 新增的坑,不加會報invalid_scope
.AddTestUsers(Config.GetTestUsers())
;
5. 測試驗證
啓動服務端5010,和客戶端5030 此時客戶端將重定向到5010 的登錄頁Account/Login
可以輸入TestUsers中配置的用戶名密碼登錄
5.1 Account/Login 的登錄方法
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
{
ViewData["ReturnUrl"] = returnUrl;
var user = _users.FindByUsername(model.UserName);
if (user == null)
{
ModelState.AddModelError(nameof(model.UserName), "User not exists.");
}
else
{
if (_users.ValidateCredentials(model.UserName, model.Password))
{
var props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30))
};
var u = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username };
await Microsoft.AspNetCore.Http.AuthenticationManagerExtensions.SignInAsync(
this.HttpContext,
u,
props);
return Redirect(returnUrl);
}
}
return View();
}