web-dev-qa-db-ja.com

DB-ASP.NET Web API 2 + EF6との最初の認証の混乱

既存のMySQLデータベース用のWebAPI C#アプリケーションを作成する必要があります。 Entity Framework 6を​​使用して、すべてのデータベーステーブルをRESTful API(CRUD操作を可能にする)にバインドすることができました。

ログイン/登録システムを実装したい(将来的にロールと権限を実装し、特定のAPIリクエストを制限できるようにするため)

私が使用しなければならないMySQLデータベースには、ユーザー用のテーブルuserと呼ばれます)があり、次の自明の列があります。

  • id
  • email
  • username
  • password_hash

認証のデファクトスタンダードはASP.NetIdentityのようです。私はこの1時間、既存のDB-First EntityFrameworkセットアップでIdentityを機能させる方法を見つけようとしてきました。

ApplicationUserインスタンス(MySQLデータベースからのエンティティ)を格納するuserインスタンスを構築して、ユーザーデータを取得しようとすると、次のエラーが発生します:

エンティティタイプApplicationUserは、現在のコンテキストのモデルの一部ではありません。

MySQLデータベースにIDデータを保存する必要があると思いますが、その方法に関するリソースが見つかりませんでした。 ApplicationUserクラスを完全に削除し、userエンティティクラスをIdentityUserから派生させようとしましたが、UserManager.CreateAsyncを呼び出すと、LINQからエンティティへの変換エラーが発生しました。

既存のuserエンティティを持つWebAPI 2アプリケーションで認証を設定するにはどうすればよいですか?

15
Vittorio Romeo

あなたは言う:

ログイン/登録システムを実装したい(将来、ロールと権限を実装し、特定のAPIリクエストを制限できるようにするため)。

既存のユーザーエンティティを持つWebAPI 2アプリケーションで認証を設定するにはどうすればよいですか?

それは間違いなくあなたがしないでください ASP.NETIdentityが必要であることを意味します。 ASP.NET Identityは、すべてのユーザーのものを処理するテクノロジです。実際には、認証メカニズムを「作成」しません。 ASP.NET Identityは、OWIN認証メカニズムを使用します。これは別のことです。

探しているのは"既存のUsersテーブルでASP.NETIDを使用する方法"ではなく"既存のUsersテーブルを使用してOWIN認証を構成する方法"

OWIN Authを使用するには、次の手順に従います。

パッケージをインストールします。

Owin
Microsoft.AspNet.Cors
Microsoft.AspNet.WebApi.Client
Microsoft.AspNet.WebApi.Core
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.WebHost
Microsoft.Owin
Microsoft.Owin.Cors
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security
Microsoft.Owin.Security.OAuth

ルートフォルダ内にStartup.csファイルを作成します(例):

[アセンブリ:OwinStartup]が正しく構成されていることを確認してください

[Assembly: OwinStartup(typeof(YourProject.Startup))]
namespace YourProject
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
            //other configurations

            ConfigureOAuth(app);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        public void ConfigureOAuth(IAppBuilder app)
        {
            var oAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/api/security/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                Provider = new AuthorizationServerProvider()
            };

            app.UseOAuthAuthorizationServer(oAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }
    }

    public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

            try
            {
                //retrieve your user from database. ex:
                var user = await userService.Authenticate(context.UserName, context.Password);

                var identity = new ClaimsIdentity(context.Options.AuthenticationType);

                identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
                identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));

                //roles example
                var rolesTechnicalNamesUser = new List<string>();

                if (user.Roles != null)
                {
                    rolesTechnicalNamesUser = user.Roles.Select(x => x.TechnicalName).ToList();

                    foreach (var role in user.Roles)
                        identity.AddClaim(new Claim(ClaimTypes.Role, role.TechnicalName));
                }

                var principal = new GenericPrincipal(identity, rolesTechnicalNamesUser.ToArray());

                Thread.CurrentPrincipal = principal;

                context.Validated(identity);
            }
            catch (Exception ex)
            {
                context.SetError("invalid_grant", "message");
            }
        }
    }
}

[Authorize]属性を使用して、アクションを承認します。

GrantTypeUserName、およびPasswordを指定してapi/security/tokenを呼び出し、ベアラートークンを取得します。このような:

"grant_type=password&username=" + username + "&password=" password;

HttpHeader Authorization内のトークンをBearer "YOURTOKENHERE"として送信します。このような:

headers: { 'Authorization': 'Bearer ' + token }

それが役に立てば幸い!

22
Fabio Luz

DBスキーマはデフォルトのUserStoreと互換性がないため、独自のUserStoreクラスとUserPasswordStoreクラスを実装してから、それらをUserManagerに注入する必要があります。この簡単な例を考えてみましょう。

まず、カスタムユーザークラスを作成し、IUserインターフェイスを実装します。

class User:IUser<int>
{
    public int ID {get;set;}
    public string Username{get;set;}
    public string Password_hash {get;set;}
    // some other properties 
}

次に、カスタムのUserStoreクラスとIUserPasswordStoreクラスを次のように作成します。

public class MyUserStore : IUserStore<User>, IUserPasswordStore<User>
{
    private readonly MyDbContext _context;

    public MyUserStore(MyDbContext context)
    {
        _context=context;
    }

    public Task CreateAsync(AppUser user)
    {
        // implement your desired logic such as
        // _context.Users.Add(user);
    }

    public Task DeleteAsync(AppUser user)
    {
        // implement your desired logic
    }

    public Task<AppUser> FindByIdAsync(string userId)
    {
        // implement your desired logic
    }

    public Task<AppUser> FindByNameAsync(string userName)
    {
        // implement your desired logic
    }

    public Task UpdateAsync(AppUser user)
    {
        // implement your desired logic
    }

    public void Dispose()
    {
        // implement your desired logic
    }

    // Following 3 methods are needed for IUserPasswordStore
    public Task<string> GetPasswordHashAsync(AppUser user)
    {
        // something like this:
        return Task.FromResult(user.Password_hash);
    }

    public Task<bool> HasPasswordAsync(AppUser user)
    {
        return Task.FromResult(user.Password_hash != null);
    }

    public Task SetPasswordHashAsync(AppUser user, string passwordHash)
    {
        user.Password_hash = passwordHash;
        return Task.FromResult(0);
    }
}

これで、独自のユーザーストアができました。ユーザーマネージャーに挿入するだけです。

public class ApplicationUserManager: UserManager<User, int>
{
    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
         var manager = new ApplicationUserManager(new MyUserStore(context.Get<MyDbContext>()));
         // rest of code
    }
}

また、独自のユーザーストアを実装しているため、DBContextクラスをDbContextではなくIdentityDbContextから直接継承する必要があることに注意してください。