舊版 CAS 的實作思路
核心原理(CAS 1.0 協定)
- 使用者存取受保護頁面
/admin/admin.aspx web.config權限設定攔截匿名使用者,自動跳轉至登入頁CasEnter.aspxCasEnter.aspx判斷網址是否帶有ticket參數
- 沒有 ticket:重新導向到 CAS 伺服器登入位址,並傳入目前網站位址做為
service - 攜帶 ticket:呼叫
serviceValidate介面向 CAS 伺服器驗證票證
- CAS 返回 XML,解析出登入使用者名稱
- 驗證成功後呼叫
FormsAuthentication.SetAuthCookie()寫入本機 Forms 登入票證,跳轉至目標頁面 - 後續存取受保護目錄,由 ASP.NET Forms 辨識登入狀態
web.config 設定
<system.web>
<authentication mode="Forms">
<!-- loginUrl 指向 CasEnter.aspx 做為 CAS 入口轉頁 -->
<forms loginUrl="CasEnter.aspx"
defaultUrl="admin/admin.aspx"
name=".LoginFormsTicket"
path="/"
timeout="40"
protection="All">
</forms>
</authentication>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
<!-- 限制 admin 目錄禁止匿名存取 -->
<location path="admin">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>Code language: HTML, XML (xml)
新版 CAS 伺服器預設強制使用 HTTPS,
casUrl務必填寫https://127.0.0.1:8443/cas/
<appSettings>
<!-- CAS7 新版位址,結尾必須加上斜線 -->
<add key="casUrl" value="https://127.0.0.1:8443/cas/"/>
</appSettings>Code language: HTML, XML (xml)
CasEnter.aspx 頁面程式碼
1. CasEnter.aspx 前端標籤
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CasEnter.aspx.cs" Inherits="CasStudy.Web.CasEnter" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>CAS 轉接登入</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server"></asp:Label><br />
<asp:HyperLink ID="HyperLink1" runat="server">重新驗證</asp:HyperLink>
</div>
</form>
</body>
</html>Code language: HTML, XML (xml)
2. CasEnter.aspx.cs 後端程式
介面升級為 CAS3.0 p3/serviceValidate
以 HttpClient 取代已淘汰的 WebClient
加入自簽憑證相容性(本機測試新版 CAS 必備)
處理 CAS 返回 XML 的命名空間
using System;
using System.Configuration;
using System.Net;
using System.Net.Http;
using System.Xml;
using System.Web.Security;
public partial class CasEnter : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string casHost = ConfigurationManager.AppSettings["casUrl"];
string ticket = Request.QueryString["ticket"];
string service = Request.Url.GetLeftPart(UriPartial.Path);
// 沒有票證就跳轉 CAS 登入頁
if (string.IsNullOrEmpty(ticket))
{
string redirectUrl = $"{casHost}login?service={Uri.EscapeDataString(service)}";
Response.Redirect(redirectUrl);
return;
}
// 驗證票證
string username = ValidateTicket(casHost, ticket, service);
if (string.IsNullOrEmpty(username))
{
Label1.Text = "很抱歉!CAS 驗證失敗,請重新嘗試!";
HyperLink1.NavigateUrl = Request.Url.AbsolutePath;
}
else
{
// 寫入 Forms 驗證票證,完成本機登入
FormsAuthentication.SetAuthCookie(username, false);
// 跳轉至目標頁面
Response.Redirect(FormsAuthentication.DefaultUrl);
}
}
/// <summary>
/// CAS3.0 p3/serviceValidate 票證驗證
/// </summary>
private string ValidateTicket(string casHost, string ticket, string service)
{
// 新版 CAS 建議使用 p3/serviceValidate(CAS3.0 協定)
string validateUrl = $"{casHost}p3/serviceValidate?ticket={Uri.EscapeDataString(ticket)}&service={Uri.EscapeDataString(service)}";
// 僅本機測試:允許不受信任的SSL憑證(正式環境務必刪除此段!)
ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, err) => true;
using var httpClient = new HttpClient();
string xml = httpClient.GetStringAsync(validateUrl).Result;
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
// CAS3 返回 XML 的命名空間
nsMgr.AddNamespace("cas", "http://www.yale.edu/tp/cas");
// 尋找使用者節點
XmlNode userNode = doc.SelectSingleNode("//cas:authenticationSuccess/cas:user", nsMgr);
return userNode?.InnerText;
}
}Code language: HTML, XML (xml)
CAS 的實作
Previous: 安裝