1. 程式人生 > >其他系統與domino系統單點登入的實現方式

其他系統與domino系統單點登入的實現方式



其他系統與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

expirescookie到期時間

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";
    }
  • 【實現效果】