CAS 구현

구버전 CAS 구현 아이디어

핵심 원리 (CAS 1.0 프로토콜)

  1. 사용자가 보호된 페이지 /admin/admin.aspx에 접속
  2. web.config 권한 설정이 익명 사용자를 차단하고 로그인 페이지 CasEnter.aspx로 자동 리다이렉트
  3. CasEnter.aspx에서 URL에 ticket 쿼리 파라미터 존재 여부 확인
  • ticket 없음: CAS 서버 로그인 주소로 리다이렉트, 현재 사이트 주소를 service로 전달
  • ticket 포함: serviceValidate API를 호출해 CAS 서버에서 티켓 검증 수행
  1. CAS가 반환한 XML에서 로그인 사용자 아이디 파싱
  2. 검증 성공 시 FormsAuthentication.SetAuthCookie()를 호출해 로컬 Forms 인증 티켓을 생성하고 대상 페이지로 이동
  3. 이후 보호 디렉터리 접근은 ASP.NET Forms가 로그인 상태를 인식하여 처리

web.config 설정

<system.web>
  <authentication mode="Forms">
    <!-- loginUrl은 CAS 진입 중계 페이지인 CasEnter.aspx로 지정 -->
    <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 코드비하인드


API를 CAS3.0 p3/serviceValidate로 업그레이드
사용이 중단된 WebClient 대신 HttpClient 적용

자체 서명 인증서 호환 기능 추가(최신 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:

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다