public class AppClientLoginHandler implements KnightAppClientLoginHandler{

   private static Logger logger = Logger.getLogger(AppClientLoginHandler.class);

    public static final String  USER_KEY = "USER_KEY_SESSION";

    @Override
    public void loginClient(KnightCredentialInfo credentialInfo, HttpServletRequest request, HttpServletResponse response) {
     request.getSession().setAttribute(USER_KEY,credentialInfo);
       logger.info("the user id is  "+ credentialInfo.getUserId() + "has logined in the app");
    }
} public interface KnightAppClientLoginHandler {


    /**
     * 登录本应用
     * @param credentialInfo
     * @param request
     * @param response
     */
    public void loginClient(KnightCredentialInfo credentialInfo,HttpServletRequest request,HttpServletResponse response);
} public class DefaultKeyServiceImpl implements KnightKeyService {

	private static Logger logger = Logger.getLogger(DefaultKeyServiceImpl.class
			.getName());

	private String ki4soServerFetchKeyUrl;
	 /** 指定私钥存放文件 ，默认是classPath*/
    private static String PRIVATE_KEY_PATH = null;
    /**私钥文件名*/
    private static String PRIVATE_KEY_FILE = "PrivateKey";
    /** 指定加密算法为RSA */
    private static final String ALGORITHM = "RSA";
	/**
	 * 本应用的秘钥信息。
	 */
	private KnightKey ki4soKey;

	/**
	 * 本应用的应用id.
	 */
	private String appId;

	private static DefaultHttpClient httpClient = new DefaultHttpClient();
	
	//默认无参构造方法
	public DefaultKeyServiceImpl(){
		
	}
	
	public DefaultKeyServiceImpl(String ki4soServerFetchKeyUrl, String appId) {
		super();
		this.ki4soServerFetchKeyUrl = ki4soServerFetchKeyUrl;
		this.appId = appId;
	}

	/* (non-Javadoc)
	 * @see com.github.ebnew.ki4so.core.key.KeyService#findKeyByAppId(java.lang.String)
	 */
	@Override
	public KnightKey findKeyByAppId(String appId) {
		if (ki4soKey == null) {
			// do fetch key from remote server.
			this.ki4soKey = fetchKeyFromKi4soServer();
		}
		return ki4soKey;
	}

	private KnightKey fetchKeyFromKi4soServer() {
		HttpPost httpPost = null;
        KnightKey ki4so = null;
		try {
			httpPost = new HttpPost(ki4soServerFetchKeyUrl);
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			nvps.add(new BasicNameValuePair("appId", this.appId));
			httpPost.setEntity(new UrlEncodedFormEntity(nvps));
			HttpResponse response = httpClient.execute(httpPost);
			if (response.getStatusLine().getStatusCode() == 200) {
				HttpEntity entity = response.getEntity();
				String content = EntityUtils.toString(entity);
				EntityUtils.consume(entity);
				ki4so = JSON.parseObject(content, KnightKey.class);
				return ki4so;
			}
		} catch (Exception e) {
			logger.log(Level.SEVERE, "fetch ki4so key from server error, the url is ["+ki4soServerFetchKeyUrl+"]", e);
		} finally {
			if (httpPost != null) {
				httpPost.releaseConnection();
			}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see com.github.ebnew.ki4so.core.key.KeyService#findKeyByKeyId(java.lang.String)
	 */
	@Override
	public KnightKey findKeyByKeyId(String keyId) {
		if (ki4soKey == null) {
			return this.findKeyByAppId(null);
		}
		return ki4soKey;
	}
	/**
	 * @param keyValue 需解密的key字符串
	 * @param token 标识privateKey
	 * @return 解密后的key
	 */
	public String decryptKey(String token,String keyValue){
		String decryptKey = null;	//解密后的密文
		//检测私钥文件是否存在
		if(!checkKeyFileExistByToken(token)){
			throw new ParamsNotInitiatedCorrectly("The Private Key File Is Not Initiated !!!");
		}
		Key privateKey = loadPrivateKey();		//加载文件私钥
		/** 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
        Cipher cipher;
		try {
			cipher = Cipher.getInstance(ALGORITHM);
	        cipher.init(Cipher.DECRYPT_MODE, privateKey);
			BASE64Decoder decoder = new BASE64Decoder();
			byte[] bytes = decoder.decodeBuffer(keyValue);
	        /** 执行解密操作 */
	        byte[] decryptValue = cipher.doFinal(bytes);
	        decryptKey = new String(decryptValue);		//decryptValue[]转换成String
		} catch (Exception e) {
			// TODO Auto-generated catch block
			logger.log(Level.WARNING, "Cipher error used by private key !!", e);
		} 
        
		return decryptKey;
	}
	/**
	 * @return 用Base64加密后的私钥文件
	 * @throws Exception 
	 */
	public Key loadPrivateKey(){
		Key privateKey = null;
        ObjectInputStream ois = null;
        try {
            /** 将文件中的私钥对象读出 */
            ois = new ObjectInputStream(new FileInputStream(
                    PRIVATE_KEY_FILE));
            privateKey = (Key) ois.readObject();
        } catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(ois != null){
				try {
					ois.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
        }
        return privateKey;
	}
	
	/* (non-Javadoc)
	 * @see com.github.ebnew.ki4so.core.key.KeyService#checkKeyFileExistByToken(java.lang.String)
	 */
	@Override
	public boolean checkKeyFileExistByToken(String token) {
		// TODO Auto-generated method stub
		Properties property = new Properties();
		//初始化文件输入流
		InputStream in = null;
		try {
			in = DefaultKeyServiceImpl.class.getResourceAsStream("/keySecurity.properties");
			property.load(in);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			logger.log(Level.WARNING, "keySecurity File Is Not Found,The Filepath Info Read Fail!", e);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.log(Level.WARNING, "keySecurity File Load Fail!", e);
		}
		//从配置文件中获取私钥存放位置
		PRIVATE_KEY_PATH = property.getProperty("keyPath");
		//配置文件中指定私钥的存放位置
		if(PRIVATE_KEY_PATH != null || !"".equals(PRIVATE_KEY_PATH)){
			PRIVATE_KEY_FILE = PRIVATE_KEY_PATH + PRIVATE_KEY_FILE;
		}
		File file = new File(PRIVATE_KEY_FILE);
		
		return file.exists();
	}

	/* (non-Javadoc)
	 * @see com.github.ebnew.ki4so.core.key.KeyService#generateKeyFile()
	 */
	@Override
	public Object generateKeyFile(String token) throws Exception{
		// TODO Auto-generated method stubj
		String keyFile = null;		//获取服务器端生成秘钥
		HttpPost httpPost = null;
		//判断运用ID列表是否为空
		if(token == null || "".equals(token)){
			throw new ParamsNotInitiatedCorrectly("Token Parameter Is Initiated Incorrently !!");
		}
		//私钥文件已存在
		if(checkKeyFileExistByToken(token)){
			return false; 
		}
		try {
			httpPost = new HttpPost(ki4soServerFetchKeyUrl);
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			nvps.add(new BasicNameValuePair("appId", this.appId));
			httpPost.setEntity(new UrlEncodedFormEntity(nvps));
			HttpResponse response = httpClient.execute(httpPost);
			if (response.getStatusLine().getStatusCode() == 200) {
				HttpEntity entity = response.getEntity();
				keyFile = EntityUtils.toString(entity);
			}
		} catch (Exception e) {
			logger.log(Level.SEVERE, "fetch ki4so key from server error, the url is ["+ki4soServerFetchKeyUrl+"]", e);
		} finally {
			if (httpPost != null) {
				httpPost.releaseConnection();
			}
		}
		//用Base64解密私钥
		@SuppressWarnings("restriction")
		BASE64Decoder decoder = new BASE64Decoder();
	    @SuppressWarnings("restriction")
		byte[] bytes = decoder.decodeBuffer(keyFile);
		keyFile = new String(bytes);  	
		//将私钥写入客户端
        ObjectOutputStream keyOutPutStream = null;
        try {
            /** 用对象流将生成的密钥写入文件 */
            keyOutPutStream = new ObjectOutputStream(new FileOutputStream(PRIVATE_KEY_FILE));
            keyOutPutStream.writeObject(keyFile);
        } catch (Exception e) {
            throw e;
        }
        finally{
            /** 清空缓存，关闭文件输出流 */
        	keyOutPutStream.close();
        }
        return true;
	}
} public class SessionStorage {
	
	/**
	 * 以sessionId为key，session为value。在统一退出的时候获取并销毁session
	 */
	private static final Map<String, HttpSession> SESSION_MAP 
		= new HashMap<String, HttpSession>();

	/**
	 * 保存session信息
	 * @param sessionId 
	 * @param session
	 */
	public  static void put(String sessionId, 
			HttpSession session){
		SESSION_MAP.put(sessionId, session);
	}
	
	/**
	 * 获取session
	 * @param sessionId 
	 */
	public static HttpSession  get(String sessionId){
		return SESSION_MAP.get(sessionId);
	}
	
	/**
	 * 是否存session信息
	 * @param sessionId  
	 */
	public static boolean  containsKey(String sessionId){
		return SESSION_MAP.containsKey(sessionId);
	}
	
	/**
	 * 移除session信息
	 * @param sessionId  
	 */
	public static HttpSession  remove(String sessionId){
		return SESSION_MAP.remove(sessionId);
	}
	
}
 public class StringUtils {
	
	/**
	 * 判断字符串是否为空。
	 * @param str 源字符串。
	 * @return
	 */
	public static boolean isEmpty(String str){
		return str == null || str.length()==0;
	}

}
 public class KnightEncryCredential extends KnightAbstractParameter implements KnightCredential{
    /**
     * 加密后的用户凭证串
     */
    private String credential;


    private KnightCredentialInfo credentialInfo;


    public String getCredential() {
        return credential;
    }

    public void setCredential(String credential) {
        this.credential = credential;
    }

    public KnightCredentialInfo getCredentialInfo() {
        return credentialInfo;
    }

    public void setCredentialInfo(KnightCredentialInfo credentialInfo) {
        this.credentialInfo = credentialInfo;
    }

    @Override
    public boolean isOriginal() {
        return false;
    }
} public class KnightEncryCredentialManagerImpl implements KnightEncryCredentialManager {

    private static final Logger logger = Logger.getLogger(KnightEncryCredentialManagerImpl.class);

    private KnightKeyService keyService;

    public void setKeyService(KnightKeyService keyService) {
        this.keyService = keyService;
    }

    @Override
    public KnightCredentialInfo decrypt(KnightEncryCredential encryCredential) {
        if(encryCredential != null && !StringUtils.isEmpty(encryCredential.getCredential())){
            String credential = encryCredential.getCredential();
            return parseEncryCredentital(credential);
        }
        return null;
    }

    /**
     * 编码的实现流程如下：
     *  1.将加密凭据信息的敏感字段包括 userId,createTime,expiredTime字段
     *  组合成json格式的数据，然后试用密钥对改字符串进行des加密，再将加密后的字符串通过base64编码
     *  2.将上述加密串与其他非敏感的信息进行拼接，格式如下:[敏感信息加密串]?appId = 1&keyId=2
     *  其中敏感信息加密串为第一步得到的结果，appid为应用标识符，keyId为密钥标识
     *  3.试用url进行编码，反之tomcat7下报cookie错误
     *
     * @param credentialInfo
     * @return
     */
    public String encrypt(KnightCredentialInfo credentialInfo) {
        StringBuffer buffer = new StringBuffer();
        if(credentialInfo != null){
            try {
                String data = encryptSensitiveInfo(credentialInfo);
                buffer.append(data).append("appId=").append(credentialInfo.getAppId()).append("&keyId=").append(credentialInfo.getKeyId());
                return URLEncoder.encode(KnightBase64Coder.encryptBase64(buffer.toString().getBytes()),"utf-8");


            }catch (Exception e){

                logger.error("对加密凭据加密失败 error message ::" +e.getMessage());
            }
        }
        return null;
    }

    @Override
    public boolean checkEncryCredentialInfo(KnightCredentialInfo credentialInfo) {
        if(credentialInfo != null){
            //无凭据对应的用户标识，则无效
                if(!StringUtils.isEmpty(credentialInfo.getUserId())) {

                    return false;
                }
              Date now = new Date();
              if(credentialInfo.getExpireTime() != null){
                 long data = credentialInfo.getExpireTime().getTime() - now.getTime();
                  if(data > 0 ){
                      return true;
                  }
              }
            }
        return false;
    }

    private KnightCredentialInfo parseEncryCredentital(String credential){
        KnightCredentialInfo info = new KnightCredentialInfo();
        try {
            credential = URLDecoder.decode(credential,"utf-8");
            credential = new String(KnightBase64Coder.decryptBase64(credential));
            String[] items = credential.split("\\?");
            if(items.length == 2){
                //如果第二个字符串不为空，先解析第二个字符串
                if(items[1] != null && items[1].length() > 0 ){
                    //使用&分割字符串
                    String[] params = items[1].split("&");
                    for (String param : params){
                        if(!StringUtils.isEmpty(param)){
                            String[] values = param.split("=");
                            if(values != null && values.length == 2){

                                if("appId".equalsIgnoreCase(values[0])){
                                    info.setAppId(values[1]);
                                }
                                if("keyId".equalsIgnoreCase(values[0])){
                                    info.setKeyId(values[1]);
                                }
                            }

                        }

                    }

                }
            }
            if(!StringUtils.isEmpty(items[0])){
                byte[] data = KnightBase64Coder.decryptBase64(items[0]);
                 KnightKey key =  keyService.findKeyByKeyId(info.getKeyId());
                if(key != null){
                    byte[] origin = KnightDECoder.decrypt(data,key.toSecurityKey());
                    String json = new String(origin);
                    Map map = (Map)JSON.parse(json);
                    if(map != null) {
                        Object userId = map.get("userId");
                        Object createTime = map.get("createTime");
                        Object expiredTime = map.get("expireTime");
                        info.setUserId(userId == null?"":String.valueOf(userId));
                        info.setCreateTime(createTime == null ? null :new Date(Long.valueOf(createTime.toString())));
                        info.setExpireTime(expiredTime == null ? null:new Date(Long.valueOf(expiredTime.toString())));
                    }
                 }
            }


        }catch (Exception e){
            logger.error("parse encry credential exception ::"  + e.getMessage());
            e.printStackTrace();

        }
        return info;

    }

    /**
     * 解析加密后的凭据信息为凭据对象,过程与加密过程相反的逆过程
     * @param info
     * @return
     * @throws Exception
     */
    private String encryptSensitiveInfo(KnightCredentialInfo info) throws Exception{

        Map<String,Object> map = new HashMap<String,Object>();
        map.put("userId",info.getUserId());
        if(info.getCreateTime() != null){
            map.put("createTime",info.getCreateTime());
        }
        if(info.getExpireTime() != null){
            map.put("expiredTime",info.getExpireTime());

        }
        KnightKey knightKey = keyService.findKeyByKeyId(info.getKeyId());
        if(knightKey != null){
            Key key = knightKey.toSecurityKey();
            if(key != null){
              byte[] data =   KnightDECoder.encrypt(JSON.toJSONBytes(map),key);
              return KnightBase64Coder.encryptBase64(data);
            }
        }
        return "";

    }
} public class KnightKey implements Serializable {

    private static final long serialVersionUID = 3535643339281384507L;
    /**
     * 私钥ID
     */
    private String keyId;


    /**
     * 应用id
     */
    private String appId;


    /**
     * 私钥值
     */
    private String value;

    /**
     * 私钥文件存放的路径
     */
    private String keyPath;

    public String getKeyId() {
        return keyId;
    }

    public void setKeyId(String keyId) {
        this.keyId = keyId;
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public String getKeyPath() {
        return keyPath;
    }

    public void setKeyPath(String keyPath) {
        this.keyPath = keyPath;
    }


    public Key toSecurityKey(){
        try{
            if(this.getValue() != null){
                return KnightDECoder.initSecretKey(this.getValue());
            }
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
        return null;

    }
    @Override
    public String toString() {
        return "knight: [keyId=" + keyId + ", appId=" + appId + ", value="
                + value + ",keyPath=" + keyPath + "]";
    }
} public interface KnightKeyService {

    /**
     * 根据密钥id查找对应的密钥信息
     * @param keyId
     * @return
     */
    public KnightKey findKeyByKeyId(String keyId);

    /**
     * 根据应用id查找对应的密钥信息
     * @param appId
     * @return
     */
    public KnightKey findKeyByAppId(String appId);


    /**
     * 判断密钥文件是否生成
     * @param token 改文件存在的标识
     * @return
     */
    public boolean checkKeyFileExistByToken(String token);


    /**
     * 生成非对称加密文件(客户端生成私钥文件；服务端生成公钥文件)
     * @param token
     * @return
     */
    public Object generateKeyFile(String token);

} public class KnightCredentialInfo implements Serializable{

    private static final long serialVersionUID = -6546063621536994328L;
    /**
     * 应用唯一标识
     */
    private String appId;

    /**
     * 用户唯一标识
     */

    private String userId;

    /**
     * 密钥唯一标识
     */
    private String keyId;


    /**
     * 加密凭证的创建时间
     */
    private Date createTime;


    /**
     * 加密凭证的失效时间
     */
    private Date expireTime;

    /**
     * 加密凭证的盐值
     */

    private String salt;


    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getKeyId() {
        return keyId;
    }

    public void setKeyId(String keyId) {
        this.keyId = keyId;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getExpireTime() {
        return expireTime;
    }

    public void setExpireTime(Date expireTime) {
        this.expireTime = expireTime;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }
} public interface WebConstants {
	
	/**
	 * ki4so中心认证服务器写入到用户web客户端cookie中的认证加密后的凭据的键名称。
	 */
	public static final String KI4SO_SERVER_ENCRYPTED_CREDENTIAL_COOKIE_KEY = "KI4SO_SERVER_EC";
	
	/**
	 * ki4so客户端应用服务器写入到用户web客户端cookie中的认证加密后的凭据的键名称。
	 */
	public static final String KI4SO_CLIENT_ENCRYPTED_CREDENTIAL_COOKIE_KEY = "KI4SO_CLIENT_EC";
	
	/**
	 * 目的服务地址service的参数名。
	 */
	public static final String SERVICE_PARAM_NAME = "service";
	
	/**
	 * 目的服务地址存储在session中的key值。
	 */
	public static final String KI4SO_SERVICE_KEY_IN_SESSION = "KI4SO_SERVICE_KEY";
	
	/**
	 * 用户标识的参数名。
	 */
	public static final String USER_ID_PARAM_NAME = "userId";

}

import com.github.ebnew.ki4so.app.custom.AppClientLoginHandler;
import com.github.ebnew.ki4so.client.handler.KnightAppClientLoginHandler;
import com.github.ebnew.ki4so.client.key.DefaultKeyServiceImpl;
import com.github.ebnew.ki4so.client.session.SessionStorage;
import com.github.ebnew.ki4so.common.utils.StringUtils;
import com.github.ebnew.ki4so.core.authentication.KnightEncryCredential;
import com.github.ebnew.ki4so.core.authentication.KnightEncryCredentialManagerImpl;
import com.github.ebnew.ki4so.core.key.KnightKey;
import com.github.ebnew.ki4so.core.key.KnightKeyService;
import com.github.ebnew.ki4so.core.model.KnightCredentialInfo;
import com.github.ebnew.ki4so.web.utils.WebConstants;
import org.apache.log4j.Logger;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
package com.github.ebnew.ki4so.client.web.filters;



/**
 * knight客户端应用的过滤器，从而实现集成knight单点登录系统
 * @author zhenglu
 * @since 15/4/30
 */
public class KnightClientFilter extends BaseClientFilter {

    private static Logger logger = Logger.getLogger(KnightClientFilter.class);

    //在客户端的session中的用户信息，避免频繁认证，提高性能
    public static final String USER_STATE_IN_SESSION_KEY = "knight_client_user_info_session_key";

    //将服务器登出地址设置到request属性中
    public static final String KNIGHT_SERVER_LOGOUT_URL = "knight_server_logout_url";


    // 登录服务器url地址
    protected String knightServerLoginUrl = knightServerHost +"login.do";

      //获取应用密钥信息的url地址
    protected String knightServerFetchKeyUrl = knightServerHost + "fetchKey.do";

    //退出服务器url地址

    protected String knightServerLogoutUrl = knightServerHost + "logout.do";

    //本应用服务器上的应用id
    protected String knightClientAppId = "1001";

   //本应用对应的加密key
    protected KnightKey knightKey;