Blazor(服务器端)和 Okta
Blazor (Server Side) and Okta
我目前正在通过此 post 将 okta 集成到 Blazor 服务器端应用程序中
https://developer.okta.com/blog/2019/10/16/csharp-blazor-authentication
我目前收到“抱歉,此地址没有任何内容”。
我希望有人能对我的问题提出建议。
或者有人知道将 okta 集成到 Blazor 服务器端应用程序中的示例吗?
请告诉我。
如有任何帮助,我们将不胜感激。我完全在旋转我的轮子。
这是我的 okta 常规设置:
下面是我的解决方案的代码(我检查了 post 很多次以确保它是正确的。但我想我可能错过了一些东西。)
启动配置服务
services.AddAuthorizationCore();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ClientId = Configuration["Okta:ClientId"];
options.ClientSecret = Configuration["Okta:ClientSecret"];
options.Authority = Configuration["Okta:Issuer"];
options.CallbackPath = "/authorization-code/callback";
options.ResponseType = "code";
options.SaveTokens = true;
options.UseTokenLifetime = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
};
});
}
启动配置
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
MainLayout.razor
<div class="top-row px-4">
<AuthorizeView>
<Authorized>
<a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
<a href="Identity/Account/LogOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>
<a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
</div>
App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState> ```
点击"Log in"锚元素时,路由器无法找到"Identity/Account/Login"路径,结果显示
抱歉,该地址没有任何内容.
.当然你没有被定向到 okta 网站,也没有进行身份验证,对吧?
我无法理解她为什么要这样设置 AuthorizeView 组件。不过,在我看来,她的整篇文章都是由市政委员会设计的,并由备用代码片段组装而成。但是,这超出了这个答案的范围。
解决方案
创建一个名为 LoginDisplay (LoginDisplay.razor) 的组件,并将其放在 Shared 文件夹中。 MainLayout组件中使用了该组件
<AuthorizeView>
<Authorized>
<a href="logout">Hello, @context.User.Identity.Name!</a>
<form method="get" action="logout">
<button type="submit" class="nav-link btn btn-link">Log out</button>
</form>
</Authorized>
<NotAuthorized>
<a href="login?redirectUri=/">Log in</a>
</NotAuthorized>
</AuthorizeView>
将 LoginDisplay 组件添加到 MainLayout 组件,就在关于上面
锚元素,像这样
<div class="top-row px-4">
<LoginDisplay />
<a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>
注意:为了将登录和注销请求重定向到 okta,我们必须创建两个 Razor 页面,如下所示:
1. 创建一个 Login Razor 页面 Login.cshtml (Login.cshtml.cs) 并将它们放在 Pages 文件夹中,如下所示:
Login.cshtml.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.IdentityModel.Tokens;
public class LoginModel : PageModel
{
public async Task OnGet(string redirectUri)
{
await
HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = redirectUri
});
}
}
此代码开始挑战您在启动 class 中定义的 Open Id Connect 身份验证方案。
- 创建一个注销 Razor 页面 Logout.cshtml (Logout.cshtml.cs) 并将它们也放在页面文件夹中:
Logout.cshtml.cs
using Microsoft.AspNetCore.Authentication;
public class LogoutModel : PageModel
{
public async Task<IActionResult> OnGetAsync()
{
await HttpContext.SignOutAsync();
return Redirect("/");
}
}
此代码将您注销,将您重定向到 Blazor 应用的主页。
将App.razor中的代码替换为以下代码:
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@*<RedirectToLogin />*@
</NotAuthorized>
<Authorizing>
Wait...
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
运行您的应用,点击登录按钮进行身份验证
注意:我的回答还没有完成...我还有更多要补充的,但我现在做不到。我会尽快做的。如果您有任何疑问,请随时提问。如果可以的话,我会回答他们...;)
注意:这个示例非常适合我。
请不要忘记您的 appsettings.json 文件。它应该是这样的:
{
"Okta": {
"Issuer": "https://dev-621531.okta.com/oauth2/default",
"ClientId": "0o1a5bsivg6wFDw6Jr347",
"ClientSecret": "ffolkG3sd2NgQ_E909etXRU3cXX3wBpgE0XxcmF5"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
不要尝试使用我的设置,因为我已经更改了它们...
Startup class 中的代码是正确的,但您可能需要添加一些命名空间引用,也许是我添加的 nuget 包等。
注意:当您在试验您的应用时,如果您想被重定向到 okta 的登录页面,您应该清除浏览数据,否则,您的浏览器可能会使用缓存数据。
更新
请注意,按照此处创建的登录机制不会使您的应用程序比以前更安全。任何用户都无需登录即可访问您的网络资源。为了保护网站的某些部分,您还必须实施授权,通常情况下,经过身份验证的用户被授权访问受保护的资源,除非实施了其他措施,例如角色、策略等。以下是如何进行的演示您可以保护您的 Fetchdata 页面免受未经授权的用户访问(同样,经过身份验证的用户被视为有权访问 Fetchdata 页面)。
- 在 Fetchdata 组件页面的顶部,为 Authorize 属性添加 @attribute 指令,如下所示:
@attribute [Authorize]
当未经身份验证的用户尝试访问 Fetchdata 页面时,将执行 AuthorizeRouteView.NotAuthorized
委托 属性,因此我们可以添加一些代码将用户重定向到同一个 okta 的登录页面以进行身份验证。
将此代码放在 NotAuthorized 元素中,如下所示:
<NotAuthorized>
@{
var returnUrl =
NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
NavigationManager.NavigateTo($"login?redirectUri=
{returnUrl}", forceLoad: true);
}
</NotAuthorized>
这会检索您尝试访问的最后一个页面的 url,即 Fetchdata 页面,然后导航到执行密码挑战的 Login Razor 页面,即用户被重定向到okta 的登录页面进行身份验证。
用户通过身份验证后,他将被重定向到 Fetchdata 页面。
最后但同样重要的是,在 Fetchdata 页面的顶部添加以下内容:
@page "/authorization-code/fetchdata"
请注意,Fetchdata 组件已经有一个@page 指令,这是第二个。多个路由模板在 Blazor 中是合法的......但我为什么要添加它?这是因为从 okta 传递到我们应用程序的 return url 是 "/authorization-code/fetchdata"
,而不是我们可能预期的“/fetchdata”,因为 returnUrl我们从导航管理器中提取出来的是"fetchdata"。我不确定我是否理解“/authorization-code/”的作用,以及为什么它对我们强制执行。我真的没有时间去调查。但是,这个 url 段是 okta 所必需的,任何没有它的尝试都失败了,无论是在我的代码中还是在 okta 网站的仪表板中。我可以在代码中去掉这个 url 段,但这不是问题。我只是想知道为什么我们需要提供它,以便我可以更好地处理它。如果你碰巧了解更多,请在这里报告,因为我不能花更多的时间调查...
顺便说一下,文章中提供的解决方案并不完整;我敢说,相当偏颇。
祝你好运...
我目前正在通过此 post 将 okta 集成到 Blazor 服务器端应用程序中
https://developer.okta.com/blog/2019/10/16/csharp-blazor-authentication
我目前收到“抱歉,此地址没有任何内容”。
我希望有人能对我的问题提出建议。
或者有人知道将 okta 集成到 Blazor 服务器端应用程序中的示例吗?
请告诉我。
如有任何帮助,我们将不胜感激。我完全在旋转我的轮子。
这是我的 okta 常规设置:
启动配置服务
services.AddAuthorizationCore();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ClientId = Configuration["Okta:ClientId"];
options.ClientSecret = Configuration["Okta:ClientSecret"];
options.Authority = Configuration["Okta:Issuer"];
options.CallbackPath = "/authorization-code/callback";
options.ResponseType = "code";
options.SaveTokens = true;
options.UseTokenLifetime = false;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("openid");
options.Scope.Add("profile");
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name"
};
});
}
启动配置
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
MainLayout.razor
<div class="top-row px-4">
<AuthorizeView>
<Authorized>
<a href="Identity/Account/Manage">Hello, @context.User.Identity.Name!</a>
<a href="Identity/Account/LogOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>
<a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
</div>
App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState> ```
点击"Log in"锚元素时,路由器无法找到"Identity/Account/Login"路径,结果显示
抱歉,该地址没有任何内容.
.当然你没有被定向到 okta 网站,也没有进行身份验证,对吧?我无法理解她为什么要这样设置 AuthorizeView 组件。不过,在我看来,她的整篇文章都是由市政委员会设计的,并由备用代码片段组装而成。但是,这超出了这个答案的范围。
解决方案
创建一个名为 LoginDisplay (LoginDisplay.razor) 的组件,并将其放在 Shared 文件夹中。 MainLayout组件中使用了该组件
<AuthorizeView> <Authorized> <a href="logout">Hello, @context.User.Identity.Name!</a> <form method="get" action="logout"> <button type="submit" class="nav-link btn btn-link">Log out</button> </form> </Authorized> <NotAuthorized> <a href="login?redirectUri=/">Log in</a> </NotAuthorized> </AuthorizeView>
将 LoginDisplay 组件添加到 MainLayout 组件,就在关于上面 锚元素,像这样
<div class="top-row px-4"> <LoginDisplay /> <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a> </div>
注意:为了将登录和注销请求重定向到 okta,我们必须创建两个 Razor 页面,如下所示: 1. 创建一个 Login Razor 页面 Login.cshtml (Login.cshtml.cs) 并将它们放在 Pages 文件夹中,如下所示:
Login.cshtml.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.IdentityModel.Tokens;
public class LoginModel : PageModel
{
public async Task OnGet(string redirectUri)
{
await
HttpContext.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = redirectUri
});
}
}
此代码开始挑战您在启动 class 中定义的 Open Id Connect 身份验证方案。
- 创建一个注销 Razor 页面 Logout.cshtml (Logout.cshtml.cs) 并将它们也放在页面文件夹中:
Logout.cshtml.cs
using Microsoft.AspNetCore.Authentication;
public class LogoutModel : PageModel
{
public async Task<IActionResult> OnGetAsync()
{
await HttpContext.SignOutAsync();
return Redirect("/");
}
}
此代码将您注销,将您重定向到 Blazor 应用的主页。
将App.razor中的代码替换为以下代码:
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
@*<RedirectToLogin />*@
</NotAuthorized>
<Authorizing>
Wait...
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
运行您的应用,点击登录按钮进行身份验证
注意:我的回答还没有完成...我还有更多要补充的,但我现在做不到。我会尽快做的。如果您有任何疑问,请随时提问。如果可以的话,我会回答他们...;)
注意:这个示例非常适合我。
请不要忘记您的 appsettings.json 文件。它应该是这样的:
{
"Okta": {
"Issuer": "https://dev-621531.okta.com/oauth2/default",
"ClientId": "0o1a5bsivg6wFDw6Jr347",
"ClientSecret": "ffolkG3sd2NgQ_E909etXRU3cXX3wBpgE0XxcmF5"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
不要尝试使用我的设置,因为我已经更改了它们... Startup class 中的代码是正确的,但您可能需要添加一些命名空间引用,也许是我添加的 nuget 包等。
注意:当您在试验您的应用时,如果您想被重定向到 okta 的登录页面,您应该清除浏览数据,否则,您的浏览器可能会使用缓存数据。
更新 请注意,按照此处创建的登录机制不会使您的应用程序比以前更安全。任何用户都无需登录即可访问您的网络资源。为了保护网站的某些部分,您还必须实施授权,通常情况下,经过身份验证的用户被授权访问受保护的资源,除非实施了其他措施,例如角色、策略等。以下是如何进行的演示您可以保护您的 Fetchdata 页面免受未经授权的用户访问(同样,经过身份验证的用户被视为有权访问 Fetchdata 页面)。
- 在 Fetchdata 组件页面的顶部,为 Authorize 属性添加 @attribute 指令,如下所示:
@attribute [Authorize]
当未经身份验证的用户尝试访问 Fetchdata 页面时,将执行AuthorizeRouteView.NotAuthorized
委托 属性,因此我们可以添加一些代码将用户重定向到同一个 okta 的登录页面以进行身份验证。 将此代码放在 NotAuthorized 元素中,如下所示:
<NotAuthorized> @{ var returnUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); NavigationManager.NavigateTo($"login?redirectUri= {returnUrl}", forceLoad: true); } </NotAuthorized>
这会检索您尝试访问的最后一个页面的 url,即 Fetchdata 页面,然后导航到执行密码挑战的 Login Razor 页面,即用户被重定向到okta 的登录页面进行身份验证。
用户通过身份验证后,他将被重定向到 Fetchdata 页面。
最后但同样重要的是,在 Fetchdata 页面的顶部添加以下内容:
@page "/authorization-code/fetchdata"
请注意,Fetchdata 组件已经有一个@page 指令,这是第二个。多个路由模板在 Blazor 中是合法的......但我为什么要添加它?这是因为从 okta 传递到我们应用程序的 return url 是 "/authorization-code/fetchdata"
,而不是我们可能预期的“/fetchdata”,因为 returnUrl我们从导航管理器中提取出来的是"fetchdata"。我不确定我是否理解“/authorization-code/”的作用,以及为什么它对我们强制执行。我真的没有时间去调查。但是,这个 url 段是 okta 所必需的,任何没有它的尝试都失败了,无论是在我的代码中还是在 okta 网站的仪表板中。我可以在代码中去掉这个 url 段,但这不是问题。我只是想知道为什么我们需要提供它,以便我可以更好地处理它。如果你碰巧了解更多,请在这里报告,因为我不能花更多的时间调查...
顺便说一下,文章中提供的解决方案并不完整;我敢说,相当偏颇。
祝你好运...