其他系統與domino系統單點登入的實現方式
阿新 • • 發佈:2019-01-29
其他系統與domino系統單點登入的實現方式
- 【背景】
隨著企業中業務不斷增多,使用者處理不同的業務則需要頻繁的切換不同的系統進行操作,而使用者則需要記住各個系統的使用者名稱、密碼,頻繁的登入。如果各個系統間能夠進行單點登入,無疑會大大減少使用者重複輸入密碼的困擾。
由於domino系統相對比較封閉,其他系統想相對安全的單點domino系統並非易事。
也許有些人會說通過這樣的方法,通過模擬使用者登入的方式就可以實現:
Names.nsf?login&username=xxx&password=xxx
但是,這樣實現顯然不太安全,一個需要單獨記錄使用者的明文密碼(domino http 密碼是不可逆演算法)。
我知道兩種實現方式相對安全:DSAPI和模擬LTPAToken。本文介紹一種,通過dominoLTPAToken的生成方式實現單點登入的方法。
- 【實現原理】
輸入引數
1. 使用者名稱;
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"); // 單點使用者名稱loginName 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("使用者名稱:"+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(/\n/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"; }
- 【實現效果】