開發環境:
vs2017 、net Core 2.1、sqlserver2012
一。搭建IdentityServer4服務端
打開VS2017,新建 netcore項目:
名字叫:IdentityS4, 然后選擇webMVC這個,如下圖:

引進安裝依賴項:IdentityServer4

設置該項目的地址為:http://localhost:5000

新建一個配置文件類:Config.cs 代碼如下:
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
}
添加一個Admin.cs的實體類,對應數據庫里面的用戶表Admin (然后把這個實體類添加到上一步的EFContext中,也就是上一步代碼的紅色部分)
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; }
}
在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=.;database=blogcore;uid=sa;pwd=123" },
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
至此,應該是可以正確的連接數據庫的,大家可以去Home控制器中查詢點數據測試下顯示到首頁,保證能連接數據庫成功的查詢出數據就可以。
接下來 我們來做登錄頁面
新增一個控制器AccountController,代碼如下:
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服務端的工作完成,接下來我們要開始建客戶端了,也就是需要保護的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認證,這樣就達到了單點登錄統一認證的效果了!
源碼下載:https://pan.baidu.com/s/1HRiAF7LMinCh03AZNArfcQ

