CAS
是一个企业级的开源项目,其github地址为:https://github.com/apereo/cas,其Net客户端的git地址为:https://github.com/apereo/dotnet-cas-client,因为本篇内容的目标是已有系统快速接入CAS
,所以CAS服务端
的部署等不在本篇内容范围内。
首先需要说明的是,CAS
的Net客户端目前并不支持Net Core
,但看项目Issues
已经有Core
客户端的相关计划。
要接入CAS
,你可以通过nuget下载DotNetCasClient
,其地址为:https://www.nuget.org/packages/DotNetCasClient,安装完毕后,CAS
需要用到的配置,会被自动地添加在web.config
中,然后你要做的,就是按照这篇文章的内容,进行相应配置的修改,需要注意的是casClientConfig.proxyTicketManager
,如果无需代理时,请删除该配置,否则CAS服务端
响应结果可能会为UNAUTHORIZED_SERVICE_PROXY
<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationFailure code='UNAUTHORIZED_SERVICE_PROXY'>
The supplied service 'http://localhost:11518/Restricted/AuthenticatedUsersOnly/default.aspx' is not authorized to use CAS proxy authentication.
</cas:authenticationFailure>
</cas:serviceResponse>
上面的内容是我用DotNetCasClient
解决方案示例项目ExampleWebSite
试运行时服务端响应的内容,为保证配置正确,你可以在MVC项目之前,先通过ExampleWebSite
进行配置测试,顺便再吐槽下这个示例项目居然是建的网站!
我们可以通过CasAuthenticationTicket
来判断系统能获取到CAS
的登录信息,根据ExampleWebSite
下的CookieViewer.ascx
,我简单的写了个Helper类,用于获取CasAuthenticationTicket
public static class CasClientHelper
{
public static CasAuthenticationTicket GetCasAuthenticationTicket(this HttpRequestBase request)
{
CasAuthenticationTicket casTicket = null;
var ticketCookie = request.Cookies[FormsAuthentication.FormsCookieName];
if (ticketCookie != null && !string.IsNullOrWhiteSpace(ticketCookie.Value))
{
var ticket = FormsAuthentication.Decrypt(ticketCookie.Value);
if (ticket != null && CasAuthentication.ServiceTicketManager != null)
{
casTicket = CasAuthentication.ServiceTicketManager.GetTicket(ticket.UserData);
}
}
return casTicket;
}
}
下面就是最常见的AuthorizeAttribute
,我在下面简单的写了个示例代码CasAuthorizeAttribute
,具体说明都通过代码注释的方式写在了代码中
using DotNetCasClient;
using DotNetCasClient.Utils;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
public class CasAuthorizeAttribute : AuthorizeAttribute
{
int _loginStatus;//此处简单示例:0未登录 1已登录 2状态错误 3无权限
public override void OnAuthorization(AuthorizationContext filterContext)
{
var casTicket = filterContext.HttpContext.Request.GetCasAuthenticationTicket();
//因为除了用户是否登录外,还需要判断权限,所以一般相关判断都在此处执行
if (casTicket != null && casTicket.Assertion != null
&& !string.IsNullOrWhiteSpace(casTicket.Assertion.PrincipalName))
{
//这里假设PrincipalName与本地系统的用户唯一Id进行匹配
var identity = filterContext.RequestContext.HttpContext.User.Identity;
//可能HttpContext.User会包含多个Identity,与你使用的登录实现有关
//所以下面的判断方法需按实际情况进行调整
if (!identity.IsAuthenticated || !identity.Name.Equals(casTicket.Assertion.PrincipalName, StringComparison.OrdinalIgnoreCase))
{
//SSOLogin,本地根据用户唯一Id进行登录
//即根据casTicket.Assertion.PrincipalName进行用户登录
//根据登录结果,将结果赋值给_loginStatus
}
else
{
//唯一Id相同,且当前系统已登录,则认为用户是登录状态
this._loginStatus = 1;
}
if (this._loginStatus == 1)
{
//进行权限判定
//如果无权限,则将_loginStatus赋值此处举例用的3
//this._loginStatus = 3;
}
}
base.OnAuthorization(filterContext);//执行了基类的OnAuthorization才会执行AuthorizeCore
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//正常这里应该是OnAuthorization数据准备后,相应判断依据参数通过全局变量传递
//例如这里的_loginStatus
return this._loginStatus == 1;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//AuthorizeCore返回false时,会执行此部分代码
//此时同样可根据OnAuthorization准备的数据进行判断
//根据判断结果不同,可跳转至不同的页面
//如果是未登陆,则跳至Cas的单点登录服务地址
//如果是本地系统判断无用户,或用户状态不正确,那应该跳至相应的无用户页面
//如果是无权限,因为在已有系统接入时,不太可能再接入Cas的角色权限部分
//所以大概率会采用系统原有权限,所以此时理论应该上应该跳至本地的无权限显示页面
base.HandleUnauthorizedRequest(filterContext);
switch (this._loginStatus)
{
case 2://无用户
filterContext.Result = new RedirectResult("~/Account/NoUser");
break;
case 3://无权限
filterContext.Result = new RedirectResult("~/Account/NotAuthorized");
break;
default://未登录
var redirectUrl = UrlUtil.ConstructLoginRedirectUrl(false, true);
filterContext.Result = new RedirectResult(redirectUrl);
//如果实际发现并没跳转至Cas登录页面,那么你可以尝试下面的方法,指定true强制结束当前响应并跳转
//HttpContext.Current.Response.Redirect(redirectUrl, true);
break;
}
}
}
然后用CasAuthorizeAttribute
替换你项目中原来的AuthorizeAttribute
即可。
最后,如果以上内容还帮助不到你,你可以尝试看下CasAuthenticationModule
源码部分,你会发现CasAuthentication
本身提供了各种跳转方式,另外自定义https
也无需通过修改源码的方式解决,你只需要在Global
的Application_Start
中增加如下代码即可
System.Net.ServicePointManager.ServerCertificateValidationCallback
+= (s, x, c, e) => true;