Skip to main content

快速开始/常规WEB应用/ASP.NET Core v2.1/

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 的托管登录页面.

关于登录流程

  1. 用户点击登录按钮后被重定向到Login路由.
  2. ChallengeAsync告知 ASP.NET 认证中间件颁发一个challenge给到认证处理器, 并携带authenticationScheme参数.
  3. OIDC 处理器重定向用户到 Authok的 /authorize端点. 用户可以通过用户名密码,社会化或其它身份提供者登录.
  4. 用户登录成功后,Authok会携带授权码回调 /callback端点.
  5. OIDC处理器拦截 /callback路径的请求.
  6. OIDC处理器提取授权码, 并且调用 /oauth/token端点来获取 用户ID 和 Access Token.
  7. OIDC中间件从 ID Token中提取用户信息.
  8. 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
}