前言
在涉及到後端專案的開發中,如何實現對於使用者許可權的管控是需要我們首先考慮的,在實際開發過程中,我們可能會運用一些已經成熟的解決方案幫助我們實現這一功能,而在 Grapefruit.VuCore 這個專案中,我將使用 Jwt 的方式實現對於使用者的許可權管控,在本章中,我將演示如何使用 Jwt 實現對於使用者的授權、鑑權。
系列目錄地址:ASP.NET Core 專案實戰
Step by Step
一、一些概念
Jwt(json web token),是一種基於 Json 的無狀態授權令牌,因為 Jwt 是一種標準的資料傳輸規範,並不是某家所獨有的技術規範,因此非常適用於構建單點登入服務,為 web、client、app 等等各種介面使用方提供授權服務。
在使用 Jwt 進行許可權控制的過程中,我們需要先請求授權伺服器獲取到 token 令牌,將令牌儲存到客戶端本地(在 web 專案中,我們可以將 token 儲存到 localstorage 或是 cookie 中),之後,對於服務端的每一次請求,都需要將獲取到的 token 資訊新增到 http 請求的 header 中。
$.ajax({
url: url,
method: "POST",
data: JSON.stringify(data),
beforeSend: function (xhr) {
/* Authorization header */
xhr.setRequestHeader("Authorization", "Bearer " + token);
},
success: function (data) {}
});
複製程式碼
當使用者擁有令牌後是否就可以訪問系統的所有功能了呢?答案當然否定的。對於一個系統來說可能會有多種使用者角色,每一個使用者角色可以訪問的資源也是不同的,所以,當使用者已經擁有令牌後,我們還需要對使用者角色進行鑑定,從而做到對使用者進行進一步的許可權控制。
在 Grapefruit.VuCore 這個專案中,我採用的是基於策略的授權方式,通過定義一個授權策略來完善 Jwt 鑑權,之後將這個自定義策略注入到 IServiceCollection 容器中,對許可權控制做進一步的完善,從而達到對於使用者訪問許可權的管控。
基於策略的授權是微軟在 ASP.NET Core 中新增的一種新的授權方式,通過定義好策略(policy)的一個或多個要求(requirements),將這個自定義的授權策略在 Startup.ConfigureServices 方法中作為授權服務配置的一部分進行註冊之後即可按照我們的策略處理程式進行許可權的控制。
services.AddAuthorization(options =>
{
options.AddPolicy("Permission",
policy => policy.Requirements.Add(new PolicyRequirement()));
})
services.AddSingleton<IAuthorizationHandler, PolicyHandler>();
複製程式碼
就像我在後面的程式碼中一樣,我定義了一個名叫 Permission 的授權策略,它包含了一個叫做 PolicyRequirement 的鑑權要求,在實現了授權策略後,將基於這個要求的鑑權方法 PolicyHandler 以單例(AddSingleton)的形式注入到服務集合中,此時,我們就可以在需要新增驗證的 controller 上新增 attribute 即可。
[Authorize(Policy = "Permission")]
public class SecretController : ControllerBase
{}
複製程式碼
二、授權
在 Grapefruit.VuCore 這個專案中,涉及到授權相關的程式碼所在的位置我已在下圖中進行標示。在之前系列開篇文章(ASP.NET Core 實戰:使用 ASP.NET Core Web API 和 Vue.js,搭建前後端分離框架)進行介紹整個專案框架時曾說到, Grapefruit.Application 是專案的應用層,顧名思義,就是為了實現我們專案中的實際業務功能所劃分的類庫。因此,我們實現 Jwt 的相關業務程式碼應該位於此層中。同時,因為對於 Jwt 的令牌頒發與鑑權,採用的是微軟的 JwtBearer 元件,所以我們在使用前需要先通過 Nuget 將引用新增到 Grapefruit.Application 上。
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
Install-Package System.IdentityModel.Tokens.Jwt
複製程式碼
在 Grapefruit.Application 這個類庫下我建立了一個 Authorization 解決方案資料夾用來儲存授權相關的程式碼。在 Authorization 這個解決方案資料夾中包含了兩個子資料夾 Jwt 和 Secret。Jwt 資料夾中主要包含我們對於 Jwt 的操作,而 Secret 資料夾下則是對於使用者的相關操作。
每個子應用資料夾(Jwt、Secret)都包含了相同的結構:Dto 資料傳輸物件、功能介面,以及功能介面的實現類,這裡介面的繼承採用單繼承的方式。
在 Jwt 資料夾下建立一個 IJwtAppService 介面檔案,在這裡定義我們對於 Jwt 的相關操作。因為對於 Jwt 的授權、鑑權是採用微軟的 JwtBearer 元件,我們只需要進行配置即可,所以這裡只定義對於 token 的生成、重新整理、停用,以及判斷這個 token 是否有效這幾個方法。同時,我們需要建立 JwtAppService 這個類檔案,去繼承 IJwtAppService 從而實現介面功能。public interface IJwtAppService
{
/// <summary>
/// 新增 Jwt token
/// </summary>
/// <param name="dto">使用者資訊資料傳輸物件</param>
/// <returns></returns>
JwtAuthorizationDto Create(UserDto dto);
/// <summary>
/// 重新整理 Token
/// </summary>
/// <param name="token">Token</param>
/// <param name="dto">使用者資訊資料傳輸物件</param>
/// <returns></returns>
Task<JwtAuthorizationDto> RefreshAsync(string token, UserDto dto);
/// <summary>
/// 判斷當前 Token 是否有效
/// </summary>
/// <returns></returns>
Task<bool> IsCurrentActiveTokenAsync();
/// <summary>
/// 停用當前 Token
/// </summary>
/// <returns></returns>
Task DeactivateCurrentAsync();
/// <summary>
/// 判斷 Token 是否有效
/// </summary>
/// <param name="token">Token</param>
/// <returns></returns>
Task<bool> IsActiveAsync(string token);
/// <summary>
/// 停用 Token
/// </summary>
/// <returns></returns>
Task DeactivateAsync(string token);
}
複製程式碼
JwtAuthorizationDto 是一個 token 資訊的傳輸物件,包含我們建立好的 token 相關資訊,用來將 token 資訊返回給前臺進行使用。而 UserDto 則是使用者登入獲取 token 時的資料傳輸物件,用來接收登入時的引數值。
在建立 token 或是驗證 token 時,像 token 的頒發者、接收者之類的資訊,因為會存在多個地方呼叫的可能性,這裡我將這些資訊存放在了配置檔案中,後面當我們需要使用的時候,只需要通過注入 IConfiguration 進行獲取即可。關於 Jwt 的配置檔案主要包含了四項:token 的頒發者,token 的接收者,加密 token 的 key 值,以及 token 的過期時間,你可以根據你自己的需求進行調整。
"Jwt": {
"Issuer": "yuiter.com",
"Audience": "yuiter.com",
"SecurityKey": "a48fafeefd334237c2ca207e842afe0b",
"ExpireMinutes": "20"
}
複製程式碼
在 token 的建立過程中可以簡單拆分為三個部分:根據配置資訊和使用者資訊建立一個 token,將加密後的使用者資訊寫入到 HttpContext 上下文中,以及將建立好的 token 資訊新增到靜態的 HashSet 集合中。
在 token 建立、校驗的整個生命週期中,都涉及到了 Scheme、Claim、ClaimsIdentity、ClaimsPrincipal 這些概念,如果你之前有使用過微軟的 Identity 許可權驗證,對於這幾個名詞就會比較熟悉,可能某些小夥伴之前並沒有使用過 Identity,我來簡單介紹下這幾個名詞的含義。
Scheme 模式,這個與其餘的名詞相對獨立,它主要是指明我們是以什麼授權方式進行授權的。例如,你是以 cookie 的方式授權或是以 OpenId 的方式授權,或是像這裡我們使用 Jwt Bearer 的方式進行授權。
Claim 宣告,以我們的現實生活為例,我們每個人都會有身份證,上面會包含我們的姓名、性別、民族、出生日期、家庭住址、身份證號,每一項資料的都可以看成是 type-value(資料型別-資料值),例如,姓名:張三。身份證上的每一項的資訊就是我們的 Claim 宣告,姓名:張三,是一個 Claim;性別:男,也是一個 Claim。而對於 ClaimsIdentity,就像這一項項的資訊最終組成了我們的身份證,這一項項的 Claim 最終組成了我們的 ClaimsIdentity。而 ClaimsPrincipal 則是 ClaimsIdentity 的持有者,就像我們擁有身份證一樣。
從上面的文字可以總結出,Claim(每一項的證件資訊)=》ClaimsIdentity(證件)=》ClaimsPrincipal(證件持有者)。其中,一個 ClaimsIdentity 可以包含多個的 Claim,而一個 ClaimsPrincipal 可以包含多個的 ClaimsIdentity。
如果想要深入瞭解 ASP.NET Core 的授權策略的可以看看部落格園裡這篇文章 =》ASP.NET Core 執行原理解剖[5]:Authentication,或是國外的這篇介紹 ASP.NET Core 授權的文章 =》Introduction to Authentication with ASP.NET Core。
實現 token 生成的最終程式碼實現如下所示,可以看到,在建立 ClaimsIdentity “證件”資訊時,我新增了使用者的角色資訊,並把加密後的使用者資訊寫入到 HttpContext 上下文中,這樣,我們在後面驗證的時候就可以通過 HttpContext 獲取到使用者的角色資訊,從而判斷使用者是否可以訪問當前請求的地址。
/// <summary>
/// 新增 Token
/// </summary>
/// <param name="dto">使用者資訊資料傳輸物件</param>
/// <returns></returns>
public JwtAuthorizationDto Create(UserDto dto)
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecurityKey"]));
DateTime authTime = DateTime.UtcNow;
DateTime expiresAt = authTime.AddMinutes(Convert.ToDouble(_configuration["Jwt:ExpireMinutes"]));
//將使用者資訊新增到 Claim 中
var identity = new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme);
IEnumerable<Claim> claims = new Claim[] {
new Claim(ClaimTypes.Name,dto.UserName),
new Claim(ClaimTypes.Role,dto.Role.ToString()),
new Claim(ClaimTypes.Email,dto.Email),
new Claim(ClaimTypes.Expiration,expiresAt.ToString())
};
identity.AddClaims(claims);
//簽發一個加密後的使用者資訊憑證,用來標識使用者的身份
_httpContextAccessor.HttpContext.SignInAsync(JwtBearerDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),//建立宣告資訊
Issuer = _configuration["Jwt:Issuer"],//Jwt token 的簽發者
Audience = _configuration["Jwt:Audience"],//Jwt token 的接收者
Expires = expiresAt,//過期時間
SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256)//建立 token
};
var token = tokenHandler.CreateToken(tokenDescriptor);
//儲存 Token 資訊
var jwt = new JwtAuthorizationDto
{
UserId = dto.Id,
Token = tokenHandler.WriteToken(token),
Auths = new DateTimeOffset(authTime).ToUnixTimeSeconds(),
Expires = new DateTimeOffset(expiresAt).ToUnixTimeSeconds(),
Success = true
};
_tokens.Add(jwt);
return jwt;
}
複製程式碼
當建立好 token 之後,客戶端就可以在 Http 請求的 header 中新增 token 資訊,從而訪問受保護的資源。不過,在某些情況下,比如說,使用者修改了密碼之後,雖然當前的 token 資訊可能還未過期,但我們也不能允許使用者再使用當前的 token 資訊進行介面的訪問,這時,就涉及到了對於 token 資訊的停用以及重新整理。
這裡我採用是當我們停用 token 資訊時,將停用的 token 資訊新增到 Redis 快取中,之後,在使用者請求時判斷這個 token 是不是存在於 Redis 中即可。
當然,你也可以在停用當前使用者的 token 資訊時,將 HashSet 中的這個 token 資訊進行刪除,之後,通過判斷訪問時的 token 資訊是否在 HashSet 集合中,判斷 token 是否有效。
方法很多,看你自己的需求了。
對於 Redis 的讀寫操作,我是使用微軟的 Redis 元件進行的,你可以按照你的喜好進行修改。如果你和我一樣,採用這個元件,你需要在 Grapefruit.Application 這個類庫中通過 Nuget 新增微軟的分散式快取抽象介面 dll 的引用,以及在 Grapefruit.WebApi 專案中新增微軟的 Redis 實現。
Install-Package Microsoft.Extensions.Caching.Abstractions ## 分散式快取抽象介面
Install-Package Microsoft.Extensions.Caching.Redis ## Redis 實現
複製程式碼
當我們停用 token 時,通過 HttpContext 上下文獲取到 HTTP Header 中的 token 資訊,將該 token 資訊儲存到 Redis 快取中,這樣,我們就完成了對於 token 的停用。
public class JwtAppService : IJwtAppService
{
/// <summary>
/// 停用 Token
/// </summary>
/// <param name="token">Token</param>
/// <returns></returns>
public async Task DeactivateAsync(string token)
=> await _cache.SetStringAsync(GetKey(token),
" ", new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(Convert.ToDouble(_configuration["Jwt:ExpireMinutes"]))
});
/// <summary>
/// 停用當前 Token
/// </summary>
/// <returns></returns>
public async Task DeactivateCurrentAsync()
=> await DeactivateAsync(GetCurrentAsync());
/// <summary>
/// 設定快取中過期 Token 值的 key
/// </summary>
/// <param name="token">Token</param>
/// <returns></returns>
private static string GetKey(string token)
=> $"deactivated token:{token}";
/// <summary>
/// 獲取 HTTP 請求的 Token 值
/// </summary>
/// <returns></returns>
private string GetCurrentAsync()
{
//http header
var authorizationHeader = _httpContextAccessor
.HttpContext.Request.Headers["authorization"];
//token
return authorizationHeader == StringValues.Empty
? string.Empty
: authorizationHeader.Single().Split(" ").Last();// bearer tokenvalue
}
}
複製程式碼
對於 token 的重新整理,其實我們就可以看成重新生成了一個 token 資訊,只不過我們需要將之前的 token 資訊進行停用。
public class JwtAppService : IJwtAppService
{
/// <summary>
/// 重新整理 Token
/// </summary>
/// <param name="token">Token</param>
/// <param name="dto">使用者資訊</param>
/// <returns></returns>
public async Task<JwtAuthorizationDto> RefreshAsync(string token, UserDto dto)
{
var jwtOld = GetExistenceToken(token);
if (jwtOld == null)
{
return new JwtAuthorizationDto()
{
Token = "未獲取到當前 Token 資訊",
Success = false
};
}
var jwt = Create(dto);
//停用修改前的 Token 資訊
await DeactivateCurrentAsync();
return jwt;
}
/// <summary>
/// 判斷是否存在當前 Token
/// </summary>
/// <param name="token">Token</param>
/// <returns></returns>
private JwtAuthorizationDto GetExistenceToken(string token)
=> _tokens.SingleOrDefault(x => x.Token == token);
}
複製程式碼
至此,我們對於 token 的建立、重新整理、停用的程式碼就已經完成了,接下來,我們來實現對於 token 資訊的驗證。PS:下面的程式碼如無特殊說明外,均位於 Startup 類中。
三、鑑權
在 ASP.NET Core 應用中,依賴注入隨處可見,而我們對於我們的功能方法的使用,也是採用依賴注入到容器,通過功能介面進行呼叫的方式。因此,我們需要將我們的介面與其實現類注入到 IServiceCollection 容器中。這裡,我們採用反射的方式,批量的將程式集內的介面與其實現類進行注入。
public void ConfigureServices(IServiceCollection services)
{
Assembly assembly = Assembly.Load("Grapefruit.Application");
foreach (var implement in assembly.GetTypes())
{
Type[] interfaceType = implement.GetInterfaces();
foreach (var service in interfaceType)
{
services.AddTransient(service, implement);
}
}
}
複製程式碼
因為基礎的許可權驗證我們是採用的微軟的 JwtBearer 許可權驗證元件進行的授權和鑑權,因此對於 token 資訊的基礎鑑權操作,只需要我們在中介軟體中進行配置即可。同時,我們也在 IJwtAppService 介面中定義了對於 token 資訊的一些操作,而對於我們自定義的許可權驗證策略,則需要通過基於策略的授權方式進行實現。
首先,我們需要先定義一個繼承於 IAuthorizationRequirement 的自定義授權要求類 PolicyRequirement。在這個類中,你可以定義一些屬性,通過有參建構函式的方式進行構造,這裡我不定義任何的屬性,僅是建立這個類。
public class PolicyRequirement : IAuthorizationRequirement
{ }
複製程式碼
當我們建立好 PolicyRequirement 這個許可權要求類後,我們就可以通過繼承 AuthorizationHandler 來實現我們的授權邏輯。這裡實現許可權控制的程式碼邏輯,主要是通過重寫 HandleRequirementAsync 方法來實現的。
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement)
{
//Todo:獲取角色、Url 對應關係
List<Menu> list = new List<Menu> {
new Menu
{
Role = Guid.Empty.ToString(),
Url = "/api/v1.0/Values"
},
new Menu
{
Role=Guid.Empty.ToString(),
Url="/api/v1.0/secret/deactivate"
},
new Menu
{
Role=Guid.Empty.ToString(),
Url="/api/v1.0/secret/refresh"
}
};
var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext;
//獲取授權方式
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
//驗證簽發的使用者資訊
var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
if (result.Succeeded)
{
//判斷是否為已停用的 Token
if (!await _jwtApp.IsCurrentActiveTokenAsync())
{
context.Fail();
return;
}
httpContext.User = result.Principal;
//判斷角色與 Url 是否對應
//
var url = httpContext.Request.Path.Value.ToLower();
var role = httpContext.User.Claims.Where(c => c.Type == ClaimTypes.Role).FirstOrDefault().Value;
var menu = list.Where(x => x.Role.Equals(role) && x.Url.ToLower().Equals(url)).FirstOrDefault();
if (menu == null)
{
context.Fail();
return;
}
return;
}
}
context.Fail();
}
複製程式碼
在判斷使用者是否可以訪問當前的請求地址時,首先需要獲取到使用者角色與其允許訪問的地址列表,這裡我使用的是模擬的資料。通過判斷當前登入使用者的角色是否包含請求的地址,當使用者的角色並不包含對於訪問地址的許可權時,返回 403 Forbidden 狀態碼。
這裡需要注意,如果你準備採取 RESTful 風格的 API,因為請求的地址是相同的,你需要新增一個 HTTP 謂詞引數用來指明所請求的方法,從而達到訪問許可權管控的目的。
包含 token 的基礎驗證的授權配置的程式碼如下所示。在中介軟體進行 Jwt 驗證的過程中,會驗證授權方式是不是 Bearer 以及通過 token 的屬性解密之後與生成時使用者資料進行比對,從而判斷這個 token 是否有效。
public void ConfigureServices(IServiceCollection services)
{
string issuer = Configuration["Jwt:Issuer"];
string audience = Configuration["Jwt:Audience"];
string expire = Configuration["Jwt:ExpireMinutes"];
TimeSpan expiration = TimeSpan.FromMinutes(Convert.ToDouble(expire));
SecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:SecurityKey"]));
services.AddAuthorization(options =>
{
//1、Definition authorization policy
options.AddPolicy("Permission",
policy => policy.Requirements.Add(new PolicyRequirement()));
}).AddAuthentication(s =>
{
//2、Authentication
s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(s =>
{
//3、Use Jwt bearer
s.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = issuer,
ValidAudience = audience,
IssuerSigningKey = key,
ClockSkew = expiration,
ValidateLifetime = true
};
s.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
//Token expired
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
//DI handler process function
services.AddSingleton<IAuthorizationHandler, PolicyHandler>();
}
複製程式碼
因為我們是使用 Swagger 進行的 API 文件的視覺化,這裡,我們繼續配置 Swagger 從而使 Swagger 可以支援我們的許可權驗證方式。
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(s =>
{
//Add Jwt Authorize to http header
s.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",//Jwt default param name
In = "header",//Jwt store address
Type = "apiKey"//Security scheme type
});
//Add authentication type
s.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
});
});
}
複製程式碼
在停用 token 的程式碼中,我們使用了 Redis 去儲存停用的 token 資訊,因此,我們需要配置我們的 Redis 連線。
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedRedisCache(r =>
{
r.Configuration = Configuration["Redis:ConnectionString"];
});
}
複製程式碼
現在,整個業務相關的程式碼已經完成了,我們可以建立前端訪問的介面了。這裡我是在 Controllers 下的 V1 資料夾下建立了一個 SecretController 用來構建前端訪問的介面。控制器中主要有三個方法,分別為 CancelAccessToken(停用 token)、Login(獲取 token)以及 RefreshAccessTokenAsync(重新整理 token)。
public class SecretController : ControllerBase
{
/// <summary>
/// 停用 Jwt 授權資料
/// </summary>
/// <returns></returns>
[HttpPost("deactivate")]
public async Task<IActionResult> CancelAccessToken()
{
await _jwtApp.DeactivateCurrentAsync();
return Ok();
}
/// <summary>
/// 獲取 Jwt 授權資料
/// </summary>
/// <param name="dto">授權使用者資訊</param>
[HttpPost("token")]
[AllowAnonymous]
public IActionResult Login([FromBody] SecretDto dto)
{
//Todo:獲取使用者資訊
var user = new UserDto
{
Id = Guid.NewGuid(),
UserName = "yuiter",
Role = Guid.Empty,
Email = "yuiter@yuiter.com",
Phone = "13912345678",
};
if (user == null)
return Ok(new JwtResponseDto
{
Access = "無權訪問",
Type = "Bearer",
Profile = new Profile
{
Name = dto.Account,
Auths = 0,
Expires = 0
}
});
var jwt = _jwtApp.Create(user);
return Ok(new JwtResponseDto
{
Access = jwt.Token,
Type = "Bearer",
Profile = new Profile
{
Name = user.UserName,
Auths = jwt.Auths,
Expires = jwt.Expires
}
});
}
/// <summary>
/// 重新整理 Jwt 授權資料
/// </summary>
/// <param name="dto">重新整理授權使用者資訊</param>
/// <returns></returns>
[HttpPost("refresh")]
public async Task<IActionResult> RefreshAccessTokenAsync([FromBody] SecretDto dto)
{
//Todo:獲取使用者資訊
var user = new UserDto
{
Id = Guid.NewGuid(),
UserName = "yuiter",
Role = Guid.Empty,
Email = "yuiter@yuiter.com",
Phone = "13912345678",
};
if (user == null)
return Ok(new JwtResponseDto
{
Access = "無權訪問",
Type = "Bearer",
Profile = new Profile
{
Name = dto.Account,
Auths = 0,
Expires = 0
}
});
var jwt = await _jwtApp.RefreshAsync(dto.Token, user);
return Ok(new JwtResponseDto
{
Access = jwt.Token,
Type = "Bearer",
Profile = new Profile
{
Name = user.UserName,
Auths = jwt.Success ? jwt.Auths : 0,
Expires = jwt.Success ? jwt.Expires : 0
}
});
}
}
複製程式碼
現在,讓我們測試一下,從下圖中可以看到,當我們未獲取 token 時,訪問介面提示我們 401 Unauthorized,當我們模擬登入獲取到 token 資訊後,再次訪問受保護的資源時,已經可以獲取到響應的資料。之後,當我們重新整理 token,此時再用原來的 token 資訊訪問時,已經無法訪問,提示 403 Forbidden,同時,可以看到我們的 Redis 中已經存在了停用的 token 資訊,此時,使用新的 token 資訊又可以訪問了。
至此,整個的 Jwt 授權鑑權相關的程式碼就已經完成了,因為篇幅原因,完整的程式碼請到 Github 上進行檢視(電梯直達)。
總結
本章,主要是使用 Jwt 完成對於使用者的授權與鑑權,實現了對於使用者 token 令牌的建立、重新整理、停用以及校驗。在實際的開發中,採用成熟的輪子可能是更好的方案,如果你有針對 Jwt 進行使用者授權、鑑權更好的解決方案的話,歡迎你在評論區留言指出。拖了很久,應該是年前的最後一篇了,提前祝大家新年快樂哈~~~
佔坑
作者:墨墨墨墨小宇
個人簡介:96年生人,出生於安徽某四線城市,畢業於Top 10000000 院校。.NET程式設計師,槍手死忠,喵星人。於2016年12月開始.NET程式設計師生涯,微軟.NET技術的堅定堅持者,立志成為雲養貓的少年中面向谷歌程式設計最厲害的.NET程式設計師。
個人部落格:yuiter.com
部落格園部落格:www.cnblogs.com/danvic712