ASP.NET Core v2.1
配置Authok
获取应用密钥
你需要如下信息
- Domain
- Client ID
- Client Secret
配置回调URL
配置 Logout URL
集成 Authok
安装依赖
Install-Package Microsoft.AspNetCore.Authentication.Cookies
Install-Package Microsoft.AspNetCore.Authentication.OpenIdConnect
安装并配置 OpenID Connect 中间件]
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Cookie configuration for HTTP to support cookies with SameSite=None
services.ConfigureSameSiteNoneCookies();
// Cookie configuration for HTTPS
// services.Configure<CookiePolicyOptions>(options =>
// {
// options.MinimumSameSitePolicy = SameSiteMode.None;
// });
// Add authentication services
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect("Authok", options => {
// Set the authority to your Authok domain
options.Authority = $"https://{Configuration["Authok:Domain"]}";
// Configure the Authok Client ID and Client Secret
options.ClientId = Configuration["Authok:ClientId"];
options.ClientSecret = Configuration["Authok:ClientSecret"];
// Set response type to code
options.ResponseType = OpenIdConnectResponseType.Code;
// Configure the scope
options.Scope.Clear();
options.Scope.Add("openid");
// Set the callback path, so Authok will call back to http://localhost:3000/callback
// Also ensure that you have added the URL as an Allowed Callback URL in your Authok dashboard
options.CallbackPath = new PathString("/callback");
// Configure the Claims Issuer to be Authok
options.ClaimsIssuer = "Authok";
});
// Add framework services.
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
在Startup
类的Configure
方法中添加认证中间件.
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
触发认证
添加登录和退登方法
Controllers/AccountController.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
public class AccountController : Controller
{
public async Task Login(string returnUrl = "/")
{
await HttpContext.ChallengeAsync("Authok", new AuthenticationProperties() { RedirectUri = returnUrl });
}
[Authorize]
public async Task Logout()
{
await HttpContext.SignOutAsync("Authok", new AuthenticationProperties
{
// Indicate here where Authok should redirect the user after a logout.
// Note that the resulting absolute Uri must be added to the
// **Allowed Logout URLs** settings for the app.
RedirectUri = Url.Action("Index", "Home")
});
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
在Startup.cs
文件中,调用AddOpenIdConnect
:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Some code omitted for brevity...
// Add authentication services
services.AddAuthentication(options => {
//...
})
.AddCookie()
.AddOpenIdConnect("Authok", options => {
// ...
options.Events = new OpenIdConnectEvents
{
// handle the logout redirection
OnRedirectToIdentityProviderForSignOut = (context) =>
{
var logoutUri = $"https://{Configuration["Authok:Domain"]}/v1/logout?client_id={Configuration["Authok:ClientId"]}";
var postLogoutUri = context.Properties.RedirectUri;
if (!string.IsNullOrEmpty(postLogoutUri))
{
if (postLogoutUri.StartsWith("/"))
{
// transform to absolute
var request = context.Request;
postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
}
logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
}
context.Response.Redirect(logoutUri);
context.HandleResponse();
return Task.CompletedTask;
}
};
});
}
添加登录和退登按钮
Views/Shared/_Layout.cshtml
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">SampleMvcApp</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp-action="Index">主页</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
@if (User.Identity.IsAuthenticated)
{
<li><a asp-controller="Account" asp-action="Logout">退登</a></li>
}
else
{
<li><a asp-controller="Account" asp-action="Login">登录</a></li>
}
</ul>
</div>
</div>
</div>
运行程序
当用户点击 登录 按钮, OIDC中间件会重定向用户到 Authok 的托管登录页面.
关于登录流程
- 用户点击登录按钮后被重定向到
Login
路由. ChallengeAsync
告知 ASP.NET 认证中间件颁发一个challenge给到认证处理器, 并携带authenticationScheme
参数.- OIDC 处理器重定向用户到 Authok的
/authorize
端点. 用户可以通过用户名密码,社会化或其它身份提供者登录. - 用户登录成功后,Authok会携带授权码回调
/callback
端点. - OIDC处理器拦截
/callback
路径的请求. - OIDC处理器提取授权码, 并且调用
/oauth/token
端点来获取 用户ID 和 Access Token. - OIDC中间件从 ID Token中提取用户信息.
- OIDC中间件返回认证成功响应,并携带认证成功的cookie. cookiez中包含用户信息的声明(claim). 后续 cookie 中间件会对cookie进行认证.
获取用于API调用的 Access Token
如果你想从 MVC应用中调用 API, 需要获取API对应的Access Token. 在访问 Authok 授权端点时传递audience
参数进行获取.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Some code omitted for brevity...
services.AddAuthentication(options => {
//...
})
.AddCookie()
.AddOpenIdConnect("Authok", options => {
// ...
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
// The context's ProtocolMessage can be used to pass along additional query parameters
// to Authok's /authorize endpoint.
//
// Set the audience query parameter to the API identifier to ensure the returned Access Tokens can be used
// to call protected endpoints on the corresponding API.
context.ProtocolMessage.SetParameter("audience", Configuration["Authok:Audience"]);
return Task.FromResult(0);
}
};
});
}
同时要更新应用的appsettings.json
文件.
"Authok": {
...
"Audience": "undefined"
}
存储和获取令牌
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Some code omitted for brevity...
// Add authentication services
services.AddAuthentication(options => {
//...
})
.AddCookie()
.AddOpenIdConnect("Authok", options => {
// ...
// Saves tokens to the AuthenticationProperties
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents
{
// handle the logout redirection
OnRedirectToIdentityProviderForSignOut = (context) =>
{
//...
}
};
});
}
调用 HttpContext.GetTokenAsync 来获取令牌:
if (User.Identity.IsAuthenticated)
{
string accessToken = await HttpContext.GetTokenAsync("access_token");
// if you need to check the Access Token expiration time, use this value
// provided on the authorization response and stored.
// do not attempt to inspect/decode the access token
DateTime accessTokenExpiresAt = DateTime.Parse(
await HttpContext.GetTokenAsync("expires_at"),
CultureInfo.InvariantCulture,
DateTimeStyles.RoundtripKind);
string idToken = await HttpContext.GetTokenAsync("id_token");
// Now you can use them. For more info on when and how to use the
// Access Token and ID Token, see https://docs.authok.cn/tokens
}