我們知道WEB網站的身份驗證一般通過session或者cookie完成的,登錄成功後客戶端發送的任何請求都帶上cookie,服務端根據客戶端發送來的cookie來識別用戶。然而在WebAPI中,我們採用類似的方式,帶有驗證的令牌模式,方便移動端和項目內部調用,能夠解耦合,便於維護,可擴展延伸。
一、OAuth簡介
1、什麼是OAuth
OAuth是一個關於授權(Authorization)的開放網絡標準,目前的版本是2.0版。注意是Authorization(授權),而不是Authentication(認證)。用來做Authentication(認證)的標準叫做openid connect,我們將在以後的文章中進行介紹。
2、名詞定義
理解OAuth中的專業術語能夠幫助你理解其流程模式,OAuth中常用的名詞術語有4個,爲了便於理解這些術語,我們先假設一個很常見的授權場景:
你訪問了一個日誌網站(third party application),你(client)覺得這個網站很不錯,準備以後就要在這個網站上寫日誌了,所以你準備把QQ空間(Resource owner)裏面的日誌都導入進來。此日誌網站想要導入你在QQ空間中的日誌需要知道你的QQ用戶名和密碼才行,爲了安全期間你不會把你的QQ用戶名和密碼直接輸入在日誌網站中,所以日誌網站幫你導航到了QQ認證界面(Authorization Server),當你輸入完用戶名和密碼後,QQ認證服務器返回給日誌網站一個token, 該日誌網站憑藉此token來訪問你在QQ空間中的日誌。
1) third party application 第三方的應用,想要的到Resource owner的授權
2) client 代表用戶
3) Resource owner 資源擁有者,在這裏代表QQ
4) Authorization server 認證服務,這裏代表QQ認證服務,Resource owner和Authorization server可以是不同的服務器,也可以是同一個服務器。
3、OAuth2.0中的四種模式
OAuth定義了四種模式,覆蓋了所有的授權應用場景:
1) 授權碼模式(authorization code)
2) 簡化模式(implicit)
3) 密碼模式(resource owner password credentials)
4) 客戶端模式(client credentials)
前面我們假設的場景可以用前兩種模式來實現,不同之處在於:
當日志網站(third party application)有服務端,使用模式1;
當日志網站(third party application)沒有服務端,例如純的js+html頁面需要採用模式2;
本文主描述利用OAuth2.0實現自己的WebApi認證服務,前兩種模式使用場景不符合我們的需求。
二、基於令牌的認證和應用
1、需要安裝的工具:postman-4.1.2
可以百度安裝postman,如下是非官方安裝,便捷操作
1)下載地址:
鏈接:https://pan.baidu.com/s/1r0gyqrkd5Gp6MwEN9GEG8w
提取碼:ridq
2)、安裝步驟
(1)、打開谷歌瀏覽器設置
(2)、打開指定目錄,添加就可以了
2、簡述創建項目
1)、第一步,創建WebAPI空項目
2)、第二步在Model目錄下添加Student類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Super.WebApi.Models
{
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
3)、第三步在Controllers目錄下添加StudentsController類
using Super.WebApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Http;
namespace Super.WebApi.Controllers
{
[System.Web.Http.RoutePrefix("api/Students")]
public class StudentsController : ApiController
{
Student[] students = new Student[]
{
new Student { Id = 1, Name = "TomatoSoup", Age = 18 },
new Student { Id = 2, Name = "Yoyo", Age = 19 },
new Student { Id = 3, Name = "Hammer", Age = 20 }
};
[Authorize]
[Route("")]
public IEnumerable<Student> GetAllStudents()
{
return students;
}
[Authorize]
public Student GetStudentById(int id)
{
var student = students.FirstOrDefault((p) => p.Id == id);
if (student == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return student;
}
}
}
完成以上三個步驟,可以直接不受權限控制訪問,這樣可能導致數據暴露不安全,所有我們引入了令牌認證的方式,防止非法的訪問;
4)、第四步安裝所需的NuGet包:
打開NuGet包管理器控制檯,然後輸入如下指令:
Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.1.2 Install-Package Microsoft.Owin.Host.SystemWeb -Version 2.1.0 Install-Package Microsoft.AspNet.Identity.Owin -Version 2.0.1
Install-Package Microsoft.Owin.Cors -Version 2.1.0
Install-Package EntityFramework -Version 6.0.0
5)、第五步在更目錄下添加Startup類和SimpleAuthorizationServerProvider類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Super.WebApi;
[assembly: OwinStartup(typeof(WebApi.Startup))]
namespace WebApi
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
//app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
namespace WebApi
{
public class SimpleAuthorizationServerProvider : 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(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name 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);
}
}
}
6)、第六步修改StudentsController
在方法之前添加標記 [Authorize]
[Authorize]
public Student GetStudentById(int id)
{
var student = students.FirstOrDefault((p) => p.Id == id);
if (student == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return student;
}
項目目錄如下:
完成以上步驟後,進行接口訪問
1)、直接訪問會請求失敗,顯示未授權
2)、先進行Post請求獲取驗證的token數據
返回:
{
“access_token”: “wS8l2AlXgK5fy6go9YwfwplYGIioU9Bznsi0YTddv9w7u1TwHloaoZEUQtFeSz2hOKC3h4beWEza69MKRM3es_86WKKeWh3faamcV7YTtuthaHR0l_Zk4U9DGNv0pE5Sgw4Rq_q0CJCz76b_IZysNiUJ3xpOM8D78UQAYGfwpeQlsQWHumu6YYTCStHuTes-2BuzavbAEC8pX3FWM0Km65GVe2mTZLvOUkpmHB3fKug”,
“token_type”: “bearer”,
“expires_in”: 86399
}
3)、在調用指定接口帶入驗證數據
“access_token”: “wS8l2AlXgK5fy6go9YwfwplYGIioU9Bznsi0YTddv9w7u1TwHloaoZEUQtFeSz2hOKC3h4beWEza69MKRM3es_86WKKeWh3faamcV7YTtuthaHR0l_Zk4U9DGNv0pE5Sgw4Rq_q0CJCz76b_IZysNiUJ3xpOM8D78UQAYGfwpeQlsQWHumu6YYTCStHuTes-2BuzavbAEC8pX3FWM0Km65GVe2mTZLvOUkpmHB3fKug”
參考:
1、https://www.cnblogs.com/relax/p/4956441.html
2、https://blog.csdn.net/qq285679784/article/details/80108389
項目源碼:
https://github.com/StevenLdh/Super.Sdk.git
以上內容個人學習認知與大家分享,還望大家一起相互學習,不足之處,歡迎留言指教~