其它系统与domino系统单点登录的实现方式
-
【背景】
随着企业中业务不断增多,用户处理不同的业务则须要频繁的切换不同的系统进行操作。而用户则须要记住各个系统的username、password,频繁的登录。假设各个系统间可以进行单点登录。无疑会大大降低用户反复输入password的困扰。
因为domino系统相对照较封闭。其它系统想相对安全的单点domino系统并不是易事。
或许有些人会说通过这个方案。通过模拟用户登录的方式就能够实现:
Names.nsf?login&username=xxx&password=xxx
可是。这样实现显然不太安全。一个须要单独记录用户的明文password(domino httppassword是不可逆算法)。
我知道两种实现方式相对安全:DSAPI和模拟LTPAToken。
本文介绍一种,通过dominoLTPAToken的生成方式实现单点登录的方法。
-
【实现原理】
输入參数
1. username;
2. 登录系统时间;
3. 登录到期时间。
4. Domino密钥
输出參数
加密后的LTPAToken加密串
创建cookie
document.cookie= "LtpaToken="+ token + ";expires=" + exp.toGMTString() +";path=/;domain=.xxx.com";
token:加密token
expires:cookie到期时间
domain:单点域名,与dominoSSO配置文档同样,格式:.xxx.com
-
【參考代码】
-
java代码
import java.io.PrintWriter; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import lotus.domino.*; public class JavaAgent extends AgentBase { public void NotesMain() { Session session = null; AgentContext agentContext = null; Document doc = null; PrintWriter pw = null; String token = ""; String sReturn = "false"; String sJson = ""; try { pw = getAgentOutput(); session = getSession(); agentContext = session.getAgentContext(); doc = agentContext.getDocumentContext(); String sPara = doc.getItemValueString("query_string_decoded"); // 单点usernameloginName String canonicalUser = sPara.substring(sPara.indexOf("sPara=")+6); // 单点起始时间 Date tokenCreation = new Date(new Date().getTime() - 60000 * 10); String timeLimit="720"; // 单点到期时间 Date tokenExpires = new Date(tokenCreation.getTime() + Long.parseLong(timeLimit) * 60000); // domino SSO 密钥(domino SSO配置文档的LTPA_DominoSecret域值) String dominoSecret = "9BY2oinn1FmI42i3oNEnL3nNVPQ="; token = LtpaToken.generate(canonicalUser, tokenCreation, tokenExpires, dominoSecret).getLtpaToken(); //System.out.println("token==ssobak==="+token); sReturn = "true"; DominoTokenParser tokenParser = new DominoTokenParser(); System.out.println("username:"+tokenParser.parse(token,dominoSecret)); } catch(Exception e) { e.printStackTrace(); }finally{ pw.println("Content-type: text/plain;charset=GB2312"); sJson = "{"oResult":""+sReturn+"","token":""+token+""}"; System.out.println("sJson="+sJson); pw.println(sJson); //回收domino对象 fnRecycle(doc); fnRecycle(agentContext); fnRecycle(session); if(pw!=null){ pw.close(); } } } public void fnRecycle(Base object){ if(object != null){ try { object.recycle(); } catch (NotesException e) { // TODO 自己主动生成 catch 块 e.printStackTrace(); } } } }
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Calendar; import java.util.Date; import java.util.Properties; /** * Lightweight Third Party Authentication. Generates and validates ltpa tokens used in Domino single sign on * environments. Does not work with WebSphere SSO tokens. You need a properties file named LtpaToken.properties which * holds two properties. * * <pre> * ) domino.secret=The base64 encoded secret found in the field LTPA_DominoSecret in the SSO configuration document. * ) cookie.domain=The domain you want generated cookies to be from. e.g. '.domain.com' (Note the leading dot) *</pre> * * @author $Author: rkelly $ * @version $Revision: 1.1 $ * @created $Date: 2003/04/07 18:22:14 $ */ public final class LtpaToken { private byte[] creation; private Date creationDate; private byte[] digest; private byte[] expires; private Date expiresDate; private byte[] hash; private byte[] header; private String ltpaToken; private Properties properties = null; private byte[] rawToken; private byte[] user; /** * Constructor for the LtpaToken object * * @param token * Description of the Parameter */ public LtpaToken(String token) { init(); ltpaToken = token; rawToken = Base64.decode(token); user = new byte[(rawToken.length) - 40]; for (int i = 0; i < 4; i++) { header[i] = rawToken[i]; } for (int i = 4; i < 12; i++) { creation[i - 4] = rawToken[i]; } for (int i = 12; i < 20; i++) { expires[i - 12] = rawToken[i]; } for (int i = 20; i < (rawToken.length - 20); i++) { user[i - 20] = rawToken[i]; } for (int i = (rawToken.length - 20); i < rawToken.length; i++) { digest[i - (rawToken.length - 20)] = rawToken[i]; } creationDate = new Date(Long.parseLong(new String(creation), 16) * 1000); expiresDate = new Date(Long.parseLong(new String(expires), 16) * 1000); } /** * Constructor for the LtpaToken object */ private LtpaToken() { init(); } /** * Gets the creationDate attribute of the LtpaToken object * * @return The creationDate value */ public Date getCreationDate() { return creationDate; } /** * Gets the expiresDate attribute of the LtpaToken object * * @return The expiresDate value */ public Date getExpiresDate() { return expiresDate; } /** * Gets the user attribute of the LtpaToken object * * @return The user value */ public String getCanonicalUser() { return new String(user); } public String getUser(String prefix, String suffix) { String userName = new String(user); if (prefix !=null && !prefix.equals("")) { userName = userName.substring(userName.indexOf(prefix) + prefix.length()); } if (suffix ==null || suffix.equals("")) { suffix = "/"; } return userName.substring(0, userName.indexOf("/")); } public String getUser() { return new String(user); } /** * Validates the SHA-1 digest of the token with the Domino secret key. * * @return Returns true if valid. */ public boolean isValid(LtpaTokenConfig config) { boolean validDigest = false; boolean validDateRange = false; byte[] newDigest; byte[] bytes = null; Date now = new Date(); MessageDigest md = getDigest(); bytes = concatenate(bytes, header); bytes = concatenate(bytes, creation); bytes = concatenate(bytes, expires); bytes = concatenate(bytes, user); bytes = concatenate(bytes, Base64.decode(config.getDominoSecret())); newDigest = md.digest(bytes); validDigest = MessageDigest.isEqual(digest, newDigest); validDateRange = now.after(creationDate) && now.before(expiresDate); return validDigest & validDateRange; } /** * String representation of LtpaToken object. * * @return Returns token String suitable for cookie value. */ public String toString() { return ltpaToken; } /** * Creates a new SHA-1 <code>MessageDigest</code> instance. * * @return The instance. */ private MessageDigest getDigest() { try { return MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException nsae) { nsae.printStackTrace(); } return null; } /** * Description of the Method */ private void init() { creation = new byte[8]; digest = new byte[20]; expires = new byte[8]; hash = new byte[20]; header = new byte[4]; } /** * Validates the SHA-1 digest of the token with the Domino secret key. * * @param ltpaToken * Description of the Parameter * @return The valid value */ public static boolean isValid(String ltpaToken,LtpaTokenConfig config) { LtpaToken ltpa = new LtpaToken(ltpaToken); return ltpa.isValid(config); } /** * Generates a new LtpaToken with given parameters. * * @param canonicalUser * User name in canonical form. e.g. 'CN=Robert Kelly/OU=MIS/O=EBIMED'. * @param tokenCreation * Token creation date. * @param tokenExpires * Token expiration date. * @return The generated token. */ public static LtpaToken generate(String canonicalUser, Date tokenCreation, Date tokenExpires,String secret) { LtpaToken ltpa = new LtpaToken(); System.out.println("Generating token for " + canonicalUser); Calendar calendar = Calendar.getInstance(); MessageDigest md = ltpa.getDigest(); ltpa.header = new byte[] { 0, 1, 2, 3 }; byte[] token = null; calendar.setTime(tokenCreation); ltpa.creation = Long.toHexString(calendar.getTimeInMillis() / 1000).toUpperCase().getBytes(); calendar.setTime(tokenExpires); ltpa.expires = Long.toHexString(calendar.getTimeInMillis() / 1000).toUpperCase().getBytes(); //try { // canonicalUser = new String(canonicalUser.getBytes(), "GB2312"); //} catch (UnsupportedEncodingException e) { // TODO 自己主动生成 catch 块 // e.printStackTrace(); //} ltpa.user = canonicalUser.getBytes(); //ltpa.user = canonicalUser.getBytes(Charset.forName("GB18030")); token = concatenate(token, ltpa.header); token = concatenate(token, ltpa.creation); token = concatenate(token, ltpa.expires); token = concatenate(token, ltpa.user); md.update(token); ltpa.digest = md.digest(Base64.decode(secret)); token = concatenate(token, ltpa.digest); return new LtpaToken(new String(Base64.encodeBytes(token,Base64.DONT_BREAK_LINES))); } /** * Helper method to concatenate a byte array. * * @param a * Byte array a. * @param b * Byte array b. * @return a + b. */ private static byte[] concatenate(byte[] a, byte[] b) { if (a == null) { return b; } else { byte[] bytes = new byte[a.length + b.length]; System.arraycopy(a, 0, bytes, 0, a.length); System.arraycopy(b, 0, bytes, a.length, b.length); return bytes; } } public String getLtpaToken() { return ltpaToken; } public void setLtpaToken(String ltpaToken) { this.ltpaToken = ltpaToken; } }
-
js代码
function fnSSO(){ if(document.getElementById("username").value==""){ alert("请输入登录名!"); document.getElementById("username").focus(); return false; } var objHTTP= new ActiveXObject("Microsoft.XMLHTTP"); var sDbPath = document.getElementById("DbFilePath").value; var sPara = document.getElementById("username").value; var vurl = "/"+sDbPath+"/"+"ajaxSSO" + "?
openagent&sPara=" + sPara; objHTTP.open("GET", vurl, false, "", ""); objHTTP.setRequestHeader("If-Modified-Since","0"); objHTTP.send(false); var getOptions = objHTTP.responseText; getOptions = getOptions.replace(/ /ig,""); var oOptions = eval("(" + getOptions + ")"); var sReturn = ""; if(oOptions && oOptions.oResult=="true"){ var Days = 30; var exp = new Date(); exp.setTime(exp.getTime() + Days*24*60*60*1000); var token = oOptions.token; if(token!=""){ // 创建单点cookie document.cookie = "LtpaToken="+ token + ";expires=" + exp.toGMTString() + ";path=/;domain=.xxx.com"; } sReturn = sPara + ":单点登录成功!" }else{ sReturn = sPara + ":单点失败。" } document.getElementById("ssoinfo").innerHTML = "<font color='red'>" + sReturn + "</font>"; // 页面跳转 location.href = "http://xxx.com/xxx.nsf/xxx?
openform"; }
-
【实现效果】