开源项目地址:
https://github.com/felixyin/SingleLoginForWeb/
一、开发原因:
0、项目中需要url加密的手段
1、其他系统跳入电子印章平台的链接很多,难以管理
2、其他的系统互相“登录”的情况很多,可以借鉴这个成功的模式
3、前台登录不够安全
二、单点登录经过的大体步骤,以tcs-电子签章为例
0、原本考虑仅对url的部分属性加密,但不足够安全,所以考虑web服务器代替用户进行登录的情况(这个思想很重要)
1、用户在tcs系统中操作,当需要签章的时候,点击一个连接(想直接跳转到电子印章系统合同的电子签章页面),这个连接不指向电子签章系统,
而是指向当前tcs的系统中的某个处理类A
2、首先需要注册: 处理类A,从当前tcs系统中的session中取出,accountid(登录名),password(登录密码),主要使用.net的HttpWebRequest这个类,
在tcs系统中的后台(也就是tcs的iis服务器)发送request请求B(后台发送请求,所以与当前浏览网页的用户的浏览器无关,甚至于这台
客户端机器无关)
3、电子印章系统类C在接受到请求B的时候,首先取出accountid、password进行正常登陆(验证用户是否存在,密码是否正确),
然后查询出用户需要存入电子印章系统session中的那些数据O(权限和用户基本信息之类的数据)和request请求B中的其他一些数据,
一并存入电子签章系统的context(应用上下文)中,以随机生成的长达20个字符的Key作为下标。并返回request请求这个Key,
作为下次真正登录的钥匙。
4、然后才是真正的登录: tcs系统的处理类A发送request请求B后,电子印章系统返回了Key,此时,类A便可以根据这个Key,再次发起request请求D(这次发起请求与上次不一样
这次是redict,是客户的浏览器进行redirct请求)进行真正的登录。
电子印章系统类E在接收到请求D,form表单提交的(之所以用form表单提交,是因为,要将登录钥匙key也给隐藏掉)key后,
到电子印章系统的context中,查找对应的信息O,如果找不到,则提示用户登录失败,如果找到则将信息从上下文中移入到当前
登录用户(客户浏览器发送的请求)的session中。
5、然后就是根据session中的信息O的某些信息(某些信息是tcs传入的,一般定义为一个数组)来进行不同的操作,如数组【0】可能是type,
如果type==1,则跳转到签章列表,如果==2则跳转到印章申请等。。
主要代码如下,原理不多说,自己看:
.net核心类:
Imports System.Net
Imports System.IO
Imports System.Text
Public Class SingleLogin
''' <summary>
''' 单点登陆方法
''' </summary>
''' <param name="res">页面Response</param>
''' <param name="sRegUrl">注册Action</param>
''' <param name="sLoginUrl">登陆Action</param>
''' <param name="InfoList">参数集合</param>
''' <remarks></remarks>
Public Shared Sub login(res As Web.HttpResponse, sRegUrl As String, sLoginUrl As String, InfoList As ArrayList)
'验证用户&注册单点登录
Dim sKey As String = registerSingleLogin(sRegUrl, InfoList)
'单点登录
signleLogin(res, sLoginUrl, sKey)
End Sub
''' <summary>
''' 向Url传输信息,并Key
''' </summary>
''' <param name="sUrl">Url</param>
''' <param name="InfoList">传输数据</param>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function registerSingleLogin(sUrl As String, InfoList As ArrayList) As String
Dim sAction As String = sUrl & "?params="
For Each sItem As String In InfoList
sAction += sItem & ","
Next
'sUrl.Remove(0, sUrl.Length - 1)
sAction = sAction.Substring(0, sAction.Length - 1)
Return sendRequest(sAction)
End Function
''' <summary>
''' 发送Request请求
''' </summary>
''' <param name="url">Url</param>
''' <returns></returns>
''' <remarks></remarks>
Private Shared Function sendRequest(ByVal url As String) As String
Dim request As HttpWebRequest = CType(WebRequest.Create(url), HttpWebRequest)
request.Credentials = CredentialCache.DefaultCredentials
Dim response As HttpWebResponse = CType(request.GetResponse(), HttpWebResponse)
Dim receiveStream As Stream = response.GetResponseStream()
Dim readStream As New StreamReader(receiveStream, Encoding.UTF8)
Dim resultKey As String = readStream.ReadToEnd()
response.Close()
readStream.Close()
Console.WriteLine(resultKey)
Return resultKey
End Function
''' <summary>
''' 单点登陆
''' </summary>
''' <param name="sUrl">Url</param>
''' <param name="sKey">Key</param>
''' <remarks></remarks>
Private Shared Sub signleLogin(res As Web.HttpResponse, sUrl As String, sKey As String)
sKey = sKey.Replace("""", "")
If sKey.Length > 0 Then
'Response.Redirect(url & "?key=" & sKey)
Dim sHtml As String = ""
sHtml = "<script type='text/javascript'>" & _
"window.onload=function(){document.forms[0].submit();}" & _
"</script>" & _
"<form action='" & sUrl & "' style='display:none' method='post' target='_blank'>" & _
"<input type='hidden' name='key' value='" & sKey & "'/>" & _
"</form>"
res.Write(sHtml)
res.Flush()
End If
End Sub
End Class
.net系统单点登录java系统代码调用举例:
Protected Sub LinkButton1_Click(sender As Object, e As EventArgs) Handles LinkButton1.Click
'1.访问 Signature 传输本系统Session中用户信息, Signature确认用户信息后,生成Key并返回。
'2.跳转 Signature 并以Key进行登陆Signature。
'传输本系统用户信息 AccountId,PassWord
Dim InfoList As ArrayList = New ArrayList()
'List Add 顺序不能错
InfoList.Add("0") '其他信息1
InfoList.Add("admin")' 用户名
InfoList.Add("a12345")'密码
InfoList.Add("cCode")'其他信息2
Dim sRegUrl As String, sLoginUrl As String
sRegUrl = System.Configuration.ConfigurationManager.AppSettings("Signature") & "/registerSingleSignon.action"
sLoginUrl = System.Configuration.ConfigurationManager.AppSettings("Signature") & "/singleSignon.action"
FY.SingleLogin.login(Response, sRegUrl, sLoginUrl, InfoList)
'Response.Write("")
End Sub
java系统为单点登录编写两个接口,一个注册、一个是登录:
/**
* 单点登录接口
*
* @return
* @throws Exception
*/
/**
* , params = { "charSet", "UTF-8" }
*/
@Action(value = "registerSingleSignon", results = {@Result(name = "success", type = "json", params = {
"ignoreHierarchy", "false", "root", "key"})})
public String registerSingleSignon() throws Exception {
if (null == params || 0 >= params.length()) {
key = "false,传入的params参数不能空";
return SUCCESS;
}
key = loginService.registerSingleSignon(params);
if (null == key) { // 没有这个用户,或者密码错误
key = "false,没有这个用户或密码错误";
}
return SUCCESS;
}
/**
* 单点登录 参数列表
*/
private String singleParams;
public String getSingleParams() {
return singleParams;
}
public void setSingleParams(String singleParams) {
this.singleParams = singleParams;
}
/**
* 真正的单点登录
*
* @return
* @throws Exception
*/
@Action(value = "singleSignon", results = {
@Result(name = "success", location = "/Main.jsp", type = "dispatcher"),
@Result(name = "failed", location = "/index.jsp", type = "dispatcher")})
public String singleSignon() throws Exception {
LoginBo user = loginService.singleSignon(key);
if (null == user) {
setAccountError("单点登录出错了!可能的情况如下,请联系管理员!1、您已经使用此链接登录过一次;2、您的会话超时了;3、不能将连接copy到其他机子上使用;4、请您重新”服务器端登录“试试;5、密码错误");
} else {
logger.info("用户进行单点登录:" + user.getSysUsersEntity().getCaccountid());
String[] paramArray = user.getParams();
singleParams = JSONUtil.serialize(paramArray);
if (null == paramArray || 0 >= paramArray.length) {
setAccountError("发票签收单,服务器单点登录,传入params数组不正确");
} else {
return SUCCESS;
}
}
return "failed";
}