- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在关注this教程,以便在我的 WCF 服务中获得具有传输安全性的用户名身份验证。然而,本教程指的是使用 basicHttpBinding
这是 Not Acceptable - 我需要 wsHttpBinding
.
想法是有一个自定义 BasicAuthenticationModule
在 WCF 服务上,它将从 HTTP 请求中读取“授权” header ,并根据“授权” header 内容执行身份验证过程。问题是缺少“授权” header !
我已经实现了IClientMessageInspector
通过自定义行为来操作传出消息并添加自定义 SOAP header 。我在 BeforeSendRequest
中添加了以下代码功能:
HttpRequestMessageProperty httpRequest = request.Properties.Where(x => x.Key == "httpRequest").Single().Value;
httpRequest.Headers.Add("CustomHeader", "CustomValue");
这应该有效,根据许多网络资源,它适用于 basicHttpBinding
但不是 wsHttpBinding
.当我说“有效”时,我的意思是 header 已被 WCF 服务成功接收。
这是检查 WCF 服务端接收到的 HTTP 消息的简化函数:
public void OnAuthenticateRequest(object source, EventArgs eventArgs)
{
HttpApplication app = (HttpApplication)source;
//the Authorization header is checked if present
string authHeader = app.Request.Headers["Authorization"];
if (string.IsNullOrEmpty(authHeader))
{
app.Response.StatusCode = 401;
app.Response.End();
}
}
this 的底帖日期为 2011 年 9 月的线程说 wsHttpBinding
不可能做到这一点.我不想接受这种回应。
附带说明一下,如果我使用 IIS 中内置的基本身份验证模块而不是自定义模块,我会得到
The parameter 'username' must not contain commas.** error message when trying
Roles.IsInRole("RoleName")
or `[PrincipalPermission(SecurityAction.Demand, Role = "RoleName")]
可能是因为我的 PrimaryIdentity.Name
属性包含证书主题名称,因为我正在使用 TransportWithMessageCredential
具有基于证书的消息安全性的安全性。
我愿意听取建议以及解决问题的替代方法。谢谢。
更新
看起来,HTTP header 稍后会在整个 WCF 服务代码中被正确读取。 (HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties["httpRequest"]
包含我的自定义 header 。然而,这已经是消息级的了。如何将 header 传递给传输验证例程?
更新 2
经过一些研究后,我得出一个结论,当 Web 浏览器收到 HTTP 状态代码 401 时,它会向我显示登录对话框,我可以在其中指定我的凭据。但是,WCF 客户端只是抛出异常并且不想发送凭据。我在访问 https://myserver/myservice/service.svc
时能够验证此行为在 Internet Explorer 中。尝试使用来自 this 的信息进行修复链接,但无济于事。这是 WCF 中的错误还是我遗漏了什么?
编辑
这是我的 system.servicemodel
的相关部分(来自 web.config
)- 我很确定我的配置是正确的。
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck" />
</clientCertificate>
<serviceCertificate findValue="server.uprava.djurkovic-co.me" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="My" />
</serviceCredentials>
<serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="AspNetSqlRoleProvider" />
</behavior>
</serviceBehaviors>
................
<wsHttpBinding>
<binding name="EndPointWSHTTP" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="20480000" maxReceivedMessageSize="20480000" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="20480000" maxStringContentLength="20480000" maxArrayLength="20480000" maxBytesPerRead="20480000" maxNameTableCharCount="20480000" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic" />
<message clientCredentialType="Certificate" negotiateServiceCredential="true" algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
............
<service behaviorConfiguration="ServiceBehavior" name="DjurkovicService.Djurkovic">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="EndPointWSHTTP" name="EndPointWSHTTP" contract="DjurkovicService.IDjurkovic" />
</service>
服务返回的异常是:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic Realm,Negotiate,NTLM'. (The remote server returned an error: (401) Unauthorized.)
最佳答案
有趣的是,当我在写关于上述答案的最后一条评论时,我停了一会儿。我的评论包含“...如果 HTTP header 不包含“授权” header ,我将状态设置为 401,这会导致异常。” 我将状态设置为 401。明白了吗?解决方案一直存在。
初始数据包不包含授权 header ,即使我明确添加它也是如此。但是,正如我在授权模块处于非事件状态时所测试的那样,每个后续数据包都包含它。所以我为什么不尝试将这个初始数据包与其他数据包区分开来呢?因此,如果我看到它是初始数据包,请将 HTTP 状态代码设置为 200(确定),如果不是 - 检查身份验证 header 。这很容易,因为初始数据包在 SOAP 信封(包含 <t:RequestSecurityToken>
标记)中发送对安全 token 的请求。
好吧,让我们看看我的实现,以防其他人需要它。
这是实现 IHTTPModule 的 BasicAuthenticationModule 实现:
public class UserAuthenticator : IHttpModule
{
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new EventHandler(this.OnAuthenticateRequest);
application.EndRequest += new EventHandler(this.OnEndRequest);
}
public void OnAuthenticateRequest(object source, EventArgs eventArgs)
{
HttpApplication app = (HttpApplication)source;
// Get the request stream
Stream httpStream = app.Request.InputStream;
// I converted the stream to string so I can search for a known substring
byte[] byteStream = new byte[httpStream.Length];
httpStream.Read(byteStream, 0, (int)httpStream.Length);
string strRequest = Encoding.ASCII.GetString(byteStream);
// This is the end of the initial SOAP envelope
// Not sure if the fastest way to do this but works fine
int idx = strRequest.IndexOf("</t:RequestSecurityToken></s:Body></s:Envelope>", 0);
httpStream.Seek(0, SeekOrigin.Begin);
if (idx != -1)
{
// Initial packet found, do nothing (HTTP status code is set to 200)
return;
}
//the Authorization header is checked if present
string authHeader = app.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader))
{
if (authHeader == null || authHeader.Length == 0)
{
// No credentials; anonymous request
return;
}
authHeader = authHeader.Trim();
if (authHeader.IndexOf("Basic", 0) != 0)
{
// the header doesn't contain basic authorization token
// we will pass it along and
// assume someone else will handle it
return;
}
string encodedCredentials = authHeader.Substring(6);
byte[] decodedBytes = Convert.FromBase64String(encodedCredentials);
string s = new ASCIIEncoding().GetString(decodedBytes);
string[] userPass = s.Split(new char[] { ':' });
string username = userPass[0];
string password = userPass[1];
// the user is validated against the SqlMemberShipProvider
// If it is validated then the roles are retrieved from
// the role provider and a generic principal is created
// the generic principal is assigned to the user context
// of the application
if (Membership.ValidateUser(username, password))
{
string[] roles = Roles.GetRolesForUser(username);
app.Context.User = new GenericPrincipal(new
GenericIdentity(username, "Membership Provider"), roles);
}
else
{
DenyAccess(app);
return;
}
}
else
{
app.Response.StatusCode = 401;
app.Response.End();
}
}
public void OnEndRequest(object source, EventArgs eventArgs)
{
// The authorization header is not present.
// The status of response is set to 401 Access Denied.
// We will now add the expected authorization method
// to the response header, so the client knows
// it needs to send credentials to authenticate
if (HttpContext.Current.Response.StatusCode == 401)
{
HttpContext context = HttpContext.Current;
context.Response.AddHeader("WWW-Authenticate", "Basic Realm");
}
}
private void DenyAccess(HttpApplication app)
{
app.Response.StatusCode = 403;
app.Response.StatusDescription = "Forbidden";
// Write to response stream as well, to give the user
// visual indication of error
app.Response.Write("403 Forbidden");
app.CompleteRequest();
}
}
重要提示:为了让我们能够读取 http 请求流,ASP.NET 兼容性不得启用。
要使您的 IIS 加载此模块,您必须将其添加到 <system.webServer>
web.config 部分,如下所示:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="BasicAuthenticationModule" />
<add name="BasicAuthenticationModule" type="UserAuthenticator" />
</modules>
但在此之前,你必须确保BasicAuthenticationModule
部分未锁定,默认情况下应锁定。如果它被锁定,您将无法更换它。
要解锁模块:(注意:我使用的是 IIS 7.5)
在客户端,您需要能够将自定义 HTTP header 添加到传出消息中。最好的方法是实现 IClientMessageInspector 并使用 BeforeSendRequest
添加您的 header 。功能。我不会解释如何实现 IClientMessageInspector,网上有大量关于该主题的资源。
要将“授权”HTTP header 添加到消息中,请执行以下操作:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Making sure we have a HttpRequestMessageProperty
HttpRequestMessageProperty httpRequestMessageProperty;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name))
{
httpRequestMessageProperty = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
if (httpRequestMessageProperty == null)
{
httpRequestMessageProperty = new HttpRequestMessageProperty();
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessageProperty);
}
}
else
{
httpRequestMessageProperty = new HttpRequestMessageProperty();
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessageProperty);
}
// Add the authorization header to the WCF request
httpRequestMessageProperty.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes(Service.Proxy.ClientCredentials.UserName.UserName + ":" + Service.Proxy.ClientCredentials.UserName.Password)));
return null;
}
好了,解决这个问题花了一些时间,但这是值得的,因为我在整个网络上发现了许多类似的悬而未决的问题。
关于c# - 带有 wsHttpBinding 的 WCF 服务 - 操作 HTTP 请求 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9111361/
我正在努力做到这一点 在我的操作中从数据库获取对象列表(确定) 在 JSP 上打印(确定) 此列表作为 JSP 中的可编辑表出现。我想修改然后将其提交回同一操作以将其保存在我的数据库中(失败。当我使用
我有以下形式的 Linq to Entities 查询: var x = from a in SomeData where ... some conditions ... select
我有以下查询。 var query = Repository.Query() .Where(p => !p.IsDeleted && p.Article.ArticleSections.Cou
我正在编写一个应用程序包,其中包含一个主类,其中主方法与GUI类分开,GUI类包含一个带有jtabbedpane的jframe,它有两个选项卡,第一个选项卡包含一个jtable,称为jtable1,第
以下代码产生错误 The nested query is not supported. Operation1='Case' Operation2='Collect' 问题是我做错了什么?我该如何解决?
我已经为 HA redis 集群(2 个副本、1 个主节点、3 个哨兵)设置了本地 docker 环境。只有哨兵暴露端口(10021、10022、10023)。 我使用的是 stackexchange
我正在 Desk.com 中构建一个“集成 URL”,它使用 Shopify Liquid 模板过滤器语法。对于开始日期为 7 天前而结束日期为现在的查询,此 URL 需要包含“开始日期”和“结束日期
你一定想过。然而情况却不理想,python中只能使用类似于 i++/i--等操作。 python中的自增操作 下面代码几乎是所有程序员在python中进行自增(减)操作的常用
我需要在每个使用 github 操作的手动构建中显示分支。例如:https://gyazo.com/2131bf83b0df1e2157480e5be842d4fb 我应该显示分支而不是一个。 最佳答
我有一个关于 Perl qr 运算符的问题: #!/usr/bin/perl -w &mysplit("a:b:c", /:/); sub mysplit { my($str, $patt
我已经使用 ArgoUML 创建了一个 ERD(实体关系图),我希望在一个类中创建两个操作,它们都具有 void 返回类型。但是,我只能创建一个返回 void 类型的操作。 例如: 我能够将 book
Github 操作仍处于测试阶段并且很新,但我希望有人可以提供帮助。我认为可以在主分支和拉取请求上运行 github 操作,如下所示: on: pull_request push: b
我正在尝试创建一个 Twilio 工作流来调用电话并记录用户所说的内容。为此,我正在使用 Record,但我不确定要在 action 参数中放置什么。 尽管我知道 Twilio 会发送有关调用该 UR
我不确定这是否可行,但值得一试。我正在使用模板缓冲区来减少使用此算法的延迟渲染器中光体积的过度绘制(当相机位于体积之外时): 使用廉价的着色器,将深度测试设置为 LEQUAL 绘制背面,将它们标记在模
有没有聪明的方法来复制 和 重命名 文件通过 GitHub 操作? 我想将一些自述文件复制到 /docs文件夹(:= 同一个 repo,不是远程的!),它们将根据它们的 frontmatter 重命名
我有一个 .csv 文件,其中第一列包含用户名。它们采用 FirstName LastName 的形式。我想获取 FirstName 并将 LastName 的第一个字符添加到它上面,然后删除空格。然
Sitecore 根据 Sitecore 树中定义的项目名称生成 URL, http://samplewebsite/Pages/Sample Page 但我们的客户有兴趣降低所有 URL(页面/示例
我正在尝试进行一些计算,但是一旦我输入金额,它就会完成。我只是希望通过单击按钮而不是自动发生这种情况。 到目前为止我做了什么: Angular JS - programming-fr
我的公司创建了一种在环境之间移动文件的复杂方法,现在我们希望将某些构建的 JS 文件(已转换和缩小)从一个 github 存储库移动到另一个。使用 github 操作可以实现这一点吗? 最佳答案 最简
在我的代码中,我创建了一个 JSONArray 对象。并向 JSONArray 对象添加了两个 JSONObject。我使用的是 json-simple-1.1.jar。我的代码是 package j
我是一名优秀的程序员,十分优秀!