雖然現在的WebAPI大部分採用Token方式來驗證,但企業內部的話,大部分還是會直接採用Windows驗證。
最近開發新專案,剛好有使用到身分驗證的相關功能,趁這次機會把.Net 的身分驗證相關問題紀錄一下。
註冊驗證服務
要使用Windows驗證(也稱為 Negotiate、Kerberos 或 NTLM 驗證),我們就必須要在Program.cs裡面註冊服務。
Windows驗證(Authentication)有兩種驗證方式,分別為NTLM跟Kerberos(Negotiate),兩者的差別可以參考:關於IIS整合式Windows驗證的冷知識)。
註冊服務時的參數也有區別:
IISDefaults.AuthenticationScheme:使用IIS on Windows進行Windows驗證
builder.Services.AddAuthentication(IISDefaults.AuthenticationScheme);
NegotiateDefaults.AuthenticationScheme:使用Kestrel on Linux進行Windows驗證
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();//Negotiate
但不管是使用IISDefaults.AuthenticationScheme還是NegotiateDefaults.AuthenticationScheme,.預設的驗證方式都會使用Kerberos(Negotiate)
另外補充,如果要加上JWT驗證的話,要另外註冊JWT服務
Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.IncludeErrorDetails = true; // 預設值為 true,有時會特別關閉
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true, //是否驗證Issuer
ValidIssuer = configuration["JwtSettings:Issuer"], //發行人Issuer
ValidateAudience = false, //是否驗證Audience
//ValidAudience = configuration["Jwt:Audience"], //訂閱人Audience
// 一般我們都會驗證 Token 的有效期間
ValidateLifetime = true,
// 如果 Token 中包含 key 才需要驗證,一般都只有簽章而已
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtSettings:SignKey"]))
};
});
加入MiddleWare
app.UseAuthentication();//驗證
app.UseAuthorization();//身分認證
launchSettings.json設定
前面的服務註冊以及MiddleWare都用好之後,還有一個地方要注意!
如果是本機Debug環境的,記得要把在iisSettings裡面加上下面設定。windowsAuthentication沒設定成true的話的話程式可是會回傳錯誤的!!
"windowsAuthentication": true,
API呼叫測試
如果有註冊三種驗證服務的話,則三種驗證方式都可以使用。以GetBoxingLocations為例,API支援我們在Program.cs註冊的三種驗證方式(Authentication issues with WWW-Authenticate: Negotiate)
- Bearer => JWT
- Negotiate(Kerberos) => Windows登入驗證
- NTLM(NTLM) => Windows登入驗證
取得驗證資訊
//驗證通過後,如果要在Controller取得相關的驗證資訊(ex:使用帳號. 名稱. email……):
var Operator = HttpContext.User.Identity.Name.Split('\\')[1];
使用Windows驗證的HttpContext.User.Identity,驗證相關資訊由Windows AD直接取得
如果有使用JWT驗證的話(於header加上bearer {JWT}),HttpContext.User.Identity驗證相關資訊要透過加在JWT的Claim裡面的資訊取得
取消驗證
- 拿掉[Authorize]
如果有某支API不需要經過身分驗證就能直接呼叫的話,只要把[Authorize]拿掉即可
[HttpGet()]
//[Authorize] /*把它註解掉*/
public async Task<Result<BoxingLocation[]>> GetBoxingLocations()
{
省略...
}
測試呼叫GetBoxingLocations
另外要注意的是,之前有遇到的問題。我把Controller的[Authorize]拿掉了,但每次呼叫API還是會回傳401說我沒有權限,但是我明明就把[Authorize]註解掉了阿。後來跟同事研究過後發現,如果Program.cs裡面的Middleware有加上RequireAuthorization()的話,就算你把Controller的[Authorize拿掉],他還是會進行身分驗證的。
app.UseEndpoints(endpoints =>{ endpoints.MapControllers().RequireAuthorization();/*不需要強制驗證的話,要把.RequireAuthorization()拿掉!*/});
- 使用匿名驗證
我們來做實驗,看看沒起用匿名驗證跟有啟用匿名驗證的差別,我們在Program.cs的MiddleWare加上,讓API一定要進行身分認證
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization();
});
把NTML驗證取消,測試呼叫API,回應401。
接著在API加上[AllowAnonymous],啟用匿名驗證。
[HttpGet()]
[AllowAnonymous]/*增加匿名驗證標籤*/
public async Task<Result<BoxingLocation[]>> GetBoxingLocations()
{
省略...
}
NTML驗證記得還是別勾選,測試呼叫API,不是回應401了(請忽略500error,這只是個測試API)。
Ref: