使用ASP.NET Identity 實現WebAPI接口的Oauth身份驗證html
目前WEB 先後端分離的開發模式比較流行,以前作過的幾個小項目也都是先後分離的模式,後端使用asp.net webapi 提供restful風格的數據接口;前端主要使用angularjs等框架。在這種先後分離的模式中如何保護咱們的數據接口不被非法調用,這就可要考慮到後臺接口的身份驗證。咱們經常使用採用的身份驗證有http基本認證(結合https)、http摘要認證、oauth認證等等。本次咱們討論的是Oauth認證。前端
Oauth(Open Authorization)認證:oauth協議爲用戶資源的受權提供了一個安全的、開放而又簡易的標準。與以往的受權方式不一樣之處是OAUTH的受權不會使第三方觸及到用戶的賬號信息(如用戶名與密碼)。angularjs
相關學習資料:https://oauth.net/2/web
http://blog.jobbole.com/49211/sql
http://kb.cnblogs.com/page/189153/數據庫
OWIN (open web interface for .net) :基於.net平臺的開放web接口。OWIN 在 .NET Web 服務器和 .NET Web 應用之間定義了一套標準的接口, 其目的是爲了實現服務器與應用之間的解耦。express
相關學習資料:http://owin.org/json
http://www.cnblogs.com/zergcom/tag/OWIN/後端
http://www.nmtree.net/2014/06/13/getting-started-with-owin-and-katana.htmlapi
ASP.NET Identity: asp.net身份驗證框架,微軟在MVC 5.0(.NET Framework4.5)中新引入,相似mumbership。
相關學習資料:https://www.asp.net/identity
http://www.cnblogs.com/liuhaorain/p/3776467.html
http://www.cnblogs.com/shanyou/p/3918178.html
http://www.cnblogs.com/vxchin/p/introduction-to-aspnet-identity.html
1、用VS2015新建一個項目,項目類型選擇「ASP.NET web應用程序」點擊「肯定」(圖1),選擇「Web API」做爲模板,點擊「肯定」(圖2)。
圖1
圖2
2、使用Nuget安裝:Microsoft.AspNet.WebApi.Owin(圖3)、Microsoft.Owin.Host.SystemWeb(圖4)
圖3
圖4
3、添加Owin啓動類(圖5):
設置Startup類代碼以下:
using System.Web.Http;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(WebAPI.Startup))]
namespace WebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
圖5
4、刪除Global.asax
5、修改App_Start文件夾中的WebApiConfig.cs文件,實現api數據經過json格式返回(圖6):
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
圖6
6、安裝Microsoft.AspNet.Identity.Owin、Microsoft.AspNet.Identity.EntityFramework
圖7
圖8
7、建立AuthContext.cs:
using Microsoft.AspNet.Identity.EntityFramework;
namespace WebAPI
{
public class AuthContext: IdentityDbContext<IdentityUser>
{
public AuthContext() : base("AuthDataBase")
{ }
}
}
Web.config中增長connectionString:
<connectionStrings>
<add name="AuthDataBase" connectionString="Data Source=.\sqlexpress;Initial Catalog= AuthDataBase;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
</connectionStrings>
8、在Models文件夾中建立UserModel.cs
using System.ComponentModel.DataAnnotations;
namespace WebApp.Models
{
public class UserModel
{
[Required]
[Display(Name = "User Name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long", MinimumLength = 6)]
public string Password { get; set; }
}
}
9、添加AuthRepository.cs:
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using WebAPI.Models;
namespace WebAPI
{
public class AuthRepository : IDisposable
{
private AuthContext _ctx;
private UserManager<IdentityUser> _userManager;
public AuthRepository()
{
_ctx = new AuthContext();
_userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(_ctx));
}
public async Task<IdentityResult> Register(UserModel model)
{
IdentityUser user = new IdentityUser()
{
UserName = model.UserName
};
IdentityResult result = await _userManager.CreateAsync(user, model.Password);
return result;
}
public async Task<IdentityUser> FindUser(UserModel model)
{
IdentityUser user = await _userManager.FindAsync(model.UserName, model.Password);
return user;
}
public async Task<IdentityUser> FindUserByName(string username)
{
IdentityUser user = await _userManager.FindByNameAsync(username);
return user;
}
public void Dispose()
{
_ctx.Dispose();
_userManager.Dispose();
}
}
}
10、添加AccountController.cs
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.AspNet.Identity;
using WebAPI.Models;
namespace WebAPI.Controllers
{
[Authorize]
[RoutePrefix("api/Account")]
public class AccountController : ApiController
{
private AuthRepository _authRepo;
public AccountController()
{
_authRepo = new AuthRepository();
}
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(UserModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityResult result = await _authRepo.Register(model);
IHttpActionResult errorResult = GetError(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
private IHttpActionResult GetError(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
foreach (string err in result.Errors)
{
ModelState.AddModelError("", err);
}
if (ModelState.IsValid)
{
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
}
}
11、使用Fiddler調用接口http://localhost:17933/api/account/register(圖9)。調用成功後會生成數據庫AuthDataBase和用戶相關表(圖10)
POST http://localhost:17933/api/account/register
Header:
Content-Type: application/x-www-form-urlencoded
Request Body:
UserName=admin&Password=111111
圖9
圖10
12、添加OrderController.cs:
using System;
using System.Collections.Generic;
using System.Web.Http;
namespace WebAPI.Controllers
{
public class OrderController : ApiController
{
[Authorize]
[RoutePrefix("api/orders")]
public class OrdersController : ApiController
{
[Route]
public IHttpActionResult Get()
{
return Ok(Order.CreateOrders());
}
}
public class Order
{
public int OrderID { get; set; }
public string CustomerName { get; set; }
public string ShipperCity { get; set; }
public Boolean IsShipped { get; set; }
public static List<Order> CreateOrders()
{
List<Order> OrderList = new List<Order>
{
new Order {OrderID = 10248, CustomerName = "Taiseer Joudeh", ShipperCity = "Amman", IsShipped = true },
new Order {OrderID = 10249, CustomerName = "Ahmad Hasan", ShipperCity = "Dubai", IsShipped = false},
new Order {OrderID = 10250,CustomerName = "Tamer Yaser", ShipperCity = "Jeddah", IsShipped = false },
new Order {OrderID = 10251,CustomerName = "Lina Majed", ShipperCity = "Abu Dhabi", IsShipped = false},
new Order {OrderID = 10252,CustomerName = "Yasmeen Rami", ShipperCity = "Kuwait", IsShipped = true}
};
return OrderList;
}
}
}
}
十3、安裝Microsoft.Owin.Security.OAuth(OAuth Bearer Token支持類庫 )(圖11)
圖11
十4、添加Providers文件夾,在文件夾中建立AuthorizationServerProvider.cs
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security.OAuth;
using WebAPI.Models;
namespace WebAPI.Providers
{
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[] { "*" });
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(
new UserModel() { UserName = context.UserName, Password = context.Password });
if (user == null)
{
context.SetError("invalid_grant", "The username or password is incorrect");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
}
安裝:Microsoft.Owin.Cors (跨域)(圖12)
圖12
修改Startup.cs:
using System;
using System.Web.Http;
using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Microsoft.Owin.Security.OAuth;
using Owin;
using WebAPI.Providers;
[assembly: OwinStartup(typeof(WebAPI.Startup))]
namespace WebAPI
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigAuth(app);
WebApiConfig.Register(config);
app.UseCors(CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions option = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new AuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(option);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
十5、使用fiddler調用:http://localhost:17933/token(圖13),調用成功後生成token(圖14)
POST http://localhost:17933/token
Header:
Content-Type: application/x-www-form-urlencoded
Request Body:
grant_type=password&UserName=admin&Password=111111
圖13
圖14
十6、使用上一步驟生成的token請求order數據(圖15),請求成功後返回Order數據(圖16):
GET http://localhost:17933/api/orders
Header:
Authorization: bearer dJvGpKGVPUfJMW_lkcMY79lEV57-LgRe1sZ35OKCzhIHsTaRGBenZ_2--GtosTbrbnwnHMyNKK_f-NalQtgXP_kwx5gj48KnzSaKcTBulQqPP2kFpB6Gbc4npQQmFttJwogEwH3a7-99PsH07Tjl-lbbVvMNhNzGEhK36e5AgeI0yyjIa2JnZGF1kujCj_hrnLvUrazgl6kvmPQZFXXgczuVWnL9dXZe4XMgg0pQ2sI
圖15
圖16