关于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 ,如需转载请自行联系原作者

相关文章
|
3月前
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
|
19小时前
【Azure App Service】PowerShell脚本批量添加IP地址到Web App允许访问IP列表中
Web App取消公网访问后,只允许特定IP能访问Web App。需要写一下段PowerShell脚本,批量添加IP到Web App的允许访问IP列表里!
|
3月前
|
关系型数据库 MySQL Linux
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
|
3月前
|
Shell PHP Windows
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
|
3月前
|
Linux 应用服务中间件 网络安全
【Azure 应用服务】查看App Service for Linux上部署PHP 7.4 和 8.0时,所使用的WEB服务器是什么?
【Azure 应用服务】查看App Service for Linux上部署PHP 7.4 和 8.0时,所使用的WEB服务器是什么?
|
3月前
【Azure 应用服务】通过 Web.config 开启 dotnet 应用的 stdoutLog 日志,查看App Service 产生500错误的原因
【Azure 应用服务】通过 Web.config 开启 dotnet 应用的 stdoutLog 日志,查看App Service 产生500错误的原因
|
3月前
|
Linux Python
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
|
3月前
|
存储 安全 网络安全
【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
|
3月前
|
存储 Linux 网络安全
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
|
3月前
【Azure 云服务】Azure Cloud Service 为 Web Role(IIS Host)增加自定义字段 (把HTTP Request Header中的User-Agent字段增加到IIS输出日志中)
【Azure 云服务】Azure Cloud Service 为 Web Role(IIS Host)增加自定义字段 (把HTTP Request Header中的User-Agent字段增加到IIS输出日志中)