Hessian是轻量级的RMI实现使用起来非常的方便,同时与SPRING也结合的非常好。但是在系统中有个比较大的缺陷就是Hessian自身没有解决安全问题。
我在项目的开发中为了解决Hessian的安全问题,在HTTP头中加入了签名信息。
首先要继承HessianProxyFactory在HTTP头中加入时间戳和签名
-
-
-
-
- public class YeatsHessianProxyFactory extends HessianProxyFactory {
-
- private long connectTimeOut = 0;
-
- private String signature;
-
- private long timeStamp;
-
-
-
-
- public String getSignature() {
- return signature;
- }
-
-
-
-
-
- public void setSignature(String signature) {
- this.signature = signature;
- }
-
-
-
-
- public long getConnectTimeOut() {
- return connectTimeOut;
- }
-
-
-
-
-
- public void setConnectTimeOut(long connectTimeOut) {
- this.connectTimeOut = connectTimeOut;
- }
-
- public URLConnection openConnection(URL url) throws IOException {
- URLConnection conn = super.openConnection(url);
-
- if (connectTimeOut > 0) {
- try {
-
- Method method = conn.getClass().getMethod("setConnectTimeout",
- new Class[] { int.class });
-
- if (method != null)
- method.invoke(conn, new Object[] { new Integer(
- (int) connectTimeOut) });
- } catch (Throwable e) {
- }
- }
- if (!StringUtil.isEmptyOrWhitespace(this.signature)) {
- conn.setRequestProperty("Yeats-Signature", this.signature);
- }
- if (this.timeStamp > 0) {
- conn.setRequestProperty("Yeats-Timestamp", Long
- .toString(this.timeStamp));
- }
- return conn;
-
- }
-
-
-
-
- public long getTimeStamp() {
- return timeStamp;
- }
-
-
-
-
-
- public void setTimeStamp(long timeStamp) {
- this.timeStamp = timeStamp;
- }
-
- }
关键之处就在于openConnection方法覆盖了HessianProxyFactory的openConnection方案在原有的openConnection方法基础上加入了链接超时的设置,及时间戳和签名。
继承HessianProxyFactoryBean使proxyFactory设置为上面的子类实现
dsaService的实现请参考我的另一篇文章《java加入DSA签名》,createSignature方法中签名的明文组成形式是“{时间戳},{密码字符串}”
继承HessianServiceExporter对签名进行校验
-
-
-
-
- public class YeatsHessianServiceExporter extends HessianServiceExporter {
- private static final Log log = LogFactory.getLog(YeatsHessianServiceExporter.class);
- private DSA dsaService;
-
- private String crypt;
-
- private long timeValve;
-
- public void handleRequest(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
- String signature = request.getHeader("Yeats-Signature");
- String timestamp = request.getHeader("Yeats-Timestamp");
- if (StringUtil.isEmptyOrWhitespace(signature)) {
- log.error("Can't get signature");
- throw new ServletException("Can't get signature");
- }
- if (StringUtil.isEmptyOrWhitespace(timestamp)) {
- log.error("Cant't get timestamp");
- throw new ServletException("Cant't get timestamp");
- }
- long now = new Date().getTime();
- long range = now - Long.parseLong(timestamp);
- if (range < 0) {
- range = -range;
- }
- if (range > timeValve) {
- log.error("Timestamp is timeout");
- throw new ServletException("Timestamp is timeout");
- }
-
- StringBuilder sb = new StringBuilder();
- sb.append("{");
- sb.append(timestamp);
- sb.append("},{");
- sb.append(crypt);
- sb.append("}");
-
- try {
- if (dsaService.verify(signature, sb.toString())) {
- super.handleRequest(request, response);
- } else {
- log.error("No permission");
- throw new ServletException("No permission");
- }
- } catch (Exception e) {
- log.error("Failed to process remote request!", e);
- throw new ServletException(e);
- }
- }
-
-
-
-
- public DSA getDsaService() {
- return dsaService;
- }
-
-
-
-
-
- public void setDsaService(DSA dsaService) {
- this.dsaService = dsaService;
- }
-
-
-
-
- public String getCrypt() {
- return crypt;
- }
-
-
-
-
-
- public void setCrypt(String crypt) {
- this.crypt = crypt;
- }
-
-
-
-
- public long getTimeValve() {
- return timeValve;
- }
-
-
-
-
-
- public void setTimeValve(long timeValve) {
- this.timeValve = timeValve;
- }
-
- }
覆盖handleRequest方法,在验证通过后再调用父类的handleRequest方法。timeValve为时间戳的校验范围,如果请求到达时间大于这个范围,此请求被认为是非法请求不会再做处理
客户端SPRING配置
- <bean id="searchService"
- class="com.yeatssearch.remote.hessian.YeatsHessianProxyFactoryBean">
- <property name="serviceUrl"
- value="http://localhost:8080/Test/remoting/TestService" />
- <property name="serviceInterface"
- value="com.yeatssearch.test.TestService" />
- <property name="readTimeOut" value="30000"/>
- <property name="connectTimeOut" value="5000"/>
- <property name="crypt" value="sdfsfdsfsdfsdfsdf"/>
- <property name="dsaService" ref="dsaService"></property>
- </bean>
服务器端SPRING配置
- <bean name="/TestService"
- class="com.yeatssearch.remote.hessian.YeatsHessianServiceExporter">
- <property name="service" ref="testService" />
- <property name="serviceInterface"
- value="com.yeatssearch.test.TestService" />
- <property name="timeValve" value="30000"/>
- <property name="crypt" value="sdfsfdsfsdfsdfsdf"/>
- <property name="dsaService" ref="dsaService"/>
- </bean>