0

我有一个现有的 C# ASP.NET Web 应用程序,它使用成员资格和角色提供程序。这些表托管在同一个数据库中,该数据库包含所有应用程序表(托管在 Azure 中用于生产,本地 SQLExpress 用于开发)。

我想编写一个使用相同用户和角色信息的控制台应用程序。为此,我启用了客户端应用程序服务(在控制台应用程序上)并添加了一个新的 Web 服务,它将这些服务公开给控制台应用程序。

我可以在测试设置(作为控制台和winforms)中使用它,即使用Web 服务创建自己的空白用户/角色集,这些用户/角色从我读到的内容存储在本地文件中作为SQL CE 数据库。

如何让 Web 服务从我的应用程序的数据库中读取?

原始 Web 应用程序的相关 web.config:

<profile defaultProvider="DefaultProfileProvider">
  <providers>
    <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="SecondBiteDBContext" applicationName="/" />
  </providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
  <providers>
    <add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="SecondBiteDBContext" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="10" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
  </providers>
</membership>
<roleManager defaultProvider="DefaultRoleProvider" enabled="true">
  <providers>
    <add name="DefaultRoleProvider" type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="SecondBiteDBContext" applicationName="/" />
  </providers>
</roleManager>

Web 服务的 Web.config:

  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled="true" requireSSL="false"  />
        <profileService enabled="true"        readAccessProperties="WebSettingsTestText"        writeAccessProperties="WebSettingsTestText" />
        <roleService enabled="true"/>
      </webServices>
    </scripting>
  </system.web.extensions>
....
<system.web>
  <profile enabled="true" >
    <properties>
      <add name="WebSettingsTestText" type="string"
        readOnly="false" defaultValue="DefaultText"
        serializeAs="String" allowAnonymous="false" />
    </properties>
  </profile>
</system.web>

控制台应用程序的 App.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <appSettings>
    <add key="ClientSettingsProvider.ServiceUri" value="http://localhost:31337/SecondBiteAppServices/Profile_JSON_AppService.axd" />
    <add key="ClientSettingsProvider.ConnectionStringName" value="DefaultConnection" />
  </appSettings>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=.\SQLExpress;Initial Catalog=SBAuto;MultipleActiveResultSets=True;Integrated Security=SSPI" />
  </connectionStrings>
  <system.web>
    <membership defaultProvider="ClientAuthenticationMembershipProvider">
      <providers>
        <add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="http://localhost:31337/SecondBiteAppServices/Authentication_JSON_AppService.axd" connectionStringName="DefaultConnection" savePasswordHashLocally="False" />
      </providers>
    </membership>
    <roleManager defaultProvider="ClientRoleProvider" enabled="true">
      <providers>
        <add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="http://localhost:31337/SecondBiteAppServices/Role_JSON_AppService.axd" cacheTimeout="86400" connectionStringName="DefaultConnection" />
      </providers>
    </roleManager>
  </system.web>
</configuration>

我没有指定可选的凭据提供程序,因为我没有使用表单 - 现在我无法对 validateuser() 进行硬编码调用来成功。

使用项目属性 -> 服务选项卡 -> 高级按钮,我指定了一个指向所需数据库的自定义连接字符串。我知道它正在连接到我的数据库,因为有一段时间我收到有关“无效对象 ApplicationProperties”的异常。在我的数据库中创建一个包含 PropertyName 和 PropertyValue 列的表修复了这个问题。请注意,我已经在我的数据库上运行了 aspnet_regsql,我希望它应该已经创建了所需的表。

但是,它没有通过对驻留在该数据库中的用户的验证检查 - 它运行干净(没有例外)但登录失败。根据上面app.config的结构,我推测自定义连接字符串只适用于profile服务,而不是membership或role services?(只有配置文件服务 [客户端设置提供程序] 将连接字符串作为参数。)编辑:回顾一些文档, http: //msdn.microsoft.com/en-us/library/bb384312似乎表明了这个自定义连接字符串仅用于存储离线本地缓存内容。

使用项目 -> asp.net 配置(选择了 Web 服务项目),网站管理工具不允许我做任何有意义的事情来配置提供程序或添加新的提供程序。

我无法找到任何能让我真正使用该数据库中的用户和角色的东西。有任何想法吗?(我对 .net 还很陌生,所以很可能我遗漏了一些东西,但是我在文档方面遇到了很多麻烦,并且从谷歌获得了任何不错的结果。)

一旦这工作起来,我想在各个项目之间共享业务逻辑。目前,逻辑位于主要的 Web 应用程序控制器方法中 - 显然我需要将其重构为共享层。考虑到所涉及的对象是基于实体框架的,这是否可能?

在开始使用所有这些客户服务内容之前,我进行了一次尝试性重构。我能够得到所有引用和编译的东西,但是代码在执行的早期就默默地失败了,我最好的猜测是所有各种与框架相关的东西都没有正确初始化——因此进入了客户端应用程序服务。但是我担心即使我让 CAS 运行,实体框架仍然会引起问题。

最后一个:如何在不同项目的配置文件之间共享配置元素(主要是连接字符串)?希望能够在开发和生产之间进行交换,而无需编辑多个文件。

编辑:有多少黑客在做这样的事情?http://devpinoy.org/blogs/comgen/archive/2007/08/15/use-membership-api-in-winforms.aspx 这是我能找到的唯一真正有用的东西,即使没有直接关系到 CAS。似乎我可以从我的主要网络应用程序 web.config 中复制适当的行(其中包括指向我的数据库连接字符串的链接)。

谢谢!

25/1/13 更新:尝试了上面编辑链接中建议的替代方案,替换上面列出的 web.config 元素(尽管我还必须手动将 system.web.providers.dll 复制到 bin/debug 目录)。我可以干净地调用 validateuser(),但它仍然不允许登录。我知道我正在访问我的数据库,因为 SQL 探查器中正在发生活动,并且正在更新用户表上的最后一个活动时间戳。以 CAS 方式执行此操作时不会发生这种情况。

更新 25/1/13 #2 - 刚刚花了 4 个小时试图调试它失败的原因,它让我快疯了。通过阅读代码(不是调试),我只能说它无法将提供的密码哈希与存储的密码进行比较。我 100% 确定我提供了正确的用户名和密码。Microsoft 尚未发布 system.web.providers.dll 的代码,因此在调试方面,我无法跨入membership.validateuser 调用之外的任何内容,因为它调用了 DefaultMembershipProvider.ValidateUser。

Resharper 将向我展示 DefaultMembershipProvider 的源代码(通过反编译),但我无法对其进行调试。我在其他地方读到过,您可以将整个 dll 反编译为项目,删除对 dll 的引用,然后添加项目。

尝试这样做时,我遇到了以下问题:

Dis# 允许我将很多内容保存为项目,但它会产生看似受管理的源代码,其中充满了很多错误。它们似乎主要是在变量名不正确的地方出现语法错误。导入项目时 VS 警告更改目标 .net 版本,但这似乎没有做任何事情

Telerik JustDecompile 让我可以保存项目,但它不包含与 system.web.providers.resources.providerresources.resx 互补的 .cs 文件。我从 Dis# 导入了 .cs,它可以工作。然而,它仍然充满了错误,但是不同的错误(尽管仍然有一些语法相关)并且没有那么多。其中相当一部分与其他 system.x 命名空间中的类有关。毯子添加很多作为参考没有任何帮助。

ILSpy - 打开 dll 时,它会列出 JustDecompile 项目似乎正在引用的其他命名空间的部分。但它只能让我一个一个地保存文件,我对尝试手动重建几十个dll文件的结构并不感兴趣......

这是我第一次尝试反编译任何东西,但我能找到的一切似乎都表明我正在做的事情应该有效(但他们当然没有提到我遇到的任何问题)。对下一步做什么有任何想法吗?

与我在 OP 中尝试的 Web 服务有关的旁注:我注意到 aspnet_regsql.exe 创建了一组以“aspnet_”为前缀的重复的成员资格和角色表。例如 aspnet_membership 而我现有的应用程序/代码只使用普通会员资格。这就解释了为什么 Web 服务不会读取我的数据,因为它正在检查不同的表。

我发现了一个博客(现在无法再次找到链接)关于某人编写了一些代码以在两个模式之间迁移他们的数据,这表明不同的表集与不同版本的成员资格(等)提供程序相关。因此,我假设在“通用提供程序 (system.web.providers) 中看到的提供程序与客户端应用程序服务中使用的提供程序不同(并且不兼容)并且由我创建的 Web 服务公开。

4

1 回答 1

0

提供一个答案,希望能减轻其他人的痛苦。我没有上面提出的所有问题的答案,但这里有一些有用的信息。

  • 要使通用提供程序 (system.web.providers.dll) 连接到任何数据库,只需使用上面我的第一个 web.config 片段中的相关配置行。这适用于任何类型的 .NET 项目(就我尝试过的项目而言)的 app.config (等)。确保添加对 dll 的引用,并且连接字符串包含在配置中。我将连接字符串重构为链接到每个项目的单独配置,以避免重复细节。
  • 旧样式提供程序与新的通用提供程序不兼容,因为它们的 SQL 模式不同。通过编写一些脚本在模式之间进行转换,可以将旧模式转换为新模式。
  • 但是当像这样在它们之间切换时,用于验证登录的哈希算法存在问题。<membership defaultProvider="DefaultMembershipProvider" hashAlgorithmType="SHA1">例如,当您尝试在 Web 应用程序之外使用这些提供程序时,您需要强制启用 SHA1 。
  • 这就是我询问的 ValidateUser() 调用失败的原因。Connect 上的这个错误是相关的:http ://connect.microsoft.com/VisualStudio/feedback/details/734341/system-web-providers-defaultmembershipprovider-behavior-differs-from-sqlmembershipprovider-when-dealing-with-password-compatibility . 由于这个错误,我的网络应用程序(使用通用提供程序)从一开始就一直在 SHA1 模式下运行 - 它应该是 HMACSHA256。使用控制台应用程序时默认 HMACSHA256 正确应用,因此不匹配!
  • 其他对哈希算法配置有疑问的人:http: //chriskinsmanblog.azurewebsites.net/2012/03/30/SystemWebProvidersDefaultMembershipProviderBehaviorChange.aspx
  • 但是,经过上述所有操作后,我发现您不一定需要使用这些提供程序。无需登录或对提供者有任何引用,就可以使实体框架正确执行。(我希望我知道为什么在开始所有这些混乱之前我无法让它工作。我知道我引用了相关的 dll。)
  • 我仍然不知道为什么我不能令人满意地重新编译 web.providers.dll。
于 2013-01-30T08:56:17.970 回答