在涉及到後端項目的開發中,如何實現對於用戶權限的管控是須要咱們首先考慮的,在實際開發過程當中,咱們可能會運用一些已經成熟的解決方案幫助咱們實現這一功能,而在 Grapefruit.VuCore 這個項目中,我將使用 Jwt 的方式實現對於用戶的權限管控,在本章中,我將演示如何使用 Jwt 實現對於用戶的受權、鑑權。html
系列目錄地址:ASP.NET Core 項目實戰前端
倉儲地址:github.com/Lanesra712/…git
1、一些概念程序員
Jwt(json web token),是一種基於 Json 的無狀態受權令牌,由於 Jwt 是一種標準的數據傳輸規範,並非某家所獨有的技術規範,所以很是適用於構建單點登陸服務,爲 web、client、app 等等各類接口使用方提供受權服務。github
在使用 Jwt 進行權限控制的過程當中,咱們須要先請求受權服務器獲取到 token 令牌,將令牌存儲到客戶端本地(在 web 項目中,咱們能夠將 token 存儲到 localstorage 或是 cookie 中),以後,對於服務端的每一次請求,都須要將獲取到的 token 信息添加到 http 請求的 header 中。 web
$.ajax({
url: url,
method: "POST",
data: JSON.stringify(data),
beforeSend: function (xhr) {
/* Authorization header */
xhr.setRequestHeader("Authorization", "Bearer " + token);
},
success: function (data) {}
});
複製代碼
當用戶擁有令牌後是否就能夠訪問系統的全部功能了呢?答案固然否認的。對於一個系統來講可能會有多種用戶角色,每個用戶角色能夠訪問的資源也是不一樣的,因此,當用戶已經擁有令牌後,咱們還須要對用戶角色進行鑑定,從而作到對用戶進行進一步的權限控制。ajax
在 Grapefruit.VuCore 這個項目中,我採用的是基於策略的受權方式,經過定義一個受權策略來完善 Jwt 鑑權,以後將這個自定義策略注入到 IServiceCollection 容器中,對權限控制作進一步的完善,從而達到對於用戶訪問權限的管控。shell
基於策略的受權是微軟在 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 便可。json
[Authorize(Policy = "Permission")]
public class SecretController : ControllerBase
{}
複製代碼
2、受權
在 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 類中。
3、鑑權
在 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 =>
{
//一、Definition authorization policy
options.AddPolicy("Permission",
policy => policy.Requirements.Add(new PolicyRequirement()));
}).AddAuthentication(s =>
{
//二、Authentication
s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(s =>
{
//三、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