加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

WebService的安全性讨论【身份识别】

发布时间:2020-12-17 01:20:44 所属栏目:安全 来源:网络整理
导读:相信很多开发者都用过WebService来实现程序的面向服务,本文主要介绍WebService的身份识别实现方式,当然本文会提供一个不是很完善的例子,权当抱砖引玉了. 首先我们来介绍webservice下的两种验证方式, 一.通过集成windows身份验证 通过集成windows方式解决webs

相信很多开发者都用过WebService来实现程序的面向服务,本文主要介绍WebService的身份识别实现方式,当然本文会提供一个不是很完善的例子,权当抱砖引玉了.

首先我们来介绍webservice下的两种验证方式,

一.通过集成windows身份验证

通过集成windows方式解决webservice的安全问题是一个很简洁,并且行之有效的解决方案,该方案的优点是比较安全,性能较好,当然因为与windows紧密的结合到了一起,缺点自然也很明显了,第一,不便于移植,第二,要进行相关的配置部署工作(当然我们也可以用代码来操作IIS,只不过比较麻烦,最近一直做自动化部署,所以一讲到配置马上就会联想到怎么去自动部署)

具体怎么做呢?

服务器端:配置IIS虚拟目录为集成windows身份验证

客户端:

Service1 wr = new Service1(); //web service实例??

wr.Credentials = new NetworkCredential("administrator","123"); //用户名密码??

lblTest.Text = wr.Add(2,2).ToString(); //调用Add的 web service方法?

二.使用 SoapHeader(SOAP 标头)自定义身份验证

SoapHeader 多数情况下用来传递用户身份验证信息,当然它的作用远不止如此,有待于在实际应用中发掘,体可以实现哪些东西大家有想法可以留言一起交流.

SoapHeader 使用步骤:
(1) 创建继承自 System.Web.WebServices.SoapHeader 的自定义 SoapHeader 类型。
(2) 在 WebService 中创建拥有 public 访问权限的自定义 SoapHeader 字段。
(3) 在需要使用 SoapHeader 的 WebMethod 上添加 SoapHeaderAttribute 访问特性。SoapHeaderAttribute 构造必须指定 memberName 参数,就是我们在第二步中申明的字段名称。
(4) 生成器会自动为客户端生成同名的自定义 SoapHeader 类型,只不过比起我们在 WebService 端创建的要复杂一些。同时还会为代理类型添加一个 soapheaderValue 属性。

下面展示一段SoapHeader的代码,多余的方法将会在后面用到

客户端

View Code
class Program
??? {
???????
static ? void Main( string [] args)
??????? {
??????????? Service1 ws
= ? new Service1();
??????????? ServiceCredential mycredential
= ? new ServiceCredential();

??????????? mycredential.User
= ? " gazi " ;
??????????? mycredential.Password
= " gazi " ;
??????????? ws.ServiceCredentialValue
= mycredential;
???????????
string ? mystr = ws.SayHello();????????????
??????? }
??? }

服务器端

View Code
? public ? class Service1 : System.Web.Services.WebService
??? {
???????
public ServiceCredential myCredential;

??????? [WebMethod]
??????? [SoapHeader(
" myCredential " ,Direction = SoapHeaderDirection.In)]
???????
public ? string ? SayHello()?
??????? {?
???????????
return ? " hello " ;
??????? }
??? }



public ? class ServiceCredential : SoapHeader
??? {
???????
public ? string User;
???????
public ? string Password;
???????
public ? static ? bool ValideUser( string User, string Password)
??????? {
???????????
return ? true ;
??????? }
???????
public ? static ? void CheckUser(Object sender,WebServiceAuthenticationEvent e)
??????? {
???????????
if (ValideUser(e.User,e.Password))
??????????? {
???????????????
return ;
??????????? }
???????????
else
??????????? {
??????????????? WebServiceAuthenticationModule module
= sender as WebServiceAuthenticationModule;
??????????????? module.Result.AddRule(
" 验证错误 " , " 不能确认您的身份,请检查用户名和密码 " );
??????????? }
??????? }
??? }
当我们拥有很多个类的时候,要添加一个或者删除一个验证方式(假设需要进行多种认证)是非常麻烦的,我们不可能跑到每个方法里面去加一个方法调用,这是灾难性的工作,当然我们也可以用AOP来实现,Aop的话需要额外增加很多代码或者直接引入第三方来做,但是我们可不可以有更简便的方法呢?

OK,答案就是使用HttpModule,我们集成IHttpModule写一个处理模块,那么它的原理是什么呢?具体进行了哪些操作呢?我们的思路如下:

  1. HTTP Module 分析 HTTP 消息以检查它们是不是 SOAP 消息。
  2. 如果 HTTP Module 检测到 SOAP 消息,它会读取 SOAP 标头。
  3. 如果 SOAP 消息的 SOAP 标头中有身份验证凭据,HTTP Module 将引发一个自定义 global.asax 事件。

下面来看看我们的Module代码

?

View Code
? 1? ? public ? class WebServiceAuthenticationModule : IHttpModule
? 2? ??? {
? 3? ??????? private ? static WebServiceAuthenticationEventHandler
? 4? ????????????????????? _eventHandler = ? null ;
? 5? ??????? /// ? <summary>
? 6? ??????? /// 验证事件.绑定到此事件可进行对用户身份的识别
? 7? ??????? /// ? </summary>
? 8? ??????? public ? static ? event WebServiceAuthenticationEventHandler Authenticate
? 9? ??????? {
10? ??????????? add { _eventHandler += value; }
11? ??????????? remove { _eventHandler -= value; }
12? ??????? }
13? ??????? public Result Result = ? new Result();
14?
15? ??????? public ? void Dispose()
16? ??????? {
17? ??????? }
18? ??????? public ? void Init(HttpApplication app)
19? ??????? {
20? ??????????? app.AuthenticateRequest += ? new
21? ?????????????????????? EventHandler( this .OnEnter);
22? ??????????? Result.EndValid += ? new ?
23? ??????????????? EventHandler( this .OnCheckError);
24? ??????? }
25?
26? ??????? /// ? <summary>
27? ??????? /// 验证用户身份
28? ??????? /// ? </summary>
29? ??????? /// ? <param name="e"></param>
30? ??????? private ? void OnAuthenticate(WebServiceAuthenticationEvent e)
31? ??????? {
32? ??????????? if (_eventHandler == ? null )
33? ??????????????? return ;
34?
35? ??????????? _eventHandler( this ,e);
36? ??????????? if (e.User != ? null )
37? ??????????????? e.Context.User = e.Principal;
38? ??????? }
39?
40? ??????? public ? string ModuleName
41? ??????? {
42? ??????????? get { return ? " WebServiceAuthentication " ; }
43? ??????? }
44?
45? ??????? void OnEnter(Object source,EventArgs eventArgs)
46? ??????? {
47? ??????????? HttpApplication app = (HttpApplication)source;
48? ??????????? HttpContext context = app.Context;
49? ??????????? Stream HttpStream = context.Request.InputStream;
50?
51? ??????????? // Save the current position of stream.
52? ??????????? long posStream = HttpStream.Position;
53?
54? ??????????? // If the request contains an HTTP_SOAPACTION?
55? ??????????? // header,look at this message.HTTP_SOAPACTION
56? ??????????? if (context.Request.ServerVariables[ " HTTP_SOAPACTION " ] == ? null )
57? ??????????????? return ;
58?
59? ??????????? // Load the body of the HTTP message
60? ??????????? // into an XML document.
61? ??????????? XmlDocument dom = ? new XmlDocument();
62? ??????????? string soapUser;
63? ??????????? string soapPassword;
64?
65? ??????????? try
66? ??????????? {
67? ??????????????? dom.Load(HttpStream);
68?
69? ??????????????? // Reset the stream position.
70? ??????????????? HttpStream.Position = posStream;
71?
72? ??????????????? // Bind to the Authentication header.
73? ??????????????? soapUser =
74? ??????????????????? dom.GetElementsByTagName( " User " ).Item( 0 ).InnerText;
75? ??????????????? soapPassword =
76? ??????????????????? dom.GetElementsByTagName( " Password " ).Item( 0 ).InnerText;
77? ??????????? }
78? ??????????? catch (Exception e)
79? ??????????? {
80? ??????????????? // Reset the position of stream.
81? ??????????????? HttpStream.Position = posStream;
82?
83? ??????????????? // Throw a SOAP exception.
84? ??????????????? XmlQualifiedName name = ? new
85? ???????????????????????????? XmlQualifiedName( " Load " );
86? ??????????????? SoapException soapException = ? new SoapException(
87? ????????????????????????? " SOAP请求没有包含必须的身份识别信息 " ,name,e);
88? ??????????????? throw soapException;
89? ??????????? }
90? ??????????? // 触发全局事件
91? ??????????? OnAuthenticate( new WebServiceAuthenticationEvent
92? ???????????????????????? (context,soapUser,soapPassword));
93? ??????????? Result.OnEndValid();
94? ??????????? return ;
95? ??????? }
96? ??????? void OnCheckError(Object sender,EventArgs e)
97? ??????? {
98? ??????????? if (Result.BrokenRules.Count == ? 0 )
99? ??????????? {
100? ??????????????? return ;
101? ??????????? }
102? ??????????? else
103? ??????????? {
104? ??????????????? HttpApplication app = HttpContext.Current.ApplicationInstance;
105? ??????????????? app.CompleteRequest();
106? ??????????????? app.Context.Response.Write(Result.Error);
107? ??????????? }
108? ??????? }
109? ??? }
??

Authenticate事件是一个静态的变量,这样我们可以在程序的外部来订阅和取消订阅事件(非静态的public 事件在外部也是不能进行订阅和取消订阅事件的,这也是事件和委托的一个区别之一)

下面是我们的事件参数以及委托

?

View Code
1? ? public ? delegate ? void WebServiceAuthenticationEventHandler(Object sender,WebServiceAuthenticationEvent e);
2?
3? ??? /// ? <summary>
4? ??? /// 封装的事件参数
5? ??? /// ? </summary>
6? ??? public ? class WebServiceAuthenticationEvent : EventArgs
7? ??? {
8? ??????? private IPrincipal _IPrincipalUser;
9? ??????? private HttpContext _Context;
10? ??????? private ? string _User;
11? ??????? private ? string _Password;
12?
13? ??????? public WebServiceAuthenticationEvent(HttpContext context)
14? ??????? {
15? ??????????? _Context = context;
16? ??????? }
17?
18? ??????? public WebServiceAuthenticationEvent(HttpContext context,
19? ??????????????????????? string user, string password)
20? ??????? {
21? ??????????? _Context = context;
22? ??????????? _User = user;
23? ??????????? _Password = password;
24? ??????? }
25? ??????? public HttpContext Context
26? ??????? {
27? ??????????? get { return _Context; }
28? ??????? }
29? ??????? public IPrincipal Principal
30? ??????? {
31? ??????????? get { return _IPrincipalUser; }
32? ??????????? set { _IPrincipalUser = value; }
33? ??????? }
34? ??????? public ? void Authenticate()
35? ??????? {
36? ??????????? GenericIdentity i = ? new GenericIdentity(User);
37? ??????????? this .Principal = ? new GenericPrincipal(i, new String[ 0 ]);
38? ??????? }
39? ??????? public ? void Authenticate( string [] roles)
40? ??????? {
41? ??????????? GenericIdentity i = ? new GenericIdentity(User);
42? ??????????? this .Principal = ? new GenericPrincipal(i,roles);
43? ??????? }
44? ??????? public ? string User
45? ??????? {
46? ??????????? get { return _User; }
47? ??????????? set { _User = value; }
48? ??????? }
49? ??????? public ? string Password
50? ??????? {
51? ??????????? get { return _Password; }
52? ??????????? set { _Password = value; }
53? ??????? }
54? ??????? public ? bool HasCredentials
55? ??????? {
56? ??????????? get
57? ??????????? {
58? ??????????????? if ((_User == ? null ) || (_Password == ? null ))
59? ??????????????????? return ? false ;
60? ??????????????? return ? true ;
61? ??????????? }
62? ??????? }
63? ??? }
??

我们在Global.asax的Application_Start方法里面把前面介绍的静态方法ServiceCredential.CheckUser订阅到我们Authenticate事件上,前面提到的增加和删除多种认证方式就是通过这种方法实现的.

? protected void Application_Start(object sender,EventArgs e)
?????? {
?????????? WebServiceAuthenticationModule.Authenticate += ServiceCredential.CheckUser;
?????? }

我们在ServiceCredential.ValideUser方法设置了返回false,这是针对测试的一个配置,实际情况下我们可以和数据库结合起来写一个认证
运行上面讲解SoapHeader的那段代码,你会发现我们的认证已经有效了.关于文章中用到的Result类改天在用一篇文章记录一下,这是一个非常好的记录错误的方案.

很晚了,洗洗睡了,各位有什么好的想法可以留言,本人技术有限,权当抛砖引玉!!!

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读