.Net Core 加入Windows驗證功能

雖然現在的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登入驗證
驗證失敗(401)的話,header會出現相關驗證方式
使用NTLM呼叫API得到回應200
使用JWT呼叫API得到回應200

取得驗證資訊
//驗證通過後,如果要在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

NTLM驗證沒有勾選
header也沒加上JWT

 

呼叫成功,回傳200

另外要注意的是,之前有遇到的問題。我把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: