概述
单点登陆(SSO-Single Sign On)简而言之就是能够通过认证系统能够使用户在一个系统登陆后就可以在权限允许范围内访问其他的系统而不需要重新登陆,或者保证用户登陆的单一性即用户在同一时间只能有一个登陆状态.
单点登陆有很多种:根据系统的架构可以分为C/S系统的,B/S系统的或者两者混合系统的;根据应用的多少可以分为单应用的和多应用的;根据系统的网络环境还可以分为单域的和跨域的.
这个文章要讨论的是.NET框架下B/S模式单个应用的单点登陆:保证用户登陆的后单一性即后一个用户登陆后前一个用户将会自动退出系统.系统的认证原理:用户信息存储在数据库中.
原理
用户登陆后的信息是存储在Session中(Session的值实际上是存储在server上的),每个Session的SessionID是不同的用户第二次登陆之前检测Session中是否已经有该用户登陆如果有则重写Session中的值,使第一个登陆用户的Session失效从而退出系统.否则直接登陆系统.
1.用于存储用户登陆信息的类UserCollection.cs
public class UserCollection
{
static UserCollection()
{
if (loginUser == null)
{
loginUser = new Hashtable();
}
}
public enum UserStatus
{
Usual,
Change,
NoUser
}
private static Hashtable loginUser;
public static Hashtable LoginUser
{
get { return loginUser; }
set { loginUser = value; }
}
public static void AddUser(string userId, string guid)
{
LoginUser.Add(userId, guid);
}
public static void Login(string userId, string guid)
{
UserStatus status = GetUserStatus(userId, guid);
if (status == UserStatus.NoUser)
{
AddUser(userId, guid);
}
else if (status == UserStatus.Change)
{
UpdateUser(userId, guid);
}
else
{
}
}
public static void LogOff(string userId)
{
DeleteUser(userId);
}
public static void LogOff(string userId, string guid)
{
if (guid == null)
DeleteUser(userId);
else
DeleteUser(userId, guid);
}
public static void DeleteUser(string userId)
{
if (LoginUser.ContainsKey(userId))
{
LoginUser.Remove(userId);
}
}
public static void DeleteUser(string userId, string guid)
{
if (LoginUser.ContainsKey(userId))
{
if (guid == LoginUser[userId].ToString())
{
LoginUser.Remove(userId);
}
}
}
public static void UpdateUser(string userId, string newGuid)
{
if (LoginUser.ContainsKey(userId))
{
DeleteUser(userId);
AddUser(userId, newGuid);
}
}
public static string GetGuid(string userId)
{
if (LoginUser.ContainsKey(userId))
{
return LoginUser[userId].ToString();
}
else
return null;
}
public static bool ContainKey(string userId)
{
return LoginUser.ContainsKey(userId);
}
public static UserStatus GetUserStatus(string userId, string guid)
{
if (LoginUser.ContainsKey(userId))
{
if (guid == LoginUser[userId].ToString())
{
return UserStatus.Usual;
}
else
{
return UserStatus.Change;
}
}
else
{
return UserStatus.NoUser;
}
}
}
{
static UserCollection()
{
if (loginUser == null)
{
loginUser = new Hashtable();
}
}
public enum UserStatus
{
Usual,
Change,
NoUser
}
private static Hashtable loginUser;
public static Hashtable LoginUser
{
get { return loginUser; }
set { loginUser = value; }
}
public static void AddUser(string userId, string guid)
{
LoginUser.Add(userId, guid);
}
public static void Login(string userId, string guid)
{
UserStatus status = GetUserStatus(userId, guid);
if (status == UserStatus.NoUser)
{
AddUser(userId, guid);
}
else if (status == UserStatus.Change)
{
UpdateUser(userId, guid);
}
else
{
}
}
public static void LogOff(string userId)
{
DeleteUser(userId);
}
public static void LogOff(string userId, string guid)
{
if (guid == null)
DeleteUser(userId);
else
DeleteUser(userId, guid);
}
public static void DeleteUser(string userId)
{
if (LoginUser.ContainsKey(userId))
{
LoginUser.Remove(userId);
}
}
public static void DeleteUser(string userId, string guid)
{
if (LoginUser.ContainsKey(userId))
{
if (guid == LoginUser[userId].ToString())
{
LoginUser.Remove(userId);
}
}
}
public static void UpdateUser(string userId, string newGuid)
{
if (LoginUser.ContainsKey(userId))
{
DeleteUser(userId);
AddUser(userId, newGuid);
}
}
public static string GetGuid(string userId)
{
if (LoginUser.ContainsKey(userId))
{
return LoginUser[userId].ToString();
}
else
return null;
}
public static bool ContainKey(string userId)
{
return LoginUser.ContainsKey(userId);
}
public static UserStatus GetUserStatus(string userId, string guid)
{
if (LoginUser.ContainsKey(userId))
{
if (guid == LoginUser[userId].ToString())
{
return UserStatus.Usual;
}
else
{
return UserStatus.Change;
}
}
else
{
return UserStatus.NoUser;
}
}
}
2. 添加一个专门用于检测用户状态的页面userstatus.aspx
protected void Page_Load(object sender, EventArgs e)
{
Response.ClearContent();
Response.Write(getUserStatus());
Response.End();
}
private string getUserStatus()
{
string returnString = "Usual";
if (Session["UserID"] != null && Session["Guid"] != null)
{
UserCollection.UserStatus status = UserCollection.GetUserStatus(Session["UserID"].ToString(), Session["Guid"].ToString());
if (status == UserCollection.UserStatus.Change)
{
UserCollection.LogOff(Session["UserID"].ToString(), Session["Guid"].ToString());
Session.Clear();
returnString = "LogOff";
}
else
{
returnString = "Usual";
}
}
else
Response.Redirect("../Login.aspx");
return returnString;
}
{
Response.ClearContent();
Response.Write(getUserStatus());
Response.End();
}
private string getUserStatus()
{
string returnString = "Usual";
if (Session["UserID"] != null && Session["Guid"] != null)
{
UserCollection.UserStatus status = UserCollection.GetUserStatus(Session["UserID"].ToString(), Session["Guid"].ToString());
if (status == UserCollection.UserStatus.Change)
{
UserCollection.LogOff(Session["UserID"].ToString(), Session["Guid"].ToString());
Session.Clear();
returnString = "LogOff";
}
else
{
returnString = "Usual";
}
}
else
Response.Redirect("../Login.aspx");
return returnString;
}
3.添加JS文件header.js,使用AJAX的机制来调用userstatus.aspx以检测用户的登陆信息
var xmlhttp;
function header()
{
var url="../UserStatus.aspx?dd=" + getRandom();
if (window.XMLHttpRequest) {
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=xmlhttpChange;
xmlhttp.open("GET",url,false);
xmlhttp.send(null);
}
else if (window.ActiveXObject) {
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
if (xmlhttp)
{
xmlhttp.onreadystatechange=xmlhttpChange;
xmlhttp.open("GET",url,false);
xmlhttp.send();
}
}
var text = xmlhttp.responseText;
if(text=="LogOff")
{ document.location.href="../Login.aspx";
}
xmlhttp = null;
}
function getRandom()
{
var ran_number= Math.random()*5;
return ran_number;
}
function xmlhttpChange()
{
if (xmlhttp.readyState==4)
{
if (xmlhttp.status==200)
{
// some code here
}
else
{
//alert("Problem retrieving XML data")
}
}
}
function header()
{
var url="../UserStatus.aspx?dd=" + getRandom();
if (window.XMLHttpRequest) {
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=xmlhttpChange;
xmlhttp.open("GET",url,false);
xmlhttp.send(null);
}
else if (window.ActiveXObject) {
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP")
if (xmlhttp)
{
xmlhttp.onreadystatechange=xmlhttpChange;
xmlhttp.open("GET",url,false);
xmlhttp.send();
}
}
var text = xmlhttp.responseText;
if(text=="LogOff")
{ document.location.href="../Login.aspx";
}
xmlhttp = null;
}
function getRandom()
{
var ran_number= Math.random()*5;
return ran_number;
}
function xmlhttpChange()
{
if (xmlhttp.readyState==4)
{
if (xmlhttp.status==200)
{
// some code here
}
else
{
//alert("Problem retrieving XML data")
}
}
}
4.让系统的webmaster或者类似页面每10检测一次用户的登陆信息
<script language="JavaScript" src="../JS/header.js"></script>
<script language="javascript">
waitTime=10000; //10 秒
timer=setInterval("header()",waitTime);
</script>
<script language="javascript">
waitTime=10000; //10 秒
timer=setInterval("header()",waitTime);
</script>
至此系统可以实现后单点登陆.