关于WEB Service&WCF&WebApi实现身份验证之WEB Service篇

简介:

在这个WEB API横行的时代,讲WEB Service技术却实显得有些过时了,过时的技术并不代表无用武之地,有些地方也还是可以继续用他的,我之所以会讲解WEB Service,源于我最近面试时被问到相关问题,我这里只是重新复习一下并总结一下,给新手们指指路,大牛们可以无视之,当然不足之处还请大家指教,谢谢!

WEB Service身份验证,网上已有许多的相关文章,总结起来有:基于自定义SoapHeader验证、Form验证、集成Windows身份验证、服务方法加入一个或几个验证参数;下面就不废话了,直接分享实现的代码吧,中间有涉及注意的地方,我会有说明文字的。

1.基于自定义SoapHeader验证

定义服务:(注意UserValidationSoapHeader必需有无参构造函数,否则无法序列化)

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
//UserValidationSoapHeader:
 
     public  class  UserValidationSoapHeader : SoapHeader
     {
         public  string  UserName {  get set ; }
 
         public  string  Password {  get set ; }
 
         public  UserValidationSoapHeader()
         { }
 
         public  bool  IsValid()
         {
             if  ( string .IsNullOrEmpty(UserName) ||  string .IsNullOrEmpty(Password))
             {
                 throw  new  Exception( "用户名及密码不能为空!" );
             }
 
             if  (! string .Equals(UserName,  "admin" ) || ! string .Equals(Password,  "123456" ))
             {
                 throw  new  Exception( "用户名或密码不正确!" );
             }
             return  true ;
         }
 
 
     }
 
 
//SoapHeaderWebService:
     [WebService(Namespace =  "http://www.zuowenjun.cn" )]
     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
     [System.ComponentModel.ToolboxItem( false )]
     // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
     [System.Web.Script.Services.ScriptService]
     public  class  SoapHeaderWebService : System.Web.Services.WebService
     {
         public  UserValidationSoapHeader userValidation;
 
         [WebMethod(Description= "问候" )]
         [SoapHeader( "userValidation" )]
         public  string  HelloTo( string  name)
         {
             if  (userValidation.IsValid() && ! string .IsNullOrEmpty(name))
             {
                 return  "Hello "  + name;
             }
             return  "Hello World!" ;
         }
     }

客户端先通过服务地址引用服务,服务引用有两种,分为服务引用和WEB服务引用(后面涉及到服务引用的不再重述)

服务引用:(这里是按照WCF的模式来生成WEB服务代理类)

WEB服务引用:

    

(如果通过项目右键,则可以直接点击“添加WEB引用”到此画面)

客户端使用如下:

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
private  void  CallWebServiceFromWebReference()
{
     try
     {
         var  svc =  new  RefWebSoapHeaderWebService.SoapHeaderWebService();
         svc.UserValidationSoapHeaderValue =  new  RefWebSoapHeaderWebService.UserValidationSoapHeader() { UserName =  "admin" , Password =  "123456"  };
         string  result = svc.HelloTo(TextBox1.Text);
         Label1.Text = result;
     }
     catch  (Exception ex)
     {
         Label1.Text = ex.Message;
     }
}
 
private  void  CallWebServiceFromServiceReference()
{
     try
     {
         var  svc =  new  RefSoapHeaderWebService.SoapHeaderWebServiceSoapClient();
         var  header =  new  RefSoapHeaderWebService.UserValidationSoapHeader();
         header.UserName =  "admin" ;
         header.Password =  "123456" ;
         string  result = svc.HelloTo(header, TextBox1.Text);
         Label1.Text = result;
     }
     catch  (Exception ex)
     {
         Label1.Text = ex.Message;
     }
}
 
 
protected  void  Button1_Click( object  sender, EventArgs e)
{
     if  (RadioButtonList1.SelectedValue ==  "1" )
     {
         CallWebServiceFromServiceReference();
     }
     else
     {
         CallWebServiceFromWebReference();
     }
}

上述代码我针对两种WEB服务引用作了不同的使用实例,关键看方法调用,服务引用下生成的服务方法参数中会自动加入一个soapHeader的参数,而WEB服务引用则没有,我感觉采用WEB服务引用基于这种验证比较方便,因为只需将soapHeader实例赋值一次就可以多次调用不同的服务方法。

2.Form验证

定义服务:

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
//专门用于登录的WEB服务:
     [WebService(Namespace =  "http://www.zuowenjun.cn" )]
     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
     [System.ComponentModel.ToolboxItem( false )]
     // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
     [System.Web.Script.Services.ScriptService]
     public  class  FormAuthWebService : System.Web.Services.WebService
     {
 
         [WebMethod]
         public  bool  Login( string  username,  string  password)
         {
             return  UserBusiness.Login(username, password);
         }
 
     }
 
 
//正常的WEB服务:
     [WebService(Namespace =  "http://www.zuowenjun.cn" )]
     [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
     [System.ComponentModel.ToolboxItem( false )]
     // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。
     [System.Web.Script.Services.ScriptService]
     public  class  FormAuthHelloWebService : System.Web.Services.WebService
     {
 
         [WebMethod]
         public  string  HelloTo( string  name)
         {
             if  (! string .IsNullOrEmpty(name))
             {
                 return  "Hello "  + name;
             }
             return  "Hello World" ;
         }
 
         [WebMethod]
         public  void  Logout()
         {
             UserBusiness.Logout();
         }
     }
 
 
//业务辅助类:
     public  static  class  UserBusiness
     {
         public  static  bool  Login( string  userName,  string  password)
         {
             if  ( string .IsNullOrEmpty(userName) ||  string .IsNullOrEmpty(password))
             {
                 throw  new  Exception( "用户名及密码不能为空!" );
             }
 
             if  (! string .Equals(userName,  "admin" ) || ! string .Equals(password,  "123456" ))
             {
                 throw  new  Exception( "用户名或密码不正确!" );
             }
 
             SaveLoginStauts(userName);
             return  true ;
         }
 
         public  static  bool  IsAuthenticated()
         {
             return  HttpContext.Current.Request.IsAuthenticated;
         }
 
         public  static  void  Logout()
         {
             System.Web.Security.FormsAuthentication.SignOut();
         }
 
         private  static  void  SaveLoginStauts( string  userName)
         {
             System.Web.Security.FormsAuthentication.SetAuthCookie(userName,  false );
         }
 
 
     }

从上面的代码可以看出,这里采用了基于ASP.NET FORM验证来保存登录状态,其它代码与正常无异

因为采用了ASP.NET FORM验证,所以web.config需要加入以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//在system.web节点加入以下配置,因为我将WEB服务全部放到了Services目录下,所以仅对该目录进行了限制
 
     < authentication  mode="Forms">
       < forms  name="auth.webservice" loginUrl="~/Services/FormAuthWebService.asmx/Login">
       </ forms >
     </ authentication >
     < webServices >
       < protocols >
         < add  name="HttpSoap"/>
         < add  name="HttpPost"/>
         < add  name="HttpGet"/>
         < add  name="Documentation"/>
       </ protocols >
     </ webServices >
   </ system.web >
   < location  path="Services">
     < system.web >
       < authorization >
         < deny  users="?"/>
       </ authorization >
     </ system.web >
   </ location >

注意以下2点:

1.上面loginUrl="~/Services/FormAuthWebService.asmx/Login",这里我直接将未登录直接转到Login,原因是如果不加Login,那么你将无法在WEB浏览器上进行登录调试及其它服务方法的调试。

2.由于采用了FORM验证,且禁止匿名访问,造成无法正常引用服务,原因是服务引用也是一次HTTP请求,默认是未登录的情况,你去引用受限的服务地址,它就会自动跳转到登录的服务地址,所以针对这个问题,我采用了两种办法,第一种是:先不要禁止匿名访问,当服务引用成功后,再加上该配置,第二种是:不直接引用服务,采用代码动态请求服务方法来进行相关的调用,客户端使用代码如下:

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
//第一种:采用服务引用:
         protected  void  Button2_Click( object  sender, EventArgs e)
         {
             try
             {
                 CookieContainer cookieContainer =  new  CookieContainer();
 
                 var  svc =  new  RefFormAuthLoginWebService.FormAuthWebService();
                 svc.CookieContainer = cookieContainer;  //共享cookie容器
                 if  (svc.Login( "admin" "123456" ))
                 {
                     var  svc2 =  new  RefFormAuthWebService.FormAuthHelloWebService();
                     svc2.CookieContainer = cookieContainer; //共享cookie容器
                     Label2.Text = svc2.HelloTo(TextBox2.Text);
                 }
 
             }
             catch  (Exception ex)
             {
                 Label2.Text = ex.Message;
             }
         }
 
//第二种:采用动态请求调用:
         protected  void  Button2_Click( object  sender, EventArgs e)
         {
             try
             {
                 CookieContainer cookieContainer =  new  CookieContainer();
 
                 var  result = CallWebServiceFromHttpWebRequest( "FormAuthWebService.asmx/Login" "username=admin&password=123456" , cookieContainer);
                 Label2.Text = result.SelectSingleNode( "//ns:boolean" , GetXmlNamespaceManager(result.NameTable)).InnerText;
 
                 var  result2 = CallWebServiceFromHttpWebRequest( "FormAuthHelloWebService.asmx/HelloTo" "name="  + HttpUtility.UrlEncode(TextBox2.Text, Encoding.UTF8), cookieContainer);
                 Label2.Text = result2.SelectSingleNode( "//ns:string" , GetXmlNamespaceManager(result2.NameTable)).InnerText;
             catch  (Exception ex)
             {
                 Label2.Text = ex.Message;
             }

辅助方法定义如下:

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
private  XmlDocument CallWebServiceFromHttpWebRequest( string  serviceUrl, string  serviceParams,CookieContainer cookieContainer)
{
     HttpWebRequest request =(HttpWebRequest)WebRequest.Create( "http://localhost:8768/Services/"  + serviceUrl);
     request.Method =  "POST" ;
     request.ContentType =  "application/x-www-form-urlencoded" ;
     request.Credentials = CredentialCache.DefaultCredentials;
     byte [] data = Encoding.UTF8.GetBytes(serviceParams); //参数
     request.ContentLength = data.Length;
     request.CookieContainer = cookieContainer;  //共享cookie容器
     Stream writer = request.GetRequestStream();
     writer.Write(data, 0, data.Length);
     writer.Close();
 
     WebResponse response = request.GetResponse();
     StreamReader sr =  new  StreamReader(response.GetResponseStream(), Encoding.UTF8);
     String retXml =sr.ReadToEnd();
     sr.Close();
     XmlDocument doc =  new  XmlDocument();
     doc.LoadXml(retXml);
     return  doc;
}
 
 
private  XmlNamespaceManager GetXmlNamespaceManager(XmlNameTable table)
{
     XmlNamespaceManager nsMgr =  new  XmlNamespaceManager(table);
     nsMgr.AddNamespace( "ns" "http://www.zuowenjun.cn" );
     return  nsMgr;
}

这里说明一下,不论是采用服务引用还是动态调用服务,均需确保引用同一个COOKIE容器,而且在动态调用服务时,还需注意最终返回的结果为XML,需要进行相应的解析才能得到指定的值,相关的注意事项,可参见这篇文章《C#操作xml SelectNodes,SelectSingleNode总是返回NULL 与 xPath 介绍》,其实看了过后就知道是XML命名空间的问题,也可以去掉命名空间,这样解析就方便多了,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//这里是得到的XML,调用去除命名空间的方法:
  String retXml = RemoveNamespace(sr.ReadToEnd());
 
private  string  RemoveNamespace( string  xml)
         {
             var  reg =  new  System.Text.RegularExpressions.Regex( "xmlns=\".*\"" , System.Text.RegularExpressions.RegexOptions.IgnoreCase);
             return  reg.Replace(xml,  "" );
         }
 
 
protected  void  Button2_Click( object  sender, EventArgs e)
{
 
...
 
                 var  result = CallWebServiceFromHttpWebRequest( "FormAuthWebService.asmx/Login" "username=admin&password=123456" , cookieContainer);
                 Label2.Text = result.SelectSingleNode( "//boolean" ).InnerText;
 
                 var  result2 = CallWebServiceFromHttpWebRequest( "FormAuthHelloWebService.asmx/HelloTo" "name="  + HttpUtility.UrlEncode(TextBox2.Text, Encoding.UTF8), cookieContainer);
                 Label2.Text = result2.SelectSingleNode( "//string" ).InnerText;
...
 
}

3.集成Windows身份验证,这个很简单,首先将IIS设置成集成WINDOWS身份验证,服务定义无特殊代码

客户端使用如下:

1
2
3
Test.WebReference.Service1 service=  new  Test.WebReference.Service1();  //生成web service实例  
service.Credentials =  new  NetworkCredential( "user" , "123456" );  //user是用户名,该用户需要有一定的权限  
lblTest.Text =service.HelloTo( "zuowenjun" ) ;  //调用web service方法 

WEB页面示例代码:

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
< form  id="form1" runat="server">
  < fieldset >
  < legend >基于自定义SoapHeader验证后调用服务</ legend >
  < div >
      < asp:RadioButtonList  ID="RadioButtonList1" runat="server" RepeatDirection="Horizontal">
      < asp:ListItem  Value="1">From Service Reference</ asp:ListItem >
      < asp:ListItem  Value="2">From Web Reference</ asp:ListItem >
      </ asp:RadioButtonList >
      < asp:TextBox  ID="TextBox1" runat="server"></ asp:TextBox >
      < asp:Button  ID="Button1" runat="server" Text="Button" onclick="Button1_Click" />
      < br  />
      < asp:Label  ID="Label1" runat="server" Text="Label"></ asp:Label >
  </ div >
  </ fieldset >
  < p >
       </ p >
  < p >
       </ p >
  < p >
       </ p >
   < fieldset >
  < legend >基于自定义Form身份验证后调用服务</ legend >
  < div >
      < asp:TextBox  ID="TextBox2" runat="server"></ asp:TextBox >
      < asp:Button  ID="Button2" runat="server" onclick="Button2_Click" Text="Button" />
      < br  />
      < asp:Label  ID="Label2" runat="server" Text="Label"></ asp:Label >
  </ div >
  </ fieldset >
< p >
       </ p >
  < p >
       </ p >
  < p >
       </ p >
   < fieldset >
  < legend >基于自定义Windows验证后调用服务</ legend >
  < div >
      < asp:TextBox  ID="TextBox3" runat="server"></ asp:TextBox >
      < asp:Button  ID="Button3" runat="server"  Text="Button"
          onclick="Button3_Click" />
      < br  />
      < asp:Label  ID="Label3" runat="server" Text="Label"></ asp:Label >
  </ div >
  </ fieldset >
  </ form >

最终呈现效果截图如下:

 

 
由于下班时间写的,所以有些仓促,不足之处,敬请指出,谢谢!~v~

补充知识点:

我上面的代码在验证用户登录时,若验证不通过,我是直接抛出一个Exception,WEB服务在返回到客户端前会被包装为SoapException类型的异常,这样导至在客户端无法很直接的查看错误内容,所以这里可以采用这篇文章《封装SoapException处理Webservice异常》里面的方法来添加格式化SoapException的方法及解析SoapException的方法。

关于WEB SERIVCE在多个ASP.NET页面中使用时共享COOKIE的办法,实现代码如下:

首先正常引用WEB服务,然后自定义继承自该引用的服务类,并在其构造函数中实例化CookieContainer并保存到静态字段中;

使用的时,不要调用引用的服务,而应该调用这些自定义的子类,从而实现共享COOKIE

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
public  class  FormAuthHelloWebServiceEx : RefFormAuthWebService.FormAuthHelloWebService
{
     private  static  CookieContainer cookieContainer;
 
     static  FormAuthHelloWebServiceEx()
     {
         cookieContainer =  new  System.Net.CookieContainer();
     }
 
     public  FormAuthHelloWebServiceEx(CookieContainer ckContainer)
     {
         if  (cookieContainer !=  null )
         {
             cookieContainer = ckContainer;
         }
         this .CookieContainer = cookieContainer;
     }
}
 
public  class  FormAuthWebServiceEx : RefFormAuthLoginWebService.FormAuthWebService
{
     private  static  CookieContainer cookieContainer;
 
     static  FormAuthWebServiceEx()
     {
         cookieContainer =  new  System.Net.CookieContainer();
     }
 
     public  FormAuthWebServiceEx(CookieContainer ckContainer)
     {
         if  (cookieContainer !=  null )
         {
             cookieContainer = ckContainer;
         }
         this .CookieContainer = cookieContainer;
     }
}

 

 


本文转自 梦在旅途 博客园博客,原文链接: http://www.cnblogs.com/zuowj/p/4981919.html ,如需转载请自行联系原作者

相关文章
在 CRM WebClient UI Attachment 区域,创建支持 Web Service 的 Word 文档
在 CRM WebClient UI Attachment 区域,创建支持 Web Service 的 Word 文档
|
12天前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第4天】JavaScript的Web Workers和Service Worker增强了Web性能。Web Workers处理后台多线程,减轻主线程负担,但通信有开销,受同源策略限制。Service Worker则用于离线缓存和推送通知,需管理其生命周期、更新策略,并确保安全。两者都带来了挑战,但也极大提升了用户体验。通过理解和优化,开发者能构建更高效、安全的Web应用。
|
6天前
|
缓存 前端开发 JavaScript
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第10天】在Web开发中,Web Workers和Service Worker提升性能。Workers运行后台任务,防止界面冻结。Web Workers处理计算密集型任务,Service Worker则缓存资源实现离线支持。常见问题包括通信故障、资源限制、注册错误及缓存更新。通过示例代码展示了两者用法,并强调生命周期管理和错误处理的重要性。善用这些技术,可构建高性能的Web应用。
|
10天前
|
缓存 JavaScript 前端开发
JavaScript进阶 - Web Workers与Service Worker
【7月更文挑战第6天】JavaScript的Web Workers和Service Worker增强了浏览器的性能处理和离线功能。Web Workers处理后台计算,减轻主线程压力,但通信有开销,受同源策略限制。Service Worker则能拦截网络请求,支持离线缓存和推送通知,但其生命周期和权限管理需谨慎处理。通过理解它们的工作原理和限制,开发者能创建更流畅、更健壮的Web应用。
|
1月前
|
JavaScript 前端开发 定位技术
Rest风格WEB服务(Rest Style Web Service)的真相
Rest风格WEB服务(Rest Style Web Service)的真相
46 1
|
2月前
|
XML API 网络架构
Web Service和Web API理解和使用场景
**Web Service**是一种基于网络、使用SOAP协议和XML的数据封装的重服务,适用于跨平台、跨语言的企业系统集成,尤其在安全性和事务处理严格的场景,如银行系统。而**Web API**是轻量级的HTTP接口,常遵循REST原则,使用JSON格式,适合移动应用、开放平台和微服务间的通信,因其简洁高效。选择哪种取决于项目需求,Web Service适合复杂交互,Web API则流行于现代Web应用。
|
2月前
|
缓存 应用服务中间件 数据库
Python Web Service开发及优化
随着互联网的快速发展,Web服务已成为现代技术的核心。Python作为一种功能强大且易于学习的编程语言,在Web服务开发领域占据着重要地位。Python Web服务开发的重要性在于它能够提供高效、可扩展且易于维护的解决方案。本篇博客将探讨如何使用Python的Flask框架、Gunicorn WSGI服务器和Nginx网页服务器来实现高性能的Web服务。
|
2月前
|
JSON 安全 API
【专栏】四种REST API身份验证方法:基本认证、OAuth、JSON Web Token(JWT)和API密钥
【4月更文挑战第28天】本文探讨了四种REST API身份验证方法:基本认证、OAuth、JSON Web Token(JWT)和API密钥。基本认证简单但不安全;OAuth适用于授权第三方应用;JWT提供安全的身份验证信息传递;API密钥适合内部使用。选择方法时需平衡安全性、用户体验和开发复杂性。
|
9月前
|
JSON 安全 API
使用 ABAP sproxy 事务码生成的 Proxy 消费 Web Service
使用 ABAP sproxy 事务码生成的 Proxy 消费 Web Service
|
2月前
|
XML 网络架构 数据格式
Ruby 教程 之 Ruby Web Service 应用 - SOAP4R 2
Ruby Web Service 应用 - SOAP4R
41 5