Подтвердить что ты не робот

Как защитить веб-API с помощью ASP.NET 5 MVC 6

У меня есть приложение ASP.NET 5/MVC 6. По существу для этого это просто обычное примерное приложение, которое вы получаете, когда начинаете новый проект, чтобы он был простым. До сих пор я могу:

  • Зарегистрировать пользователя
  • Вход
  • Выход
  • Защита страницы (форсирование входа и т.д.)

Теперь я хотел бы предоставить API-интерфейс приложения для входа в систему и получить токен аутентификации. В частности, я работаю над двумя мобильными приложениями для тестирования, используя Angular/Cordova и используя Xamarin.

Я выглядел высоко и низко, и я пока не могу найти пример, который показывает, как сделать эту работу. Каждый пример, который я нахожу до сих пор, предполагает, что пользователь войдет в систему через обычный веб-форму/пост-цикл, а затем будет отправлен на страницу, загружающую Angular, и этот токен аутентификации уже находится в браузере.

Соответствующий код из файла AccountController.cs для контроллера MVC приведен ниже. То, что я в конечном счете хочу, это эквивалентная функциональность, но из чистого вызова API, который позволяет Angular/Xamarin отправить ему имя пользователя/пароль и вернуть токен аутентификации или сбой.

    // 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);
    }

Каков рекомендуемый способ защиты веб-API с помощью ASP.NET MVC 6?

4b9b3361

Ответ 1

Я думаю, что рекомендуемый подход для обеспечения безопасности WebApi2 - это сервер авторизации. Сервер авторизации заботится о создании жетонов. Но на основе этого сервер авторизации на основе OAuth2, который является частью Katana3, был удален из Asp.Net 5.

Я предполагаю, что ваше приложение пока не является живым приложением, так как ASP.NET 5 и MVC 6 еще не находятся в финальной стадии. Поэтому, если вы можете изменить свой процесс идентификации/аутентификации, вы можете использовать Thinktecture IdentityServer3.

Доминик Байер опубликовал блог о состоянии безопасности на ASP.NET 5 и MVC 6 и где IdSvr3 входит в изображение. Этот блог имеет ссылку на репозиторий Github для образца API-контроллера, а также клиент API. Он также имеет образец для веб-приложения MVC. И он может работать с идентификатором Asp.Net.

UPDATE:

Если это не сработает, вы можете попробовать AspNet.Security.OpenIdConnect.Server. Обратите внимание, что у него есть открытые проблемы в Github, поэтому у вас могут возникнуть проблемы с его использованием. Обратите внимание также на его зависимости, в частности, на azureadwebstacknightly.

Обратите внимание, что ASP.NET 5 и MVC 6 могут быть в стабильной бета-версии, но они все еще находятся в бета-версии. Он все еще может измениться.

Кроме того, IdSvr3 v2.0 может быть в финальной версии, но разработан отдельной командой. И он был выпущен только 2 недели назад, поэтому IMHO, как и большинство программ, вы можете столкнуться с вещами, которые, возможно, пропустили их тесты. Обратите внимание на ASP.NET Team, на прошлой неделе, твиттер о IdSvr3 (v2.0), поэтому, похоже, они одобряют его.

Ответ 2

Вот блог-пост, который я обещал, это первый проект, и он будет полезен для тех, у кого есть некоторый опыт работы с ASP.NET MVC 5: Книжный магазин - веб-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;
    }
  }
}