C# 实现身份验证之WEB Service篇
在这个WEB API横行的时代,讲WEB Service技术却实显得有些过时了,过时的技术并不代表无用武之地,有些地方也还是可以继续用他的,我之所以会讲解WEB Service,源于我最近面试时被问到相关问题,我这里只是重新复习一下并总结一下,给新手们指指路,大牛们可以无视之,当然不足之处还请大家指教,谢谢!
WEB Service身份验证,网上已有许多的相关文章,总结起来有:基于自定义SoapHeader验证、Form验证、集成Windows身份验证、服务方法加入一个或几个验证参数;下面就不废话了,直接分享实现的代码吧,中间有涉及注意的地方,我会有说明文字的。
1.基于自定义SoapHeader验证
定义服务:(注意UserValidationSoapHeader必需有无参构造函数,否则无法序列化)
//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, ""))
{
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引用”到此画面)
客户端使用如下:
private void CallWebServiceFromWebReference()
{
try
{
var svc = new RefWebSoapHeaderWebService.SoapHeaderWebService();
svc.UserValidationSoapHeaderValue = new RefWebSoapHeaderWebService.UserValidationSoapHeader() { UserName = "admin", Password = "" };
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 = "";
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 == "")
{
CallWebServiceFromServiceReference();
}
else
{
CallWebServiceFromWebReference();
}
}
上述代码我针对两种WEB服务引用作了不同的使用实例,关键看方法调用,服务引用下生成的服务方法参数中会自动加入一个soapHeader的参数,而WEB服务引用则没有,我感觉采用WEB服务引用基于这种验证比较方便,因为只需将soapHeader实例赋值一次就可以多次调用不同的服务方法。
2.Form验证
定义服务:
//专门用于登录的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, ""))
{
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需要加入以下配置:
//在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请求,默认是未登录的情况,你去引用受限的服务地址,它就会自动跳转到登录的服务地址,所以针对这个问题,我采用了两种办法,第一种是:先不要禁止匿名访问,当服务引用成功后,再加上该配置,第二种是:不直接引用服务,采用代码动态请求服务方法来进行相关的调用,客户端使用代码如下:
//第一种:采用服务引用:
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", ""))
{
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;
}
}
辅助方法定义如下:
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, , 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命名空间的问题,也可以去掉命名空间,这样解析就方便多了,如下代码:
//这里是得到的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身份验证,服务定义无特殊代码
客户端使用如下:
Test.WebReference.Service1 service= new Test.WebReference.Service1(); //生成web service实例
service.Credentials = new NetworkCredential("user",""); //user是用户名,该用户需要有一定的权限
lblTest.Text =service.HelloTo("zuowenjun") ; //调用web service方法
WEB页面示例代码:
<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>
最终呈现效果截图如下:
补充知识点:
我上面的代码在验证用户登录时,若验证不通过,我是直接抛出一个Exception,WEB服务在返回到客户端前会被包装为SoapException类型的异常,这样导至在客户端无法很直接的查看错误内容,所以这里可以采用这篇文章《封装SoapException处理Webservice异常》里面的方法来添加格式化SoapException的方法及解析SoapException的方法。
关于WEB SERIVCE在多个ASP.NET页面中使用时共享COOKIE的办法,实现代码如下:
首先正常引用WEB服务,然后自定义继承自该引用的服务类,并在其构造函数中实例化CookieContainer并保存到静态字段中;
使用的时,不要调用引用的服务,而应该调用这些自定义的子类,从而实现共享COOKIE
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;
}
}
C# 实现身份验证之WEB Service篇的更多相关文章
- 关于WEB Service&WCF&WebApi实现身份验证之WEB Service篇
在这个WEB API横行的时代,讲WEB Service技术却实显得有些过时了,过时的技术并不代表无用武之地,有些地方也还是可以继续用他的,我之所以会讲解WEB Service,源于我最近面试时被问到 ...
- WinCE数据通讯之Web Service篇
准备写个WinCE平台与数据库服务器数据通讯交互方面的专题文章,今天先整理个Web Service通讯方式. 公司目前的硬件产品平台是WinCE5.0,数据通讯是连接服务器与终端的桥梁,关系着终端的数 ...
- Kerberos身份验证访问Web HttpFS
原文出处: https://www.ibm.com/support/knowledgecenter/en/SSPT3X_3.0.0/com.ibm.swg.im.infosphere.biginsig ...
- 关于WEB Service&WCF&WebApi实现身份验证之WebApi篇
之前先后总结并发表了关于WEB Service.WCF身份验证相关文章,如下: 关于WEB Service&WCF&WebApi实现身份验证之WEB Service篇. 关于WEB S ...
- WinCE数据通讯之Web Service分包传输篇
前面写过<WinCE数据通讯之Web Service篇>那篇对于数据量不是很大的情况下单包传输是可以了,但是对于大数据量的情况下WinCE终端的内存往往会在解包或者接受数据时产生内存溢出. ...
- 基于表单的身份验证(FBA)
https://technet.microsoft.com/zh-cn/library/ee806890(office.15).aspx http://www.tuicool.com/articles ...
- 黑客攻防技术宝典Web实战篇(一)Web应用程序技术基础
在开展Web应用程序渗透测试之前请先了解下面列出的这些内容,如果不是很懂的话,请读David Gourley & Brian Totty的HTTP权威指南也叫HTTP:The Definiti ...
- ASP.NET身份验证
Asp.net的身份验证有有三种,分别是"Windows | Forms | Passport",其中又以Forms验 证用的最多,也最灵活. Forms 验证方式对基于用户的验证 ...
- Asp.net 身份验证
Forms 验证方式对基于用户的验证授权提供了很好的支持,可以通过一个登录页面验证用户的身份,将此用户的身份发回到客户端的Cookie,之后此用户再访问这个 web应用就会连同这个身份Cookie一起 ...
随机推荐
- rand和randn
1,rand 生成均匀分布的伪随机数.分布在(0~1)之间 主要语法:rand(m,n)生成m行n列的均匀分布的伪随机数 rand(m,n,'double')生成指定精度的均匀分布的伪随机数,参数还可 ...
- hdu-6166(最短路+二进制分组)
题意:给你n个点m条边的有向图,然后再给你k个不同的点,问你这k个点的最小距离: 解题思路:这道题最需要注意的就是k个点一定是不同的,那么有一个结论就是任意两个不同的数字中,在他们的二进制地表示中,一 ...
- Tyche 2317 Color
题目大意:有三个人alice,bob,yazid,三种颜色red,blue,green,每个人对应一种颜色. [name] is [color]. Yazid会做以下操作: 1 将三个句子连在一起 2 ...
- 【数学建模】day01-线性规划问题
线性规划问题是在一组线性约束条件下,求线性目标函数最大/最小值的问题.这些约束条件有不等式约束.等式约束以及边界约束,这和中学讲的线性规划无异. 此类问题的MATLAB标准形式为: 其中,max问 ...
- DRF 版本和认证
Django Rest Framework 版本控制组件 DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢~~大家都知道我们开发项目是有多个版本的~~ 当我们 ...
- 基于网络编程 TCP协议 及 socket 基本语法
socket是什么 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面, ...
- centos部署nextcloud
简介 Nextcloud是一套用于创建和使用文件托管服务的客户端-服务器软件.它在功能上类似于Dropbox,虽然Nextcloud是免费的和开源的,允许任何人在私人服务器上安装和操作它.与Dropb ...
- windows下操作linux虚拟机映射网络驱动器中文件提示chmod权限不足解决方案
为了方便操作,linux虚拟机会通过windows下连接网络驱动器的方式共享自己的文件,对于前端来说,我想把gulp放在windows磁盘,操作虚拟机中的php文件,一来节省虚拟机磁盘大小,二来解决虚 ...
- 【CTSC2018】暴力写挂(边分治,虚树)
[CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...
- Dynamic CRM 2015学习笔记(5)CRM 2015 导入 OData Query Designer 解决方案
以前一直使用OData Query Designer来生成.验证odata查询字符串,本想把它导入到CRM 2015的环境里,但报错: 到MSDN上发现太老版本的solution确实不能再导入到crm ...