大多公司是採用微軟企業方案,可以由Azure Portal管理Office、Outlook、群組(Group)等權限和功能。維護舊專案(.NET Framework 4.7.2)時,想加上SSO(Single sign-on)在網頁上,可是網路上大多是寫給.NET Core的教學,雖然官方也有提供.NET Framework的教學,但新增專案時選擇Azure驗證可以得到比較漂亮的寫法,故特此筆記。
一、新增OWIN中介軟體Nuget套件
Install-Package Microsoft.Owin.Security.OpenIdConnect
Install-Package Microsoft.Owin.Security.Cookies
Install-Package Microsoft.Owin.Host.SystemWeb
二、新增Startup.cs
與ASP.NET Core類似,在Startup.cs設定中介軟體的操作,在專案新增「OWIN啟動類別」,命名為"Startup.cs"。
然後,編輯Startup類別。
Startup.cs:
using System;
using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
[assembly: OwinStartup(typeof(DemoProject.Startup))
namespace DemoProject
{
public partial class Startup
{
private static string clientId = System.Configuration.ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = EnsureTrailingSlash(System.Configuration.ConfigurationManager.AppSettings["ida:AADInstance"]);
private static string tenantId = System.Configuration.ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = System.Configuration.ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, aadInstance, tenantId);
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri
});
}
private static string EnsureTrailingSlash(string value)
{
if (value == null)
{
value = string.Empty;
}
if (!value.EndsWith("/", StringComparison.Ordinal))
{
return value + "/";
}
return value;
}
}
}
三、Azure AD | App registrations
從程式碼可以看出來我們需要4個參數,他們都是從Azure AD取得的,所以再來我們會先到Azure Portal註冊新的程式,然後在『Redirect URIs』裡增加Web的Redirect URI,也就是你的網址。這邊我用https://localhost:44388示範。
記得要勾選"ID tokens"。
四、Web.config設定
在Azure AD設定好之後,回到專案,把相對應參數寫入appSettings裡。
Web.config:
<appSettings>
...
<add key="ida:ClientId" value="Enter_the_Application(client)_ID_here" />
<add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}/" />
<add key="ida:TenantId" value="Enter_the_Directory(tenant)_ID_here" />
<add key="ida:PostLogoutRedirectUri" value="https://localhost:44388/" />
</appSettings>
五、Authorize Attribute
有了這些設定我們就已經可以在要管制的Controller、Action上加上[Authorize] attribute,這樣就會強制你要登入了,很神奇吧XD
[Authorize]
public class HomeController : BaseController
{
// Code here.
}
偵錯F5看看,可以看到網頁會直接先導頁到Azure Login的頁面~
六、登入、登出功能
當然,有了SSO,不可能不讓用戶登出吧,所以我們會增加AccountController來處理這些事。
AccountController.cs:
using System.Web;
using System.Web.Mvc;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
namespace DemoProject.Controllers
{
public class AccountController : Controller
{
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
public void SignOut()
{
string callbackUrl = Url.Action("SignOutCallback", "Account", routeValues: null, protocol: Request.Url.Scheme);
HttpContext.GetOwinContext().Authentication.SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
OpenIdConnectAuthenticationDefaults.AuthenticationType,
CookieAuthenticationDefaults.AuthenticationType);
}
public ActionResult SignOutCallback()
{
if (Request.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
return View();
}
}
}
可以看到SignIn()就是拿OpenIdConnectAuthenticationDefaults.AuthenticationType=OpenIdConnect
來Challenge執行驗證登入,而我們是只用[Authorize]
attribute就搞定這一連串動作。
SignOut()其實只是把Challenge改成SignOut,但這邊我們增加一個方法SignOutCallback(),讓我們執行完登出的時候呼叫他,來跳轉(Redirect)到登出頁。
在_Layout這邊我們可以加入_LoginPartial。
_LoginPartial:
@if (Request.IsAuthenticated)
{
<text>
<ul class="nav navbar-nav navbar-right">
<li class="navbar-text">
Hello, @User.Identity.Name!
</li>
<li>
@Html.ActionLink("Sign out", "SignOut", "Account")
</li>
</ul>
</text>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Sign in", "SignIn", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}
畫面會長這樣~
總結
從微軟預設的驗證程式可以學到蠻多思維的,在嫁接這些寫法的時候也採了很多坑,尤其是Azure Redirect URIs那邊。再來應該會再玩玩看.NET 6,還有就是Group Policy的實作,針對不同API或網頁會需要不同的權限,應該會記錄在下集ㄎㄎ。
沒有留言:
張貼留言