JAVA

org.springframework.dao.IncorrectResultSizeDataAccessException

今天在写一个游戏接口的时候,以为数据库中就一条记录,想当然的写下了

select appid, code, name, url from hall_games where code=:code

测试机上一运行就出现这个异常。
org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected 0 or 1, actual 8: com.xiaonei.in.dao.LoginUserDAO#getHallGameInfo

于是查看文档。
Data access exception thrown when a result was not of the expected size, for example when expecting a single row but getting 0 or more than 1 rows。
当期望返回的结果记录是1时,如果返回值为0或者>1会抛此异常。于是修改sql,当需要一条数据的时候,还是老实加上limit 1比较好。

select appid, code, name, url from hall_games where code=:code limit 1

HttpClient模拟操作https网站

在日常工作中,我们常常需要抓取一些网站的信息为自己所用,比如自动抓取天气预报啊,抓取sina的股票数据,某些图片网站的美图等等,这就需要spider出场了,其实每种语言都有自己的写法,比如在Linux下,第一眼想到的就是wget,这是个强大的工具,可以递归地抓取网站,有windows下的版本,在本文章中还是说下Java的方式,那就是著名的开源软件HttpClient,在使用HttpClient的时候,我们需要知道网站的运行方式,可能在中间跳转了好几次,为了看到这些信息,我们还需要一点工具,Firefox的话直接中Httpfox就可以了,在IE下可以使用Httpwatch,官方下载地址是这个http://www.httpwatch.com,具体的使用很简单,不会的可以参考这个:http://www.cnblogs.com/mayingbao/archive/2007/11/30/978530.html
完事具备了,我们来看看怎么使用Httpclient

public static HttpMethod loginXN() throws IOException {
                PostMethod post = new PostMethod("http://passport.renren.com/PLogin.do");
		NameValuePair user = new NameValuePair("email",
				"zhangsan@hotmail.com");
		NameValuePair pwd = new NameValuePair("password", "12345");
		NameValuePair domain = new NameValuePair("domain", "renren.com");
		post.setRequestBody(new NameValuePair[] { user, pwd, domain});
		return post;
}

如果是https的请求,略微麻烦一点,需要使用SSLProtocolSocketFactory,在new HttpClient实例的时候,需要指定一下。

public static String httpGet(String url,Header header) {
        Protocol myhttps = new Protocol("https", new SSLProtocolSocketFactory(), 443);
        String result = "";
        HttpClient httpClient = new HttpClient();
        GetMethod getMethod = new GetMethod(url);
        getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                new DefaultHttpMethodRetryHandler());
        int statusCode = 1000;//默认代码Connection refused
        try {
            httpClient.getHostConfiguration().setHost("passport.dudusp.com",443,myhttps);
            getMethod.addRequestHeader(header);            
            statusCode = httpClient.executeMethod(getMethod);//执行getMethod
            if (statusCode == HttpStatus.SC_OK) {
                result = getMethod.getResponseBodyAsString();
            }
        } catch (Exception e) {
            log.info(e.getMessage(), e);
        } finally {
            getMethod.releaseConnection(); //释放连接
        }
        return result;
    }

公司的passport验证原理

最近为了安全考虑,出了套验证机制。passport处理未登录用户的过程大致为:
1.用户访问应用服务器A
2.如果用户未登录A,自动重定向到passport服务器
3.passport验证登录成功后,返回验证票
4.应用服务器A使用验证票到passport服务器进行二次验证
5.passport二次验证成功后,返回用户名

根据上述处理过程,passport的使用方法为:
1.passport的请求地址为:passport.no.xxxx.com;
2.应用服务器请求passport页面时,需提供forward跳转参数;
3.passport接收请求,验证正确的情况下,返回验证票(ticket参数);
4.应用服务器二次验证的过程为:
1)构造HTTP REFERER请求头;
2)携带HTTP REFERER请求头,远程读取passport.no.xxxx.com/verify.php?t=$ticket(该$ticket为 passport返回的ticket)地址的内容;
3)如果passport验证成功,返回用户名,否则返回空串。

passport的使用示例如下(以MM系统为例http://mm.renren.com/index.php):
1.MM请求访问passport的地址:https://passport.no.xxxx.com/login.php?forward=http://mm.renren.com/index.php
2.passport验证成功后,将地址重定向到http://mm.renren.com/index.php/index.php?ticket=nscr3omh80rn367h5pu0dq76u0
3.MM系统处理passport验证票($ticket参数)的过程为:

   function auth_check_valid($ticket){
       if($ticket == "")
           return false;

       $opts=array(
           'http'=>array(
               'header'=>"Referer :".$_SERVER['REQUEST_URI']
           )
       );

       $context = stream_context_create($opts);//构造HTTP REFERER头   
       $url = "https://passport.no.xxxx.com/verify.php?t=".$ticket;
       $user_id = file_get_contents($url,false,$context);//二次验证,远程请求用户名       
       return $user_id;//返回登录用户名,需进行后续判断是否为空串
   }

在接下来的文章中,我们会进一步讲解如果使用程序模拟登录验证系统,该怎么处理。