httpclient的webservice客户端与session验证

httpclient的webservice客户端与session验证
2013-08-17 23:46:22
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://observer.blog.51cto.com/4267416/1275608

   欢迎来到:http://observer.blog.51cto.com    

   webservcie是可以跨语言跨平台开发的一种技术,各种计算机语言都可以搭建服务器,同时各种计算机语言也可以开发客户端。只要有服务器,不管是用java还是C++抑或是php搭建的,其他语言都可以根据其开放的wsdl开发好客户端,然后调用其方法就像调用本地本项目的方法一样。
   本文使用httpclient开发客户端,服务器例子依赖于文章:http://observer.blog.51cto.com/4267416/1231205,如果有不明白的可以先看这些文章再看本文。本文在原来例子的基础上加上安全验证与session的使用与权限管理
   webservice的安全验证有很多方法,实现soapheader认证、集成windows身份验证、ip绑定验证等等。在此博主觉得,基础的,往往却是最有效的。本文使用过滤器验证登录身份,然后就像平时web登录后一样,在session中存放信息达到保持登录与权限的管理
第一:编写webservice客户端模板
   首先咱们使用httpclient写一个能够访问webservice的客户端,详情可下载源码查看。
   在此,因为wsdl、url、encoding还有就是返回的字段名在后面修改的可能性很大,所以使用配置文件存放他们的信息。

   webservicetest.properties:

1
2
3
4
webservice.wsdl=http://localhost:8080/webServiceCXF/services/Service?wsdl
     webservice.url=http://www.observer.com/service
     webservice.encoding=utf-8
     webservice.fieldname=result

   然后包装一个类读取他们:com.observer.webservcie.util.ServicePropertiesUtil详情可以下载代码查看,咱们说正点。
   在我的上一篇文章:http://observer.blog.51cto.com/4267416/1244274中,介绍了js的webservice客户端的相关内容,其中XMLHttpRequest所起到的作用其实就相当于浏览器访问一个web服务器的作用一样,只不过它访问的时候向服务器推送了一段xml,并且访问的是webservice的url而已。
   既然知道了这个原理,那么跟他一样在java中能够起到浏览器作用的httpclient能不能访问webservice的url进而访问webservice的方法呢?咱们做一下尝试,拭目以待。
   因为后面加上session验证之后,客户端封装就要有所变化,所以在此先将客户端模板写出来,其中发送的xml模板完全可以使用soupui自动生成,soupri的使用参看介绍js的那篇文章。
   TestWebServiceClient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public  class  TestWebServiceClient {
         public  static  void  main(String[] args)  throws  FormatException {
             ServicePropertiesUtil servicePropertiesUtil =  new  ServicePropertiesUtil( "webservicetest" );
             //获取读取配置文件的ServicePropertiesUtil类
             StringBuffer sb =  new  StringBuffer();
             sb.append( "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:ser='" +
                     servicePropertiesUtil.getWebServiceUrl()+ "'>" );
             sb.append( "<soapenv:Header/>" );
             sb.append( "<soapenv:Body>" );
             sb.append( "<ser:getGradeName>" );
             sb.append( "<toid>" ); sb.append( 123 ); sb.append( "</toid>" );
             sb.append( "</ser:getGradeName>" );
             sb.append( "</soapenv:Body>" );
             sb.append( "</soapenv:Envelope>" );
             //访问webservcie时需要用到的xml
                                                          
             HttpClient httpClient =  new  HttpClient(); //新建一个HttpClient类
             PostMethod postMethod= new  PostMethod(servicePropertiesUtil.getWebServiceWsdl());
             postMethod.setRequestHeader( "SOAPAction" , "http://dao.wfservice.ws.emolay.com" );
             String html =  null ;
             try  {
                 String out = sb.toString();
                 postMethod.setRequestEntity( new  StringRequestEntity(out,  "text/xml" ,
                         servicePropertiesUtil.getWebServiceEncoding()));
                 //设置需要推送的数据
                 httpClient.executeMethod(postMethod); //访问webservcie
                 html=postMethod.getResponseBodyAsString(); //获取访问webserivce反馈的xml
                 postMethod.releaseConnection();
             catch  (HttpException e) {
                 e.printStackTrace();
                 throw  new  FormatException( "连接异常1" "服务器正在维护中,请稍后再试,或者联系服务器维护人员进行处理" );
             catch  (IOException e) {
                 throw  new  FormatException( "连接异常2" "服务器正在维护中,请稍后再试,或者联系服务器维护人员进行处理" );
             }
                                                          
             Document doc=Jsoup.parse(html); //使用jsoup解析xml,得到webservice返回的数据
             Elements elements=doc.select(servicePropertiesUtil.getWebServiceFieldname());
             //根据webservcie的配置,解析xml中的数据
             String [] datas =  new  String[elements.size()];
             int  i =  0 ;
             for (Element ele:elements){
                 datas[i] = ele.text();
                 i++;
             }
                                                          
             //输出数据 
             for (String d : datas){
                 System.out.println(d);
             }
         }
     }

   技术上的东西在注释已经写的很清楚了,这里就不多说了,服务器使用依赖的服务器webServiceCXF,部署服务器到tomcat后,运行这里的main方法,的到结果:“is succeed”;证明客户端代码编写正确,同时也证明了,httpclient是可以访问webservice的
第二:改造服务器
   首先咱们将服务器改个名,改为:webServiceCXFSession
   在此,也许会有人想,如果在webservice中定义一个登录的方法,webserivce访问这个方法的时候就必须不能被拦截,但是过滤器中是不可能知道你的webservice是要访问什么方法的,只有到了webservice解析完传过来的xml才能知,这时候就矛盾了,怎么办呢?在此,博主起初是想在传过来的头文件中加上一个参数(如islogin),然后在过滤器中得到这个头文件信息,然后根据这个头文件信息进行判断是否放行。但是这时候又出现问题了,要是别人重写了客户端,把所有的方法传过来的这个islogin全部都改为true,其他都不变,那也可以不用验证就进入webservcie。最后,博主觉得,直接在过滤器中进行判断登录即可,而登录的用户名密码可以使用头文件的形式传过来。
   好了,登录的问题解决了,使用过滤器登录了之后,我们就可以在islogin为false时判断session中是否已经登录了,session中也的确放进了用户信息,但是要怎样才能在webservice的方法中的到session中的信息,进而判断其权限呢?在此就要用到博主的上一篇文章中提到的一个类了,该类为:java.lang.ThreadLocal,简单的解释就是:每个不同线程访问的都是不同的副本(原理上是否这样暂且不管,只要知道达到了这个效果就成),详情可看API或上一篇文章:http://observer.blog.51cto.com/4267416/1263577;我们在访问webservice的过程其实就像访问servlet,以多线程的方式访问,但是一旦进到了服务器,那么访问过滤器与访问webservice的是同一个线程。那么好,只要我们在webservcie实现类中定义一个ThreadLocal,其中放着session,然后在webservcie进入与出来时把session放进与移除,那么在不同用户访问webservcie都可以得到不同的自己的session,然后在方法里面判断其权限即可,如下所示
   GradeServiceImpl中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private  static  final  ThreadLocal<HttpSession> threadLocal =
         new  ThreadLocal<HttpSession>();
     public  static  void  setHttpSession(HttpSession session){
         threadLocal.set(session);
     }
     public  static  void  removeSession(){
         threadLocal.remove();
     }
     /**
      * 验证权限,如果不是admin用户,返回状态码"-1"表示没有权限访问该方法
      */
     public  String getGradeName( long  toid) {
         HttpSession session = threadLocal.get();
         String user = (String)session.getAttribute( "user" );
         if ( "admin" .equals(user)){
             System.out.println( "业务逻辑" );
         } else {
             return  "-1" ;
         }
         return  "is succeed" ;
     }

   然后在web.xml中添加过滤器:

1
2
3
4
5
6
7
8
9
10
<!-- webserivce过滤器 -->
< filter >
     < filter-name >webserviceFilter</ filter-name >
     < filter-class >com.observer.cxf.login.filter.LoginFilter</ filter-class >
</ filter >
< filter-mapping >
     < filter-name >webserviceFilter</ filter-name >
     < url-pattern >/services/*</ url-pattern >
</ filter-mapping >
<!-- webserivce过滤器 end -->

   新建过滤器的实现类:com.observer.cxf.login.filter.LoginFilter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public  class  LoginFilter  implements  Filter{
     public  void  destroy() {
     }
     /**
      * 过滤器的过滤方法
      * 当传过来的html头文件中有islogin并且为true时表示用户需要登录,那么就验证登录
      * 当传过来的html头文件中有islogin并且为false时表示用户需要调用webservice,
      * 那么首先验证是否已经登录,如果已经登录就继续,如果没有登录就直接返回notlogin提示用户没有登录
      */
     public  void  doFilter(ServletRequest arg0, ServletResponse arg1,
             FilterChain arg2)  throws  IOException, ServletException {
         HttpServletRequest request = (HttpServletRequest)arg0;
         HttpServletResponse response = (HttpServletResponse)arg1;
         HttpSession session = request.getSession();
         String islogin = request.getHeader( "islogin" );
         if ( "false" .equals(islogin)){ //判断是要登录还是要访问webservice
             String user = (String) session.getAttribute( "user" );
             if (user !=  null ){ //判断是否已经登录
                 GradeServiceImpl.setHttpSession(session);
                 //访问webservice之前将session放到webservice实现类中
                 arg2.doFilter(request, response);
                 GradeServiceImpl.removeSession();
                 //webservice访问完成后移除里面的session
                 return ;
             } else {
                 PrintWriter out = response.getWriter();
                 out.println( "notlogin" );
                 out.flush();
                 out.close();
             }
         } else  if ( "true" .equals(islogin)){
             String username = request.getHeader( "username" );
             String password = request.getHeader( "password" );
             //的到头文件中传过来的username与password
             String user = LoginManger.login(username, password);
             PrintWriter out = response.getWriter();
             if (user!= null ){
                 session.setAttribute( "user" , user);
                 out.println( "islogin" );
             } else {
                 out.println( "notlogin" );
             }
             out.flush();
             out.close();
         }
     }
     public  void  init(FilterConfig arg0)  throws  ServletException {
     }
}

   以上代码中的LoginManger.login(username, password);是登录的业务逻辑管理类,一般要检查数据库,在此就不多做了,详情可下载源码看即可。
第三:封装客户端
   服务器已经写好了,究竟能不能用,写好客户端就知道了。
   在此,博主使用模板模式编写客户端,这样在我们的服务器修改了之后,只要大体上没改,我们就可以只修改实现类就可以了,当然,服务器方法添加了的话,抽象类还是要写的,一般情况下,服务器修改的就是一些配置文件上的东西,这时候,相应的xml就会有所改变,那么你就只要修改实现类就可以了。还有这里使用了可变长参数列表,这样如果服务器的webservice方法要是参数改变了的话,也只要修改实现类中的方法内容即可。
   上代码:
   抽象类com.observer.webservcie.httpclient.WebServiceClient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
public  abstract  class  WebServiceClient {
     public  static  ServicePropertiesUtil servicePropertiesUtil =  new  ServicePropertiesUtil( "webservice" );
     private  static  String tmpcookies= "" ;
     /**
      * 向webservcie发送请求
      * @param outXml 请求所需要推送的xml数据
      * @return 请求反馈的信息
      * @throws FormatException 当webservice服务器关闭或链接不可以等原因造成请求失败时,
      *  抛出com.observer.webservcie.error.FormatException
      */
     private  String executeMethod(String outXml)  throws  FormatException{
         HttpClient httpClient =  new  HttpClient();
         PostMethod postMethod= new  PostMethod(servicePropertiesUtil.getWebServiceWsdl());
         postMethod.setRequestHeader( "Cookie" , tmpcookies);
         postMethod.setRequestHeader( "islogin" , "false" );
         postMethod.setRequestHeader( "SOAPAction" , "http://dao.wfservice.ws.emolay.com" );
         String html =  null ;
         try  {
             String out = outXml;
             postMethod.setRequestEntity( new  StringRequestEntity(out,  "text/xml" ,
                     servicePropertiesUtil.getWebServiceEncoding()));
             httpClient.executeMethod(postMethod);
             html=postMethod.getResponseBodyAsString();
         catch  (HttpException e) {
             e.printStackTrace();
             throw  new  FormatException( "连接异常1" "服务器正在维护中,请稍后再试,或者联系服务器维护人员进行处理" );
         catch  (IOException e) {
             throw  new  FormatException( "连接异常2" "服务器正在维护中,请稍后再试,或者联系服务器维护人员进行处理" );
         } finally {
             postMethod.releaseConnection();
         }
         return  html;
     }
     /**
      * 向webservcie服务器发送登录请求
      * @param username 用户名
      * @param password 用户密码
      * @return 登录成功后返回服务器反馈的信息:islogin
      * @throws FormatException 当webservice服务器关闭或链接不可以等原因造成请求失败或登录失败时,
      *  抛出com.observer.webservcie.error.FormatException
      */
     public  String login(String username, String password)  throws  FormatException{
         HttpClient httpClient =  new  HttpClient();
         PostMethod postMethod= new  PostMethod(servicePropertiesUtil.getWebServiceWsdl());
         postMethod.setRequestHeader( "islogin" , "true" );
         postMethod.setRequestHeader( "username" ,username);
         postMethod.setRequestHeader( "password" ,password);
         String html =  null ;
         try  {
             httpClient.executeMethod(postMethod);
             html=postMethod.getResponseBodyAsString();
             if (html!= null  &&  "notlogin" .equals(html.trim())){
                 throw  new  FormatException( "登录失败" "用户名密码错误" );
             }
             Cookie[] cookies = httpClient.getState().getCookies();
             StringBuffer sb =  new  StringBuffer();
             for  (Cookie c : cookies) {
                 sb.append(c.toString() +  ";" );
             }
             synchronized ( this ){
                 tmpcookies = sb.toString();
             }
         catch  (HttpException e) {
             e.printStackTrace();
             throw  new  FormatException( "连接异常1" "服务器正在维护中,请稍后再试,或者联系服务器维护人员进行处理" );
         catch  (IOException e) {
             throw  new  FormatException( "连接异常2" "服务器正在维护中,请稍后再试,或者联系服务器维护人员进行处理" );
         } finally {
             postMethod.releaseConnection();
         }
         return  html;
     }
     /**
      * 将服务器反馈回来的xml解析出结果来
      * @param xml 服务器返回的xml
      * @return 解析出来的结果集
      */
     private  String[] parseData(String xml){
         Document doc=Jsoup.parse(xml);
         Elements elements=doc.select(servicePropertiesUtil.getWebServiceFieldname());
         String [] datas =  new  String[elements.size()];
         int  i =  0 ;
         for (Element ele:elements){
             datas[i] = ele.text();
             i++;
         }
         return  datas;
     }
     /**
      * 想webservice方式请求,并且解析webservice反馈的xml得到结果
      * @param outXml 需要请求发送的xml
      * @return 返回webservice反馈的xml解析之后的结果
      * @throws FormatException 当用户没有登录或者
      * 服务器已经关闭时抛出com.observer.webservcie.error.FormatException
      */
     public  String[] send(String outXml)  throws  FormatException{
         String xml = executeMethod(outXml);
         if ( "notlogin" .equals(xml.trim())){
             throw  new  FormatException( "登陆异常" "对不起您没有登录" );
         }
         return  parseData(xml);
     }
     /**
      * 获取webservice中getGradeName方法所需要的xml的抽象方法,该方法让子类重写,
      * 让其可以在服务器参数更改是灵活的更改
      * @param data 获取webservice中getGradeName所需要的参数
      * @return webservice中getGradeName方法所需要的xml
      * @throws FormatException 当参数不符合规则时
      * 抛出com.observer.webservcie.error.FormatException
      */
     public  abstract  String getGradeNameXml(String... data)  throws  FormatException;
     /**
      * 想webservice请求getGradeName方法的模板方法
      * @param data 获取webservice中getGradeName所需要的参数
      * @return 返回webservice的getGradeName方法返回的数据
      * @throws FormatException 当连接失败或者没有登录或者没有那个权限进行访问该方法时
      * 抛出com.observer.webservcie.error.FormatException
      */
     public  String[] getGradeName(String... data)  throws  FormatException{
         String outXml = getGradeNameXml(data);
         String[] datas = send(outXml);
         if (datas.length> 0  &&  "-1" .equals(datas[ 0 ])){
             throw  new  FormatException( "操作异常" "对不起你没有权限进行此操作" );
         }
         return  datas;
     }
}

   实现类:com.observer.webservcie.httpclient.impl.WebServiceClientImpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public  class  WebServiceClientImpl  extends  WebServiceClient {
         /**
          * 获取webservice中getGradeName方法所需要的xml的实现方法
          * @param data 获取webservice中getGradeName所需要的参数
          * @return webservice中getGradeName方法所需要的xml
          * @throws FormatException 当参数不符合规则时
          * 抛出com.observer.webservcie.error.FormatException
          */
         public  String getGradeNameXml(String... toid)  throws  FormatException{
             int  id;
             try {
                 id = Integer.valueOf(toid[ 0 ]);
             } catch  (Exception e) {
                 throw  new  FormatException( "参数错误" , "输入的参数必须为整数字符串" );
             }
             StringBuffer sb =  new  StringBuffer();
             sb.append( "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:ser='" +
                     servicePropertiesUtil.getWebServiceUrl()+ "'>" );
             sb.append( "<soapenv:Header/>" );
             sb.append( "<soapenv:Body>" );
             sb.append( "<ser:getGradeName>" );
             sb.append( "<toid>" ); sb.append(id); sb.append( "</toid>" );
             sb.append( "</ser:getGradeName>" );
             sb.append( "</soapenv:Body>" );
             sb.append( "</soapenv:Envelope>" );
             return  sb.toString();
         }
     }

   以上代码中,因为登录跟访问webservice是两个不同的与代码,所以写了两个连接webservice的方法,然后,因为不同的httpclient访问服务器都会产生不同的session,解决的办法就是将上一次访问的Cookie传到下一次访问的httpclient中,这样服务器就会认为你这一次访问的依然是同一个session;在此,用户登录过后,就会将cookie的有关信息存放在一个静态的变量(tmpcookies)中,然后如果下次要进行访问,就将这个变量的cookie放到httpclient的头文件中,让服务器知道你需要使用刚才的session进行访
第四:测试
   好了,服务器跟客户端都已经编写完毕,该测试一下了,先登录后访问,不登录就访问等等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public  class  TestClient {
     public  static  void  main(String[] args) {
         WebServiceClient webServiceClient =  new  WebServiceClientImpl();
         String[] datas1 =  null ;
         String[] datas2 =  null ;
         try  {
             String login = webServiceClient.login( "admin" "admin" );
             System.out.println(login);
             datas1 = webServiceClient.getGradeName( "123" );
             datas2 = webServiceClient.getGradeName( "2222" );
         catch  (FormatException e) {
             e.printStackTrace();
             System.out.println(e.getDetails());
             return ;
         }
         for (String data : datas1){
             System.out.println(data);
         }
         for (String data : datas2){
             System.out.println(data);
         }
     }
}

   在此,博主测试是通过的,如果哪位朋友修改客户端测出了bug,可以给在下个信息,咱们看看能否解决。
第五:打包客户端
   详细的打包客户端这里就不说了,因为在这一篇文章已经有了:http://observer.blog.51cto.com/4267416/1238972,不明白的可以参看它后面的打包测试。
   在这里要说明的是,因为这里的客户端用到了第三方依赖包,所以,你可以直接将依赖包一起打进自己的jar中,但是如果这样做的话,当别人导进你的客户端的同时也导进了依赖包的话就会出现冲突,所以,一般是写一个说明文档,说明该客户端依赖于什么什么第三方的包,想要使用此客户端必须在项目下导进第三方的包,然后将所需要的包列出来,在自己压缩出来的压缩包中也像其他如ssh的框架一样将依赖包也一起放进去。
   到了这里使用httpclient制作客户端与使用session进行用户的验证的方法就已经完成了,详细的可以下载附件代码查看。
   在此,博主是在maven的服务器的项目下进行改造的,因为使用jar包实在是太大,上传实在太慢了,所以有不会用maven的朋友,实在是抱歉了,不过相信能够做java到这一步的,看着本文都能够自己写出项目了,把项目中的代码跟配置移植到普通web项目应该更是没有问题的。

本文出自 “观察者*奔放” 博客,请务必保留此出处http://observer.blog.51cto.com/4267416/1275608

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值