web-dev-qa-db-ja.com

ASP.NET 5 MVC6を使用してWebAPIを保護する方法

素敵なASP.NET5/MVC6アプリを起動して実行しています。基本的にこの目的のために、それはそれを単純に保つために新しいプロジェクトを開始するときにあなたが得る通常のサンプルアプリです。これまでのところ私はできます:

  • ユーザーを登録する
  • ログインする
  • ログアウト
  • ページを保護する(ログインを強制するなど)

ここで、アプリがログインして認証トークンを取得するためのAPIメカニズムを提供したいと思います。具体的には、テストする2つのモバイルアプリに取り組んでいます。1つはAngular/Cordovaを使用し、もう1つはXamarinを使用しています。

私は高くも低くも見えましたが、これを機能させる方法を示す例をまだ見つけることができないようです。これまでに見つけたすべての例では、ユーザーが通常のWebフォーム/ポストサイクルを介してログインし、Angular)をロードするページに移動し、この認証トークンが既にブラウザーにあることを前提としています。

MVCコントローラーのAccountController.csファイルからの関連コードは以下のとおりです。私が最終的に必要としているのは同等の機能ですが、Angular/Xamarinがユーザー名/パスワードを送信して認証トークンまたは失敗を取り戻すことを可能にする純粋なAPI呼び出しからです。

    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
    {
        ViewBag.ReturnUrl = returnUrl;
        if (ModelState.IsValid)
        {
            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, set shouldLockout: true
            var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
            if (result.Succeeded)
            {
                return RedirectToLocal(returnUrl);
            }
            if (result.RequiresTwoFactor)
            {
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            }
            if (result.IsLockedOut)
            {
                return View("Lockout");
            }
            else
            {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                return View(model);
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

ASP.NET MVC6を使用してWebAPIを保護するための推奨される方法は何ですか?

21
Samurai Ken

WebApi2を保護するための推奨されるアプローチは、承認サーバーを使用することだと思います。承認サーバーがトークンの生成を処理します。ただし、 this に基づいて、Katana3の一部であるOAuth2ベースの承認サーバーはAsp.Net5から削除されました。

ASP.NET5とMVC6はどちらもまだ最終リリース段階にないため、アプリはまだライブアプリではないと思います。したがって、ID /認証プロセスを変更しても問題がない場合は、Thinktectureの IdentityServer を使用できます。

Dominick Baierは、 ASP.NET5およびMVC6のセキュリティの状態 とIdSvr3が登場する場所についてブログに書いています。そのブログには、サンプルAPIコントローラーのGithubリポジトリ、およびAPIクライアントへのリンクがあります。また、MVCWebアプリのサンプルもあります。 そして、Asp.Net Identityで動作します。

更新:

それでも問題が解決しない場合は、 AspNet.Security.OpenIdConnect.Server を試してみてください。 Githubで未解決の問題があるため、使用時に問題が発生する可能性があることに注意してください。その依存関係、特にazureadwebstacknightlyにも注意してください。

ASP.NET5とMVC6は安定したベータリリースである可能性がありますが、それらはまだベータリリースであることに注意してください。それはまだ変わる可能性があります。

また、IdSvr3 v2.0は最終リリースになる可能性がありますが、別のチームによって開発されています。また、リリースされたのは2週間前なので、IMHOはほとんどのソフトウェアと同様に、テストを見逃した可能性のあるものに遭遇する可能性があります。 ASP.NET Team 、先週、 tweeted IdSvr3(v2.0)のリリースについて、彼らはそれを支持しているようです。

3
Frank Fajardo

これが私が約束したブログ投稿です。これは最初のドラフトであり、ASP.NET MVC5の経験がある人に役立ちます。 Bookstore-認証付きのWeb API

Githubの完全なソース: https://github.com/kbajpai/bookstore

承認付きのAPI:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using Bookstore.Models;

namespace Bookstore.Controllers
{
  public class BooksController : ApiController
  {
    private BooksDbContext db = new BooksDbContext();

    // GET: api/Books
    [Authorize(Roles="superuser,user")]
    public IQueryable<Book> GetBooks()
    {
      return db.Books;
    }

    // GET: api/Books/5
    [ResponseType(typeof(Book))]
    [Authorize(Roles = "superuser,user")]
    public async Task<IHttpActionResult> GetBook(string id)
    {
      Book book = await db.Books.FindAsync(id);
      if (book == null)
      {
        return NotFound();
      }

      return Ok(book);
    }

    // PUT: api/Books/5
    [ResponseType(typeof(void))]
    [Authorize(Roles = "superuser")]
    public async Task<IHttpActionResult> PutBook(string id, Book book)
    {
      if (!ModelState.IsValid)
      {
        return BadRequest(ModelState);
      }

      if (id != book.Id)
      {
        return BadRequest();
      }

      db.Entry(book).State = EntityState.Modified;

      try
      {
        await db.SaveChangesAsync();
      }
      catch (DbUpdateConcurrencyException)
      {
        if (!BookExists(id))
        {
          return NotFound();
        }
        else
        {
          throw;
        }
      }

      return StatusCode(HttpStatusCode.NoContent);
    }

    // POST: api/Books
    [Authorize(Roles = "superuser")]
    [ResponseType(typeof(Book))]
    public async Task<IHttpActionResult> PostBook(Book book)
    {
      if (!ModelState.IsValid)
      {
        return BadRequest(ModelState);
      }

      db.Books.Add(book);

      try
      {
        await db.SaveChangesAsync();
      }
      catch (DbUpdateException)
      {
        if (BookExists(book.Id))
        {
          return Conflict();
        }
        else
        {
          throw;
        }
      }

      return CreatedAtRoute("DefaultApi", new { id = book.Id }, book);
    }

    // DELETE: api/Books/5
    [Authorize(Roles = "superuser")]
    [ResponseType(typeof(Book))]
    public async Task<IHttpActionResult> DeleteBook(string id)
    {
      Book book = await db.Books.FindAsync(id);
      if (book == null)
      {
        return NotFound();
      }

      db.Books.Remove(book);
      await db.SaveChangesAsync();

      return Ok(book);
    }

    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        db.Dispose();
      }
      base.Dispose(disposing);
    }

    private bool BookExists(string id)
    {
      return db.Books.Count(e => e.Id == id) > 0;
    }
  }
}
0
Kunal B.