2016-04-12 14 views
0

私は多くの場所を調べましたが、ここではWIFとWCFに関するいくつかのサンプルコードがあり、MVCプロジェクトでWCFサービスを使用しています私は簡単に行うことができますが)MVCプロジェクトのボックスから出てくるAccountController.cs機能をWCFを介したサービスとしてエミュレート/提供することに関しては何も具体的ではないようです。主にユーザーの認証。MVCプロジェクトで使用するWCFでのASP.Net Identity認証の実装方法

WCFサービスを使用して、Webアプリケーション層と私のデータベースレイヤー(従来の3レイヤーアーキテクチャ)を完全に分離したいと考えています。私が見たすべてのガイドは、独自のカスタムPasswordValidatorを作成したり、UserStoresを修正したり、独自のカスタム認証スキームを変更したりする必要があると思われます(これは正しいですか?

WCFプロジェクトのMVCプロジェクトの開始時に生成される同じASP.NET Entity Frameworkを生成または有効にする簡単な方法があるようですが、ASPをどのように結びつけるのか分かりません.Net MVCプロジェクトでWCFサービスに使用されているようなID認可。サービスでMVCサイトを使用して作成され、入力されたIDデータベースに対してユーザーの資格情報を承認する方法を教えてください。

これは本質的に私が問題を抱えていることです。私はそれを得ることができた後、私は、Webアプリケーションにサービス参照を追加し、それらのサービス要求を使用して、私のコントローラメソッドを介して任意のデータをロードし、ビューで使用するが、その部分は、前にそれをしました。

私は見ました。私は比較するのに必要な塩を使用してテーブルにハッシュされたパスワードを格納し、私はこれを行うための単純な方法を見つけようとして放棄し、ちょうど私自身の認証を実装するために喜ん時点でよ https://msdn.microsoft.com/en-us/library/ff647503.aspx http://www.codeproject.com/Articles/802435/Authentication-and-Authorization-with-ASP-NET-Iden

ユーザーのパスワードを入力してこの問題を回避するだけです。それは、しかし、ホイールを再発明し、すべてのAccountController.csメソッドとIdentityConfig.csを再実行し、カスタム認証を許可するようにweb.configを設定する方法を考え出すことを意味します。私はこれらのVisualStudioツールとフレームワークの全体的なポイントがそれをしないと考えていました。

私には紛失や誤解がありますか?独自のカスタム認証プロバイダを作成するはずですか?

ご協力いただければ幸いです。 WCFのSTSとWIFについての記事も見たことがありますが、それは私が現時点で抱いていることではないと思います。

+0

あなたは[this]を見ましたか(http://stackoverflow.com/questions/24910304/how-to-use-asp-net-identity-model-for-wcf-service-authorization-and- authenticati)質問? –

+0

中間層にWCFを使用する特別な理由は、WebAPIを使用できません。なぜ他の中間層ですか? –

+0

私は、クライアント側のWebアプリケーションとデータサービスを完全に分離したいと思っていました。これは、データサービスを中規模の信頼サブネット内の別のサーバーにホストし、そこからサーバーがすべて高SQLサブネット上にあるSQLサーバーに必要なすべてのSQLクエリを作成することを意味します。私はそれに精通していないWebAPIに対して何も持っていません。 –

答えて

0

私は独自のIdentityUserクラスとインターフェイスを実装しました。そして、ベースとして、ここで

using System; 
using System.Data; 
using System.Data.SqlClient; 
using System.Security.Claims; 
using System.Threading.Tasks; 
using Microsoft.AspNet.Identity; 
using Microsoft.AspNet.Identity.Owin; 
using Microsoft.Owin; 
using Microsoft.Owin.Security; 
using System.Security.Cryptography; 
using System.Text; 

namespace ProudSourcePrime.Identity 
{ 
    /// <summary> 
    /// Class that represents the Users table in our database. 
    /// 
    /// It actually does not connect to our database anymore. 
    /// 
    /// The data it recives for this implementation comes from a service reference running on our data service server. 
    /// </summary> 
    /// <typeparam name="TUser"></typeparam> 
    public class UserTable<TUser> where TUser : IdentityUser 
    { 
     /// <summary> 
     /// sql query that will retrive the username of this user account, keying off of the userId 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <returns></returns> 
     public string GetUserName(string userId) 
     { 
      string userName = null; 
      // TODO : sql query that will retrive the username of this user account, keying off of the userId 
      return userName; 
     } 
     /// <summary> 
     /// sql query that will retrive the userId of this user account, keying off of the userName 
     /// </summary> 
     /// <param name="userName"></param> 
     /// <returns></returns> 
     public string GetUserId(string userName) 
     { 
      string userId = null; 
      // TODO : sql query that will retrive the userId of this user account, keying off of the userName 
      return userId; 
     } 
     /// <summary> 
     /// sql query to get our user data 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <returns></returns> 
     public TUser GetUserById(string userId) 
     { 
      TUser user = null; 
      ServiceReference1.Service1Client ProudSoureService = new ServiceReference1.Service1Client(); 
      ServiceReference1.UserRecordComposite userComposite = ProudSoureService.get_UserById(userId); 
      user = (TUser)Activator.CreateInstance(typeof(TUser)); 
      user.AccessFailedCount = userComposite.AccessFailedCount; 
      user.Email = userComposite.Email; 
      user.Id = userComposite.Id; 
      user.LockoutEnabled = userComposite.LockoutEndDateUtc; 
      user.Name = userComposite.Name; 
      user.PasswordHash = userComposite.PasswordHash; 
      user.PhoneNumber = userComposite.PhoneNumber; 
      user.PhoneNumberConfirmed = userComposite.PhoneNumberConfirmed; 
      user.SecurityStamp = userComposite.SecurityStamp; 
      user.TwoFactorEnabled = userComposite.TwoFactorEnabled; 
      user.UserName = userComposite.UserName; 
      return user; 
     } 
     /// <summary> 
     /// sql query to retrive user using username 
     /// </summary> 
     /// <param name="userName"></param> 
     /// <returns></returns> 
     public TUser GetUserByUserName(string userName) 
     { 
      TUser user = null; 
      ServiceReference1.Service1Client ProudSourceService = new ServiceReference1.Service1Client(); 
      ServiceReference1.UserRecordComposite userComposite = ProudSourceService.get_UserByUserName(userName); 
      user = (TUser)Activator.CreateInstance(typeof(TUser)); 
      user.AccessFailedCount = userComposite.AccessFailedCount; 
      user.Email = userComposite.Email; 
      user.Id = userComposite.Id; 
      user.LockoutEnabled = userComposite.LockoutEndDateUtc; 
      user.Name = userComposite.Name; 
      user.PasswordHash = userComposite.PasswordHash; 
      user.PhoneNumber = userComposite.PhoneNumber; 
      user.PhoneNumberConfirmed = userComposite.PhoneNumberConfirmed; 
      user.SecurityStamp = userComposite.SecurityStamp; 
      user.TwoFactorEnabled = userComposite.TwoFactorEnabled; 
      user.UserName = userComposite.UserName; 
      return user; 
     } 
     /// <summary> 
     /// sql query for password hash of this user keying off of the userId 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <returns></returns> 
     public string GetPasswordHash(string userId) 
     { 
      return new ServiceReference1.Service1Client().get_PasswordHash(userId); 
     } 
     /// <summary> 
     /// Sql command that actually created the user record and inserts into it the passwordHash, usernam and Guid Id. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <param name="passwordHash"></param> 
     /// <returns></returns> 
     public bool SetPasswordHash(TUser user, string passwordHash) 
     { 
      return new ServiceReference1.Service1Client().set_PasswordHash(user.Id, passwordHash, user.UserName, user.Name); 
     } 
     /// <summary> 
     /// Sql command that actually creates the user record and inserts into it the passwordHash, and Guid Id. 
     /// 
     /// No in use currently SetPasswordHash(TUser user, string passwordHash) gets called. 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <param name="passwordHash"></param> 
     /// <returns></returns> 
     public bool SetPasswordHash(string userId, string passwordHash) 
     { 
      bool result = false; 
      // 
      return result; 
     } 
     /// <summary> 
     /// sql query that retrives the security stamp for this user record 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <returns></returns> 
     public string GetSecurityStamp(string userId) 
     { 
      string securityStamp = null; 
      // TODO : sql query that retrives the security stamp for this user record 
      return securityStamp; 
     } 
     /// <summary> 
     /// sql query that will update a Users table record with the given security stamp 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <param name="securityStamp"></param> 
     /// <returns></returns> 
     public bool SetSecurityStamp(string userId, string securityStamp) 
     { 
      bool result = false; 
      // TODO : sql query that sets the security stamp of a user record 
      return result; 
     } 
     /// <summary> 
     /// sql query that inserts a new user into our data base 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public bool Insert(TUser user) 
     { 
      bool result = false; 
      // TODO : sql query that inserts a new User entry 
      return result; 
     } 
     /// <summary> 
     /// sql query to delete this user from our table 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public bool Delete(TUser user) 
     { 
      bool result = false; 
      // TODO : sql query to delete this user from our table 
      return result; 
     } 
     /// <summary> 
     /// sql query that will update our user record on our Users table 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public bool Update(TUser user) 
     { 
      bool result = false; 
      // TODO : sql query that will update our user record on our Users table 
      return result; 
     } 
    } 
    /// <summary> 
    /// Class that implements ASP.NET IUserPasswordStore, IUserSecurityStamp and IUserStore off of the concrete impemntations of IdentityUser's methods. 
    /// </summary> 
    /// <typeparam name="TUser"></typeparam> 
    public class UserStore<TUser> : IUserStore<TUser>, IUserPasswordStore<TUser> where TUser : IdentityUser 
    { 
     /// <summary> 
     /// Private resident that gives access to UserTable's concrete methods 
     /// </summary> 
     private UserTable<TUser> userTable; 
     /// <summary> 
     /// Default Constructor that initializes a new UserTable with connection to our data base. 
     /// </summary> 
     public UserStore() 
     { 
      userTable = new UserTable<TUser>(); 
     } 
     /// <summary> 
     /// Insert a new user into our Users table. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public Task CreateAsync(TUser user) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      // This is commented out for now 
      // 
      // New User is actually created in method SetPasswordHash(TUser user, string passwordHash). 
      //userTable.Insert(user); 
      return Task.FromResult<object>(null); 
     } 
     /// <summary> 
     /// Delete a user from our Users table. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public Task DeleteAsync(TUser user) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      userTable.Delete(user); 
      return Task.FromResult<object>(null); 
     } 
     void IDisposable.Dispose() 
     { 
      throw new NotImplementedException(); 
     } 
     /// <summary> 
     /// Retrives a user by using an Id. 
     /// </summary> 
     /// <param name="userId"></param> 
     /// <returns></returns> 
     public Task<TUser> FindByIdAsync(string userId) 
     { 
      if (string.IsNullOrEmpty(userId)) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      return Task.FromResult(userTable.GetUserById(userId)); 
     } 
     /// <summary> 
     /// Find a user by using the username. 
     /// </summary> 
     /// <param name="userName"></param> 
     /// <returns></returns> 
     public Task<TUser> FindByNameAsync(string userName) 
     { 
      if (string.IsNullOrEmpty(userName)) 
      { 
       throw new ArgumentNullException("TUser is null"); 
      } 
      return Task.FromResult(userTable.GetUserByUserName(userName)); 
     } 
     /// <summary> 
     /// Returns the passwordhash for a given TUser 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public Task<string> GetPasswordHashAsync(TUser user) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      return Task.FromResult(userTable.GetPasswordHash(user.Id)); 
     } 
     /// <summary> 
     /// Get the security stamp for a given TUser. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public Task<string> GetSecurityStampAsync(TUser user) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      return Task.FromResult(userTable.GetSecurityStamp(user.Id)); 
     } 
     /// <summary> 
     /// Verfies whether a given TUser has a password. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public Task<bool> HasPasswordAsync(TUser user) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      if (!string.IsNullOrEmpty(userTable.GetPasswordHash(user.Id))) 
      { 
       return Task.FromResult(true); 
      } 
      else 
      { 
       return Task.FromResult(false); 
      } 
     } 
     /// <summary> 
     /// Sets the password hash for a given TUser. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <param name="passwordHash"></param> 
     /// <returns></returns> 
     public Task SetPasswordHashAsync(TUser user, string passwordHash) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      if(userTable.SetPasswordHash(user, passwordHash)) 
      { 
       return Task.FromResult("true"); 
      } 
      else 
      { 
       return Task.FromResult("false"); 
      } 
     } 
     /// <summary> 
     /// This method will set the secrity stamp for a given TUser. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <param name="stamp"></param> 
     /// <returns></returns> 
     public Task SetSecurityStampAsync(TUser user, string stamp) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      if(userTable.SetSecurityStamp(user.Id, stamp)) 
      { 
       return Task.FromResult("true"); 
      } 
      else 
      { 
       return Task.FromResult("false"); 
      } 
     } 
     /// <summary> 
     /// This method will update a given TUser 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public Task UpdateAsync(TUser user) 
     { 
      if (user == null) 
      { 
       throw new ArgumentNullException("user"); 
      } 
      if(userTable.Update(user)) 
      { 
       return Task.FromResult("true"); 
      } 
      else 
      { 
       return Task.FromResult("false"); 
      } 
     } 
    } 
    /// <summary> 
    /// Implementation of the UserManager class that will be handeling verification and 
    /// </summary> 
    public class ApplicationUserManager : UserManager<IdentityUser> 
    { 
     /// <summary> 
     /// Class instantiation. 
     /// </summary> 
     /// <param name="store"></param> 
     public ApplicationUserManager(UserStore<IdentityUser> store) : base(store) 
     { 
      Store = store; 
      this.PasswordHasher = new AppPassword(); 

      UserLockoutEnabledByDefault = false; 
      // this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10); 
      // this.MaxFailedAccessAttemptsBeforeLockout = 10; 
      UserValidator = new UserValidator<IdentityUser>(this) 
      { 
       AllowOnlyAlphanumericUserNames = false, 
       RequireUniqueEmail = false 
      }; 

      // Configure validation logic for passwords 
      PasswordValidator = new PasswordValidator 
      { 
       RequiredLength = 6, 
       RequireNonLetterOrDigit = false, 
       RequireDigit = false, 
       RequireLowercase = false, 
       RequireUppercase = false, 
      }; 
     } 
     /// <summary> 
     /// Override that actually uses the base layer implementation thus exposing it's funtionality through this object. 
     /// </summary> 
     /// <param name="user"></param> 
     /// <returns></returns> 
     public override System.Threading.Tasks.Task<IdentityResult> CreateAsync(IdentityUser user, string password) 
     { 
      return base.CreateAsync(user, password); 
     } 
    } 
    /// <summary> 
    /// Custom password hasher and hash comparison implementor. 
    /// </summary> 
    public class AppPassword : IPasswordHasher 
    { 
     /// <summary> 
     /// Method that hashes passwords. 
     /// </summary> 
     /// <param name="password"></param> 
     /// <returns></returns> 
     public string HashPassword(string password) 
     { 
      using (SHA256 sha = SHA256Managed.Create()) 
      { 
       byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(password.ToString())); 
       StringBuilder hashSB = new StringBuilder(); 
       for (int i = 0; i < hash.Length; i++) 
       { 
        hashSB.Append(hash[i].ToString("x2")); 
       } 
       return hashSB.ToString(); 
      } 
     } 
     /// <summary> 
     /// Method that compares a given password hash with an input password and compares the given password hash with the hash of the input password. 
     /// </summary> 
     /// <param name="hashedPassword"></param> 
     /// <param name="providedPassword"></param> 
     /// <returns></returns> 
     public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword) 
     { 
      string providedPassword_hashed = HashPassword(providedPassword); 
      if (hashedPassword.Equals(providedPassword_hashed)) 
      { 
       return PasswordVerificationResult.Success; 
      } 
      else 
      { 
       return PasswordVerificationResult.Failed; 
      } 
     } 
    } 
} 

using Microsoft.AspNet.Identity; 
using System; 

namespace ProudSourcePrime.Identity 
{ 
    /// <summary> 
    /// Class that implements ASP.NET Identity IUser interface. 
    /// </summary> 
    public class IdentityUser : IUser 
    { 
     public string Id { get; set; } 
     public string UserName { get; set; } 
     public virtual string Email { get; set; } 
     public virtual string PasswordHash { get; set; } 
     public virtual string SecurityStamp { get; set; } 
     public virtual string PhoneNumber { get; set; } 
     public virtual bool PhoneNumberConfirmed { get; set; } 
     public virtual bool TwoFactorEnabled { get; set; } 
     public virtual DateTime? LockoutEnabled { get; set; } 
     public virtual int AccessFailedCount { get; set; } 
     public virtual string Name { get; set; } 
     /// <summary> 
     /// Default constructor that generates a new guid. 
     /// </summary> 
     public IdentityUser() 
     { 
      Id = Guid.NewGuid().ToString(); 
     } 
     /// <summary> 
     /// Constructor that accepts a Username as a parameter. 
     /// </summary> 
     /// <param name="userName"></param> 
     public IdentityUser(string userName) : this() 
     { 
      UserName = userName; 
     } 
     /// <summary> 
     /// Public accessor to this IdentityUsers GUID 
     /// </summary> 
     string IUser<string>.Id 
     { 
      get 
      { 
       return Id; 
      } 
     } 
     /// <summary> 
     /// Public accessor to this IdentityUsers UserName 
     /// </summary> 
     string IUser<string>.UserName 
     { 
      get 
      { 
       return UserName; 
      } 

      set 
      { 
       UserName = value; 
      } 
     } 
    } 
} 
IdentityUser

で必要なデータを取得するために、WCFサービスへの参照を利用した私のアプリでカスタムUSERSTORE、USERTABLE、ApplicationUserManagerとAppPasswordクラスを記述していることに使用

次にClaimsPrincipalを継承し、WebViewPageを継承するAppViewPageを継承したAppUserPrincipalを実装しました。これにより、サイトのすべてのページがAppViewPageタイプになりましたが、適切に実行するために必要なものが引き継がれます。

using System.Security.Claims; 
using System.Web.Mvc; 

namespace ProudSourcePrime.Config 
{ 
    public class AppUserPrincipal : ClaimsPrincipal 
    { 
     public AppUserPrincipal(ClaimsPrincipal principal) : base(principal) 
     { 

     } 

     public string Name 
     { 
      get 
      { 
       return this.FindFirst(ClaimTypes.Name).Value; 
      } 
     } 
    } 

    public abstract class AppController : Controller 
    { 
     public AppUserPrincipal CurrentUser 
     { 
      get 
      { 
       return new AppUserPrincipal(this.User as ClaimsPrincipal); 
      } 
     } 
    } 

    /// <summary> 
    /// Custom base view page to be inherited by all Razor Views in the web application. 
    /// 
    /// This provides access too our AppUser principal. 
    /// </summary> 
    /// <typeparam name="TModel"></typeparam> 
    public abstract class AppViewPage<TModel> : WebViewPage<TModel> 
    { 
     protected AppUserPrincipal CurrentUser 
     { 
      get 
      { 
       return new AppUserPrincipal(this.User as ClaimsPrincipal); 
      } 
     } 
    } 

    public abstract class AppViewPage : AppViewPage<dynamic> 
    { 

    } 
} 

私はウェブを作った。ないルートディレクトリにあるが、実際にビュー内の設定は、MVCプロジェクトのために、そこに私は私のAppViewPageクラス

<?xml version="1.0"?> 

<configuration> 
    <configSections> 
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> 
     <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> 
     <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> 
    </sectionGroup> 
    </configSections> 

    <system.web.webPages.razor> 
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 
    <!--<pages pageBaseType="System.Web.Mvc.WebViewPage">--> 
    <pages pageBaseType="ProudSourcePrime.Config.AppViewPage"> 
     <namespaces> 
     <add namespace="System.Web.Mvc" /> 
     <add namespace="System.Web.Mvc.Ajax" /> 
     <add namespace="System.Web.Mvc.Html" /> 
     <add namespace="System.Web.Routing" /> 
     <add namespace="System.Web.Optimization "/> 
     <add namespace="ProudSourcePrime" /> 
     </namespaces> 
    </pages> 
    </system.web.webPages.razor> 

    <appSettings> 
    <add key="webpages:Enabled" value="false" /> 
    <add key="owin:AppStartup" value="Startup"/> 
    </appSettings> 

    <system.webServer> 
    <handlers> 
     <remove name="BlockViewHandler"/> 
     <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" /> 
    </handlers> 
    </system.webServer> 

    <system.web> 
    <compilation> 
     <assemblies> 
     <add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> 
     </assemblies> 
    </compilation> 
    </system.web> 
</configuration> 

を使用するようにサイトを構成し、そのフォルダその後、私はちょうどプロジェクトに私のサービス参照を追加することを確認しなければなりませんでしたサービスが必要なデータを返すようにして、準備が整うまで事前に確認してください。

ここに私のgithubにあるプロジェクトへのリンクがあります。あなたがオハイオ州に行く前に、そこに内部IPと感覚情報があります!私は知っているし、私はそれが格納されているサーバー環境は、パブリックに直面しているWebサーバーを通過していない変更から封印され、そのWebサーバーは開いているWebから到達可能な唯一のものです気にしない。私はもはやアクセス権がなくて、目的に応じてアクセスできないので、誰もそれにアクセスすることはできず、間もなく終了します。

https://github.com/jewishmexicanguy/ProudSourcePublic/tree/master/Staging/Herzon/Csharp_codebehnid/ProudSourcePrime

このプロジェクトは、3人の創設者(自分自身と他の二人は)他のものに進出することを決定したので、今は亡きであるフィンテック・スタートアップのためでした。

ここに行っても最終結果を見たい場合は、アカウントの支払いを停止してから間もなく終了します。 http://proudsource.us/welcome

関連する問題