单点接入方案总结
Posted yuxiaoxu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单点接入方案总结相关的知识,希望对你有一定的参考价值。
单点登录首先获取统一门户的登陆凭据,然后通过验证取得登陆名,最后对登陆名在接入系统中进行验证并创建会话。也有一种是约定的方式,比如根据几组串包括时间戳、密钥等进行加密,然后用约定的方式解密获得数据。老式的单点通过域名方式将令牌存储在Cookie中,该方式不支持IP方式或者跨域,有一定局限。新式的单点运用重定向技术,由门户系统自动附加到应用系统接入接口,不受IP或者协议本身限制。
下面对几种常见的单点方式进行总结。
CAS登陆
CAS登陆包含登陆地址、验证地址以及退出地址。接入的应用系统首先需要登记通过参数service识别。
示例代码用C#编写,其他语言均可以改写。底层原理都可以使用抓包的方式分析。
没有会话的时候,就去访问CAS登陆地址进行登陆,通过参数service。验证成功后,CAS会回调service地址并将ticket参数提供给应用。通过获取的ticket以及service进行去验证,验证成功返回响应的XML包含user即登陆名。
一般情况下CAS的地址形如:
登陆:……/cas目录/login
验证:……/cas目录/proxyValidate
注销:……/cas目录/logout
public string getHost()
{
Boolean https=Request.ServerVariables["HTTPS"].ToLower().Equals("on");
String Host=Request.ServerVariables["HTTP_HOST"];
return (https ? "https://" : "http://") + Host;
}
public String getEmployeeID()
{
string loginaspx =HttpUtility.UrlEncode(getHost()+Request.ServerVariables["PATH_INFO"]) ;
string ticket = Request.QueryString["ticket"];
if (ticket == null || ticket.Length == 0)
{
Response.Redirect(loginServer + "?service=" + loginaspx);
return null;
}
string validateUrl = validateServer + "?ticket=" + ticket + "&service=" + loginaspx;
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
//验证返回的ticket,获取服务器返回的用户名
try
{
StreamReader Reader = new StreamReader(new WebClient().OpenRead(validateUrl));
string resp = Reader.ReadToEnd();
NameTable nt = new NameTable();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlTextReader reader = new XmlTextReader(resp, XmlNodeType.Element, context);
string uid = null;
while (reader.Read())
{
if (reader.IsStartElement())
{
string tag = reader.LocalName;
if (tag == "user")
uid = reader.ReadString();
}
}
reader.Close();
return uid;
}
catch (Exception ee)
{
return null;
}
}
OAuth登陆
OAuth登陆方式,门户会提供相关接口,一般会提供约定的appKey、appPassword,通过响应JSON来获取信息。与CAS类似,第一步仍然需要通过接口获取accessToken,然后验证accessToken获取登陆名。
下面是C#代码示例,同时使用一个获取门户在线用户的方法,这样可以避免每次重复去登陆验证。
public class JsonObjectAccessToken
{
public int type;
public string access_token;
}
public class JsonObjectSession
{
public string status;
public string desc;
public Dictionary<string, string> obj;
}
public String getEmployeeID()
{
String appKey = configNode.Attributes["appKey"].Value;
String appPassword = configNode.Attributes["appPassword"].Value;
String serverName = configNode.Attributes["serverName"].Value; //本地登录入口
string redirectUrl=HostName+Request.ServerVariables["PATH_INFO"]; string ticket = Request.QueryString["code"];
if (ticket == null || ticket.Length == 0)
{
//如果门户已登陆直接返回登陆信息
String loginUserResponse = UserInfo.Text.Replace("callback(", "").Replace(")", "");
if (loginUserResponse != null && loginUserResponse.Equals("") == false)
{
JsonObjectSession loginUserObject = JsonConvert.DeserializeObject<JsonObjectSession>(loginUserResponse);
if (loginUserObject != null && loginUserObject.status.Equals("1"))
{
return loginUserObject.obj["LoginName"];
}
}
if (Session["ticket"] != null)
{
ticket = Session["ticket"].ToString();
}
if (ticket == null || ticket.Length == 0)
{
Response.Redirect(serverName + "/OAuth2/OAuth?client_id=" + appKey + "&redirect_uri=" + redirectUrl + "&forcelogin=false&state=STATE");
return null;
}
}
Session["ticket"] = ticket;
string validateUrl = serverName + "/OAuth2/access_token?client_id=" + appKey + "&client_secret="+appPassword+"&redirect_uri=" + redirectUrl+"&code="+ticket;
//验证返回的ticket,获取服务器返回的用户名
try
{
String accessTokenResponse = getResponse(validateUrl);
JsonObjectAccessToken accessTokenObject = JsonConvert.DeserializeObject<JsonObjectAccessToken>(accessTokenResponse);
if (accessTokenObject != null)
{
if (accessTokenObject.type == 1)
{
String accessToken = accessTokenObject.access_token;
//请求用户信息
String sessionUrl = serverName + "/SSOService.asmx/user_base_info?access_token=" + accessToken;
String sessionResponse = getResponse(sessionUrl).Replace("(","").Replace(")","");
JsonObjectSession sessionObject = JsonConvert.DeserializeObject<JsonObjectSession>(sessionResponse);
if (sessionObject.status.Equals("1"))
{
return sessionObject.obj["UserLoginName"];
}
}
if (Session["ticket"] != null) Session.Remove("ticket");
}
}
catch (Exception e)
{
Response.Write(e.StackTrace);
}
return null;
}
public static String getTimestamp()
{
System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0));
long t = (DateTime.Now.Ticks - startTime.Ticks) / 10000;
return t.ToString();
}
public string getResponse(String url)
{
Stream stream = null;
StreamReader Reader = null;
string responseMsg = "";
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url); request.Method = "GET"; //以下可以自定义请求头部
request.ContentType = "text/json;charset=utf-8";
request.Timeout = 1000;
request.Referer = HostName;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
stream = response.GetResponseStream();
Reader = new StreamReader(stream, System.Text.Encoding.GetEncoding("utf-8"));//自行进行编码转换
responseMsg = Reader.ReadToEnd(); //这里决定了 肯定不会出现EndofStream异常。
}
catch
{
}
finally
{
if (Reader != null)
{
Reader.Close();
Reader.Dispose();
}
Reader = null;
if (stream != null)
{
stream.Close();
stream.Dispose();
}
stream = null;
}
return responseMsg;
}
自定义加密
public string getMD5(String plainText)
{
System.Security.Cryptography.MD5 key=System.Security.Cryptography.MD5.Create();
Byte[] bytes = key.ComputeHash(System.Text.Encoding.UTF8.GetBytes(plainText));
System.Text.StringBuilder builder = new System.Text.StringBuilder();
foreach(Byte _byte in bytes)
{
builder.Append(_byte.ToString("x2").ToUpper());
}
return builder.ToString();
}
String verify=Request.QueryString["verify"];
String userName=Request.QueryString["userName"];
String strSysDatetime=Request.QueryString["strSysDatetime"];
String jsName=Request.QueryString["jsName"];
String key=……;
if (getMD5(userName + key + strSysDatetime + jsName).Equals(verify))
{
return userName;
}
门户与各接入应用系统之间的账户要实现同步,包括新增用户以及删除用户。比较完善的系统,还将涉及到密码的同步。
单点的方式本质不是很安全,可以伪造并进行模拟登陆。安全性最关键的一环就是验证。一旦获得用户名,后面都将放行了。
以上是关于单点接入方案总结的主要内容,如果未能解决你的问题,请参考以下文章