如何使用ASP.NET 5 MVC 6保护Web API

时间:2022-07-14 03:34:41

I have a nice ASP.NET 5 / MVC 6 app up and running. Essentially for this purpose it is just the normal sample app you get when you start a new project to keep it simple. So far I can:

我有一个很好的ASP.NET 5 / MVC 6应用程序启动并运行。基本上就此而言,它只是您启动新项目时保持简单的正常示例应用程序。到目前为止,我可以:

  • Register a user
  • 注册用户
  • Login
  • 登录
  • Logout
  • 登出
  • Protect a page (forcing login etc)
  • 保护页面(强制登录等)

Now, what I would like is to provide an API mechanism for a app to login and get an authentication token. Specifically I am working on two mobile apps to test with, one using Angular / Cordova and one using Xamarin.

现在,我想要的是为应用程序提供一个API机制来登录并获取身份验证令牌。具体来说,我正在开发两个移动应用程序进行测试,一个使用Angular / Cordova,一个使用Xamarin。

I have looked high and low and I cannot seem to find an example yet that shows how to make this work. Every example I find so far assumes the user will login via the normal web form / post cycle and then be taken to a page that loads Angular and this the authentication token is already in the browser.

我看起来高低不一,我似乎找不到一个例子来说明如何使这项工作。到目前为止,我发现的每个示例都假设用户将通过正常的Web表单/发布周期登录,然后转到加载Angular的页面,并且此身份验证令牌已经在浏览器中。

The relevant code from the AccountController.cs file for the MVC controller is below. What I ultimately want is the equivalent functionality but from a pure API call that allows Angular / Xamarin to send it a username / password and get back a authentication token or failure.

下面是MVC控制器的AccountController.cs文件中的相关代码。我最终想要的是等效功能,但来自纯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);
    }

What is the recommended way to protect a Web API using ASP.NET MVC 6?

使用ASP.NET MVC 6保护Web API的推荐方法是什么?

2 个解决方案

#1


3  

I think the recommended approach for securing WebApi2 is through an Authorisation Server. The Authorisation Server takes care of generating tokens. But based on this, the OAuth2 based authorization server that is part of Katana3 has been dropped from Asp.Net 5.

我认为保护WebApi2的推荐方法是通过授权服务器。授权服务器负责生成令牌。但基于此,作为Katana3一部分的基于OAuth2的授权服务器已从Asp.Net 5中删除。

I assume your app is not a live app yet, since both ASP.NET 5 and MVC 6 are not in their final release stage yet. So if you are okay to change your identity/authentication process, you could use Thinktecture's IdentityServer3.

我认为您的应用程序还不是实时应用程序,因为ASP.NET 5和MVC 6都尚未处于最终发布阶段。因此,如果您可以更改身份/身份验证过程,则可以使用Thinktecture的IdentityServer3。

Dominick Baier has blogged about the The State of Security on ASP.NET 5 and MVC 6 and where IdSvr3 comes into the picture. That blog has a link to a Github repository for sample API Controller, and also a API client. It also has sample for an MVC Web app. And it can work with Asp.Net Identity.

Dominick Baier在博客上发表了关于ASP.NET 5和MVC 6的安全状态以及IdSvr3的图片。该博客有一个指向样本API控制器的Github存储库的链接,还有一个API客户端。它还有一个MVC Web应用程序的示例。它可以与Asp.Net Identity一起使用。

UPDATE:

更新:

If that does not work for you, you could try AspNet.Security.OpenIdConnect.Server. Note that it has open issues on Github, so you may encounter problems using it. Note also its dependencies, particularly the azureadwebstacknightly.

如果这对您不起作用,您可以尝试AspNet.Security.OpenIdConnect.Server。请注意,它在Github上有未解决的问题,因此您可能会遇到使用它的问题。还要注意它的依赖关系,特别是azureadwebstacknightly。

Please note that ASP.NET 5 and MVC 6 may be in a stable beta release, but they are still in beta release. It could still change.

请注意,ASP.NET 5和MVC 6可能处于稳定的beta版本,但它们仍处于测试阶段。它仍然可以改变。

Also, IdSvr3 v2.0 may be in its final release, but it is developed by a separate team. And it is only been released 2 weeks ago, so IMHO like most software, you can encounter things that may likely missed their tests. Note the ASP.NET Team, last week, tweeted about IdSvr3's (v2.0) release, so it appears they are endorsing it.

此外,IdSvr3 v2.0可能在最终版本中,但它是由一个单独的团队开发的。它只在2周前发布,所以恕我直言,像大多数软件,你可以遇到可能错过他们的测试的东西。请注意,ASP.NET团队上周发布了有关IdSvr3(v2.0)版本的推文,因此看起来他们支持它。

#2


0  

Here is the blog-post that I had promised, this is the first draft, and would be helpful for someone who has a some experience with ASP.NET MVC 5: Bookstore - Web API with Authorization

这是我承诺的博客文章,这是第一稿,对于对ASP.NET MVC 5有一定经验的人有用:Bookstore - 带有授权的Web API

Complete Source at Github: https://github.com/kbajpai/bookstore

Github的完整资源:https://github.com/kbajpai/bookstore

The API with Authorization:

具有授权的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;
    }
  }
}

#1


3  

I think the recommended approach for securing WebApi2 is through an Authorisation Server. The Authorisation Server takes care of generating tokens. But based on this, the OAuth2 based authorization server that is part of Katana3 has been dropped from Asp.Net 5.

我认为保护WebApi2的推荐方法是通过授权服务器。授权服务器负责生成令牌。但基于此,作为Katana3一部分的基于OAuth2的授权服务器已从Asp.Net 5中删除。

I assume your app is not a live app yet, since both ASP.NET 5 and MVC 6 are not in their final release stage yet. So if you are okay to change your identity/authentication process, you could use Thinktecture's IdentityServer3.

我认为您的应用程序还不是实时应用程序,因为ASP.NET 5和MVC 6都尚未处于最终发布阶段。因此,如果您可以更改身份/身份验证过程,则可以使用Thinktecture的IdentityServer3。

Dominick Baier has blogged about the The State of Security on ASP.NET 5 and MVC 6 and where IdSvr3 comes into the picture. That blog has a link to a Github repository for sample API Controller, and also a API client. It also has sample for an MVC Web app. And it can work with Asp.Net Identity.

Dominick Baier在博客上发表了关于ASP.NET 5和MVC 6的安全状态以及IdSvr3的图片。该博客有一个指向样本API控制器的Github存储库的链接,还有一个API客户端。它还有一个MVC Web应用程序的示例。它可以与Asp.Net Identity一起使用。

UPDATE:

更新:

If that does not work for you, you could try AspNet.Security.OpenIdConnect.Server. Note that it has open issues on Github, so you may encounter problems using it. Note also its dependencies, particularly the azureadwebstacknightly.

如果这对您不起作用,您可以尝试AspNet.Security.OpenIdConnect.Server。请注意,它在Github上有未解决的问题,因此您可能会遇到使用它的问题。还要注意它的依赖关系,特别是azureadwebstacknightly。

Please note that ASP.NET 5 and MVC 6 may be in a stable beta release, but they are still in beta release. It could still change.

请注意,ASP.NET 5和MVC 6可能处于稳定的beta版本,但它们仍处于测试阶段。它仍然可以改变。

Also, IdSvr3 v2.0 may be in its final release, but it is developed by a separate team. And it is only been released 2 weeks ago, so IMHO like most software, you can encounter things that may likely missed their tests. Note the ASP.NET Team, last week, tweeted about IdSvr3's (v2.0) release, so it appears they are endorsing it.

此外,IdSvr3 v2.0可能在最终版本中,但它是由一个单独的团队开发的。它只在2周前发布,所以恕我直言,像大多数软件,你可以遇到可能错过他们的测试的东西。请注意,ASP.NET团队上周发布了有关IdSvr3(v2.0)版本的推文,因此看起来他们支持它。

#2


0  

Here is the blog-post that I had promised, this is the first draft, and would be helpful for someone who has a some experience with ASP.NET MVC 5: Bookstore - Web API with Authorization

这是我承诺的博客文章,这是第一稿,对于对ASP.NET MVC 5有一定经验的人有用:Bookstore - 带有授权的Web API

Complete Source at Github: https://github.com/kbajpai/bookstore

Github的完整资源:https://github.com/kbajpai/bookstore

The API with Authorization:

具有授权的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;
    }
  }
}