CAS的实现

旧版本的CAS实现思路

核心原理(CAS 1.0 协议)

  1. 用户访问受保护页面 /admin/admin.aspx
  2. web.config 权限配置拦截匿名用户,自动跳转登录页 CasEnter.aspx
  3. CasEnter.aspx 判断URL有无 ticket 参数
  • 无ticket:重定向到CAS服务端登录地址,并传入当前站点地址 service
  • 携带ticket:调用 serviceValidate接口向CAS服务器校验票据
  1. CAS返回XML,解析出登录用户名
  2. 校验成功后调用 FormsAuthentication.SetAuthCookie() 写入本地Forms登录票据,跳转目标页面
  3. 后续访问受保护目录,由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;
        }

        // 校验ticket
        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)

Previous:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注