Javascript端加密java服務端解密
通常我們會通過htts來保證傳輸安全,但如果我們不用https,如何通過javascript來保證瀏覽器端發送的參數進行加密,并且通過RSA算法來處理。
這里我們可以利用jquery的一個加密插件jcryption來處理,可以參考
http://jcryption.org/#examples
現在版本是3.0 但是沒有java端的實現,下次有時間再研究。現在這個用的是1.1的版本
這個可以在
http://linkwithweb.googlecode.com/svn/trunk/Utilities/jCryptionTutorial 獲取
不過他的服務端有個缺陷我修改了。
接來大致介紹如下:
1. 首先服務端有產生publicKey的servlet:
package com.gsh.oauth.auth.servlet;
import java.io.IOException;
import java.security.KeyPair;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gsh.oauth.auth.util.JCryptionUtil;
/**
* Servlet implementation class EncryptionServlet
*/
public class EncryptionServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* Default constructor.
*/
public EncryptionServlet() {
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
int KEY_SIZE = 1024;
if (request.getParameter("generateKeypair") != null) {
JCryptionUtil jCryptionUtil = new JCryptionUtil();
KeyPair keys = null;
//if (request.getSession().getAttribute("keys") == null) { //這里注釋掉 否則第二次請求會500
keys = jCryptionUtil.generateKeypair(KEY_SIZE);
request.getSession().setAttribute("keys", keys);
//}
StringBuffer output = new StringBuffer();
String e = JCryptionUtil.getPublicKeyExponent(keys);
String n = JCryptionUtil.getPublicKeyModulus(keys);
String md = String.valueOf(JCryptionUtil.getMaxDigits(KEY_SIZE));
output.append("{\"e\":\"");
output.append(e);/Files/linugb118/bcprov-jdk15-1.46.jar.zip
output.append("\",\"n\":\"");
output.append(n);
output.append("\",\"maxdigits\":\"");
output.append(md);
output.append("\"}");
output.toString();
response.getOutputStream().print(
output.toString().replaceAll("\r", "").replaceAll("\n", "")
.trim());
} else {
response.getOutputStream().print(String.valueOf(false));
}
}
}
2. Client例子
<html>
<head>
<title>Login form</title>
</head>
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8">
<script src="../js/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="../js/jquery-ui-1.8.2.custom.min.js"
type="text/javascript"></script>
<script type="text/javascript"
src="../js/security/jquery.jcryption-1.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var $statusText = $('<span id="status"></span>').hide();
$("#status_container").append($statusText);
$("#lf").jCryption({
getKeysURL:"/gsh/oauth/encryption?generateKeypair=true",
beforeEncryption : function() {
$statusText
.text("Test Code")
.show();
return true;
},
encryptionFinished : function(
encryptedString,
objectLength) {
$statusText
.text(encryptedString);
return true;
}
});
});
</script>
<body>
<form id="lf" action="/gsh/oauth/authorization"
method="post">
<fieldset><legend>login</legend>
<div>
<div>client_id:<br>
<input type="text" size="45" name="client_id" value=""></div>
<div>redirect_uri:<br>
<input type="text" size="45" name="redirect_uri" value=""></div>
</div>
<div>loginid:<br>
<input type="text" size="45" name="loginid" value=""></div>
</div>
<div>password:<br>
<input type="password" size="45" name="password" value=""></div>
</div>
<div>
<p><input type="submit" /><span id="status_container"></span></p>
</div>
</fieldset>
</form>
</body>
</html>
上面看代碼可以看出 他通過/gsh/oauth/encryption?generateKeypair=true來先請求獲取public 然后通過jcryption進行加密 然后post到服務端。Encryption就是上面的EncryptionServlet。
通過瀏覽器工具可以看到表單里面的數據加密為
jCryption=95f1589502288050e08b4bd8b1a360341cf616d9054531b85a6ef85783c1723b46686ec454ee81f1304fa2370ce24c4d9c06f84d47aa4bdf99310ae12b514db19bfcc325f3a39a584c23b1546550f4e0635c12486f2fd84dec137e1c61cfa775dfa3057a1f0154712aaba0af0cc61810282780f15bed909c24a184e66ab39f2e
3. 目標servlet(authorization)的解密
public class Authorization extends HttpServlet {
protected void doGet(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws ServletException,
IOException {
PrintWriter out = httpServletResponse.getWriter();
KeyPair keys = (KeyPair) httpServletRequest.getSession().getAttribute("keys");
String encrypted = httpServletRequest.getParameter("epCryption");
String client_id = null;
String redirect_uri = null;
String loginid = null;
String password = null;
try {
String data = JCryptionUtil.decrypt(encrypted, keys);
httpServletRequest.getSession().removeAttribute("keys");
Map params = JCryptionUtil.parse(data, "UTF-8");
client_id = (String) params.get("client_id");
redirect_uri = (String) params.get("redirect_uri");
loginid = (String) params.get("loginid");
password = (String) params.get("password");
} catch (Throwable e) {
e.printStackTrace();
}
}
}
上面至少片段,需要相關的js和java問題,請在svn上面獲取。另外還需要bcprov-jdk15-1.46.jar
可以在http://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15/1.46
獲取。
在使用cas3的時候,往往有這樣的需求,希望每個應用有個獨立的登錄頁面
這塊cas 官方文檔有一些說明
https://wiki.jasig.org/display/CAS/Using+CAS+without+the+Login+Screen
首先從官方的角度,不建議使用多個登錄頁面,這樣對安全會形成短板。但是
用戶需求之上,如果我們要實現,有下面幾種方式
1.通過參數來判斷css來改變布局甚至一些圖片,典型cas里面的default-view中
casLoginView.jsp 里面就有這樣的描述,通過描述可以看出他通過不同css來區分
weblogin和mobilelogin。
比如片段
<c:if
test="${not empty requestScope['isMobile'] and not empty mobileCss}">
<meta name="viewport"
content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<!--<link type="text/css" rel="stylesheet" media="screen" href="<c:url value="/css/fss-framework-1.1.2.css" />" />
<link type="text/css" rel="stylesheet" href="<c:url value="/css/fss-mobile-${requestScope['browserType']}-layout.css" />" />
<link type="text/css" rel="stylesheet" href="${mobileCss}" />-->
</c:if>
2.cas服務端(或者各種應用中)建立一個獨立的form頁面
參考:https://wiki.jasig.org/display/CAS/Using+CAS+from+external+link+or+custom+external+form
比如:
在cas(或者各種的應用頁面) web-inf/ 頁面添加testlogin.html
代碼:
<html>
<head />
<body>
<form method="GET" action="http://192.168.2.109:8080/cas/login">
<p>Username : <input type="text" name="username" /></p>
<p>Password : <input type="password" name="password" /></p>
<p>Remember me : <input type="checkbox" name="rememberMe" value="true" /></p>
<p><input type="submit" value="Login !" /></p>
<input type="hidden" name="auto" value="true" />
<input type="hidden" name="service" value="http://localhost/user/checklogintocas.php" />
</form>
</body>
</html>
casLoginView.jsp
實現自動提交功能:
...
<%@ page contentType="text/html; charset=UTF-8" %>
<%
String auto = request.getParameter("auto");
if (auto != null && auto.equals("true")) {
%>
<html>
<head>
<script language="javascript">
function doAutoLogin() {
document.forms[0].submit();
}
</script>
</head>
<body onload="doAutoLogin();">
<form id="credentials" method="POST" action="<%= request.getContextPath() %>/login?service=<%= request.getParameter("service") %>">
<input type="hidden" name="lt" value="${loginTicket}" />
<input type="hidden" name="execution" value="${flowExecutionKey}" />
<input type="hidden" name="_eventId" value="submit" />
<input type="hidden" name="username" value="<%= request.getParameter("username") %>" />
<input type="hidden" name="password" value="<%= request.getParameter("password") %>" />
<% if ("true".equals(request.getParameter("rememberMe"))) {%>
<input type="hidden" name="rememberMe" value="true" />
<% } %>
<input type="submit" value="Submit" style="visibility: hidden;" />
</form>
</body>
</html>
<%
} else {
%>
<jsp:directive.include file="includes/top.jsp" />
...
<jsp:directive.include file="includes/bottom.jsp" />
<%
}
%>
3.第三種方法 其實是第二種方法的啟發,直接把用if-else 把多個頁面組合在一起,通過參數來判斷顯示。(最好能可以支持多套casLoginView.jsp 不過研究下來好像比較難,也許cas開發者也是為了怕再次開放的人用太多靈活的多套casLoginView.jsp 頁面跳來跳去把項目搞混吧。)
之前在
安裝memcache時有提到memcached客戶端是叫memcache,其實還有一個基于libmemcached的客戶端叫memcached,據說性能更好,功能也更多。
memcache的官方主頁:
http://pecl.php.net/package/memcachememcached的官方主頁:
http://pecl.php.net/package/memcached以下是我安裝Memcached版本的PHP模塊的過程記錄:
wget
http://download.tangent.org/libmemcached-0.48.tar.gztar zxf libmemcached-0.48.tar.gz
cd libmemcached-0.48
./configure --prefix=/usr/local/libmemcached --with-memcached
make
make install
wget
http://pecl.php.net/get/memcached-1.0.2.tgztar zxf memcached-1.0.2.tgz
cd memcached-1.0.2
/usr/local/webserver/php/bin/phpize
./configure --enable-memcached --with-php-config=/usr/local/webserver/php/bin/php-config --with-libmemcached-dir=/usr/local/libmemcached
make
make install
在php.ini中加入
extension=memcached.so
完成
另:
在安裝libmemcached時,如果只用./configure,可能會提示:
checking for memcached… no
configure: error: “could not find memcached binary”
兩者使用起來幾乎一模一樣。
$mem = new Memcache;
$mem->addServer($memcachehost, '11211');
$mem->addServer($memcachehost, '11212');
$mem->set('hx','9enjoy');
echo $mem->get('hx');
$md = new Memcached;
$servers = array(
array($memcachehost, '11211'),
array($memcachehost, '11212')
);
$md->addServers($servers);
$md->set('hx','9enjoy');
echo $md->get('hx');
memcached的方法比memcache多不少,比如getMulti,getByKey,addServers等。
memcached沒有memcache的connect方法,目前也還不支持長連接。
memcached 支持 Binary Protocol,而 memcache 不支持,意味著 memcached 會有更高的性能。
Memcache是原生實現的,支持OO和非OO兩套接口并存,memcached是使用libmemcached,只支持OO接口。
更詳細的區別:
http://code.google.com/p/memcached/wiki/PHPClientComparisonmemcached服務端是集中式的緩存系統,分布式實現方法是由客戶端決定的。
memcached的分布算法一般有兩種選擇:
1、根據hash(key)的結果,模連接數的余數決定存儲到哪個節點,也就是hash(key)% sessions.size(),這個算法簡單快速,表現良好。然而這個算法有個缺點,就是在memcached節點增加或者刪除的時候,原有的緩存數據將大規模失效,命中率大受影響,如果節點數多,緩存數據多,重建緩存的代價太高,因此有了第二個算法。
2、Consistent Hashing,一致性哈希算法,他的查找節點過程如下:
首先求出memcached服務器(節點)的哈希值,并將其配置到0~232的圓(continuum)上。然后用同樣的方法求出存儲數據的鍵的哈希值,并映射到圓上。然后從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。如果超過2的32次方后仍然找不到服務器,就會保存到第一臺memcached服務器上。
memcache在沒有任何配置的情況下,是使用第一種方法。memcached要實現第一種方法,似乎是使用(未確認):
$md->setOption(Memcached::OPT_HASH, Memcached::HASH_CRC);
第二種一致性哈希算法:
memcache在php.ini中加
Memcache.hash_strategy =consistent
Memcache.hash_function =crc32
memcached在程序中加(未確認)
$md->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$md->setOption(Memcached::OPT_HASH, Memcached::HASH_CRC);
或
$mem->setOption(Memcached::OPT_DISTRIBUTION,Memcached::DISTRIBUTION_CONSISTENT);
$mem->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE,true);
一些參考文檔:
memcached分布測試報告(一致性哈希情況下的散列函數選擇):
http://www.iteye.com/topic/346682php模塊memcache和memcached區別:
http://hi.baidu.com/dong_love_yan/blog/item/afbe1e12d22e7512203f2e21.htmlPHP模塊:Memcached > Memcache:
http://hi.baidu.com/thinkinginlamp/blog/item/717cd42a11f6e491023bf67a.html20110509@@UPDATE:
如果安裝libmemcached有如下出錯提示:
make[2]: *** [clients/ms_conn.o] Error 1
make[2]: Leaving directory `/www/soft/libmemcached-0.48'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/www/soft/libmemcached-0.48'
make: *** [all] Error 2
可在configure時增加--disable-64bit CFLAGS="-O3 -march=i686"
即:./configure --prefix=/usr/local/libmemcached --with-memcached --disable-64bit CFLAGS="-O3 -march=i686"