SWAGGER ve JWT TOKEN BASED WEBAPI Kullanımı
Husamettin Elalmis
Last updated
Husamettin Elalmis
Last updated
Merhaba arkadaşlar, bu yazıda sizlere Swagger ve JWT .NetCore WebAPI yapılandırmasını anlatacağım,
Yenilikler
Swagger, WebAPI projelerinde kullanılan ve .netCore projelerine entegre edilebilen 3th party bir API dokumantasyon aracıdır. Bu araç, PostMan'nın yapabildiklerini kısmen yapabilmektedir. Özellikle dokumantasyon işlemlerinde standard yöntemlerin kullanılabilmesi amacıyla geliştirilmiş ve kabul görmüştür.
Önceki .netCore versiyonlarında (3.0, 3.1) manuel bu araç ilave olarak kurulabiliyordu ve kullanılabiliyordu
.netCore 5.0 ile birlikte WebAPI projelerine embedded şeklinde (nugetpackage) kurulu halde gelmektedir.
Swagger, bizim controller classlarımızdaki method signaturlerini otomatik olarak okuyarak ekranda kendisi UI olarak göstermektedir.
UI üzerinde, methodların açıklamaları ve parametrelerini görebilmekte ve istenilen method test amaçlı invoke edebilmekteyiz. (Postman usulu)
JWT yapılandırmasını ise, nugetpackageler ile birlikte projeye kurularak ve doğru configure ayarları yaparak entegre yapıyor olacağız. (eskiden JWT yapılandırmaları daha meşakkatli idi, MVC taraflarında bu işlemlerin configure edilmesi biraz daha karmaşık idi)
Swagger Nedir?
.netCore5.0 ile entegre gelen API dokumantasyon ve API Test aracıdır.
UI otomatik olarak oluşturulmakta ve projenin start edilmesi ile birlikte ekrana gelmektedir
Swagger görünümü yukarıdaki gibidir. Methodlar, controller classından otomatik olarak okunarak gelmektedir.
İstenilen herhangi bir metodun parametreleri görülebilir, ve invoke(test) edilebilir. Postman gibi çağırabilmekteyiz.
İlgili methodun parametreleri görüntülenir. Try It Out butonu ile invoke edilebilir.
Execute butonu ile post denemeleri yapılabilmektedir
Swagger, artık standard hale gelmiş bir araçtır.
JWT Nedir?
JWT, JsonWebToken anlamına gelir. Web tabanlı Json formatında Token demektir. Token, bilet anlamına gelir. Güvenlik amacıyla geliştirilmiş bir mekanizmadır. Biz, bir API çağrısını yaptığımızda bu bile tile ilgili methodu çağırabiliyor olacağız. Bileti olmayan bu methodumuzu çağıramıyor olacak. Bu mekanizma ile güvenlik sağlanması amaçlanmıştır.
JWT Hakkında Bilinmesi Gereken Temel Kavramlar
JWT, bir json tokendir.
Örnek bir jwt tokeni şöyledir, eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJoZWxhbG1pcyIsImVtYWlsIjoiaHVzYW1ldHRpbi5lbGFsbWlzQGFya3NvZnQuY29tLnRyIiwianRpIjoiODFjNWM3N2EtMjk4Ni00MDZlLWE2MzQtMDNlZGNlNzM5YzE3IiwiZXhwIjoxNjI0Nzk4ODQyLCJpc3MiOiJhcmtzb2Z0LmNvbS50ciIsImF1ZCI6ImFya3NvZnQuY29tLnRyIn0.woj4Td0GrYN8aNVmSR1zpgCtPVp2fRE1LXlSTqXobSw
Bu token, 3 segmentten oluşmaktadır.
1.segment: Kullanılan algoritmanın tipini söyler.
2.segment: Bu token üzerinde yaşayan bilgileri söyler (claims diyoruz buna)
3.segment: Bu tokenin hash kodunu söyler. Hash kodu, kırılırsa token kırılmış olur. Hash kodu kırılamadığı sürece bu tokenın kırılamayacağı öngörülür. Bu tokena bilet diyoruz. Bu token ile methodlarımızı çağırıyor olacağız.
3 segmentli yapının açık görünümü. ( https://jwt.io/ adresini kullanabilirsiniz)
Tokenlar, belirtilen süre kadar yaşar. Süresi biten token 401 yer. (UnAuthorized)
Tokenlerin içerisinde Dünya Standartlarında oluşturulmuş yaşayan bilgiler vardır. Bunlara claims diyoruz. (Detaylı bilgi için https://datatracker.ietf.org/doc/html/rfc7519#section-4 adresini inceleyebilirsiniz )
Tokeni doğrulanmış bir istek, response alabilir. Buna 200 success diyoruz. Doğrulanmamış bir token veya süresi bitmiş bir token 401 UnAuthorized hatası alır.
Claimsler, isteğe bağlı olarak kullanılır, en çok kullanılanlar ise UserName, UserEmail gibi bilgilerdir. Bunlar token içerisinde yaşar. Uygulama, bu claims içerisindeki bilgileri kendi logic kontrollerini yaparak dbye sorarak diğer yetki mekanizmalarını devreye alır. Bu kısım bize kalmıştır.
JWT tokenlar, genellikle Post edilerek methoda gönderilir. Header kısmında "Bearer" ön eki alarak bu token sunucuya gönderilir. Sunucu, bu Bearer başlıklı headerin içerisindeki token bilgisini alır ve doğrulama işlemine tabi tutar. Doğrulama sağlanırsa geriye içerik döndürülür (200), doğrulama sağlanmazsa geriye 401 UnAuthorized döner. Bu nedenle, JWT projelerinde header kısımlarında mutlaka "Bearer <token>" şeklinde çağrımlar olur. (Postman tarafında da bunu kullanıyor olacağız)
PostMan Nedir?
RestAPI, SOAP vb. Web tabanlı API çağrılarını test etmeye yarayan 3th parti bir araçtır.
Postman, yukarıdaki gibi bir IDEye sahiptir.
https://www.postman.com/downloads/ adresinden masaüstü sürümünü indirebilirsiniz.
Özellikle, Testerlar tarafından kullanılan bir araçtır. Developerlar method çağrıları yaparken, testerlar güvenlik açıkları ile ilgilenir.
Örnek Proje Yapılandırması
.NetCore5.0 WebAPI projesi oluşturulmuştur
Swashbuckle (swagger) kurulu gelmiştir, diğer nugetpackageler kurulmuştur (JWT ile ilgilidir bunlar)
AppSettings.json Yapılandırması
{
“Logging”: {
“LogLevel”: {
“Default”: “Information”,
“Microsoft”: “Warning”,
“Microsoft.Hosting.Lifetime”: “Information”
}
},
“AllowedHosts”: “*”,
“Jwt”: {
“Key”: “arksoft easy2patch application”,
“Issuer”: “arksoft.com.tr”
}
}
Startup.cs Yapılandırması
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System;
using System.Text;
namespace TestWebApi
{
public
class
Startup
{
public
Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public
void ConfigureServices(IServiceCollection services)
{
// cors ayarları
services.AddCors(options =>
{
options.AddPolicy(“CorsPolicy”, builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials());
});
// jwt ayarları
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration[“Jwt:Issuer”],
ValidAudience = Configuration[“Jwt:Issuer”],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[“Jwt:Key”])),
ClockSkew = TimeSpan.Zero // bu önemli. yoksa token expire olmuyor
};
});
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc(“v1”, new OpenApiInfo { Title = “Easy2Patch Api”, Version = “v1” });
});
}
public
void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint(“/swagger/v1/swagger.json”, “Easy2Patch v1”));
}
app.UseHttpsRedirection();
app.UseRouting();
// jwt için. Burası önemli, authorizationun üstünde olmalı
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
UserModel.cs
namespace TestWebApi.Model
{
public
class
UserModel
{
public
string UserName { get; set; }
public
string Password { get; set; }
public
string Email { get; set; }
}
}
LoginRequestItem.cs
namespace TestWebApi.Model
{
public
class
LoginRequestItem
{
public
string UserName{ get; set; }
public
string Password { get; set; }
}
}
LoginController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using TestWebApi.Model;
namespace TestWebApi.Controllers
{
[Route(“api/[controller]”)]
[ApiController]
public
class
LoginController : ControllerBase
{
private IConfiguration _config;
public
LoginController(IConfiguration config)
{
_config = config;
}
[AllowAnonymous] // Herkese açık method
[HttpPost(“Login”)] // Uyarı: Swagger, bu şekilde istiyor yoksa hata verir.
public IActionResult Login([FromBody] LoginRequestItem obj) // LoginRequestItem obje olarak alması için. Parametre kullanmıyoruz.
{
UserModel model = new UserModel();
model.UserName = obj.UserName;
model.Password = obj.Password;
IActionResult response = Unauthorized();
var user = AuthenticateUser(model);
if (user != null)
{
var tokenStr = GenerateJSONWebToken(user); // token üretilir
response = Ok(new { token = tokenStr });
}
return response;
}
// AuthenticateUser – Login olan kullanıcıyı doğrula
private UserModel AuthenticateUser(UserModel model)
{
UserModel user = null;
// Burada DB seviyesinde araya girilir
if (model.UserName == “helalmis” && model.Password == “1”)
{
user = new UserModel() { UserName = “helalmis”, Password = “1”, Email = “husamettin.elalmis@arksoft.com.tr” };
}
return user;
}
[Authorize] // JWT ile çağrılsın
[HttpPost(“TestPost”)] // Uyarı: Swagger, bu şekilde istiyor yoksa hata verir.
public
string TestPost()
{
var identity = HttpContext.User.Identity as ClaimsIdentity;
IList<Claim> claim = identity.Claims.ToList();
var userName = claim[0].Value;
return
“Welcome to “ + userName;
}
[Authorize] // JWT ile çağrılsın
[HttpGet(“TestGet”)] // Uyarı: Swagger, bu şekilde istiyor yoksa hata verir.
public ActionResult<IEnumerable<string>> TestGet()
{
return
new
string[] { “val1”, “val2”, “val3” };
}
[AllowAnonymous] // Herkese açık method
[HttpGet(“TestAnonymous”)] // Uyarı: Swagger, bu şekilde istiyor yoksa hata verir.
public
string TestAnonymous()
{
return
“TestAnonymous ok”;
}
// GenerateJSONWebToken – token üretir
private
string GenerateJSONWebToken(UserModel user)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config[“Jwt:Key”]));
var createndials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
var token = new JwtSecurityToken(
issuer: _config[“Jwt:Issuer”],
audience: _config[“Jwt:Issuer”],
claims,
expires: DateTime.Now.AddSeconds(15), // 15 sn yaşam süresi. Bu süre bize kalmış
signingCredentials: createndials
);
var encodeToken = new JwtSecurityTokenHandler().WriteToken(token);
return encodeToken;
}
}
}
Uygulamanın Test Edilmesi
Uygulama Run edilir, yukarıdaki ekran görünür
PostMan İle JWT Testlerinin Yapılması
Login testinin yapılması (401) – hatalı şifre
Kullanıcı adı ve şifre doğru. 200 OK. Token aldık (15 sn yaşam süresi var.)
Token testinin çalıştığı görülür.
Süresi biten token için 401 UnAuthorized alındığı görülür.
Anonymous çağrılarının çalıştığı görülür
TestGet methodunun token ile çağrıldığı görülür
Süresi bitmiş olan tokenin 401 aldığı görülür. (Aynı method çağrılma esnasında)
Sonuç
WebAPI projelerinde Swagger kullanımı standard hale gelmiştir
Güvenlik tarafında genellikle JWT token mekanizması kullanılmaktadır
Bu projede tokeni biz ürettik, bunun farklı bir versiyonu AzureAD üzerinden yapılabilir. Tüm token mekanizmaları belirtilen bir application üzerinden AzureAD üzerinden alınmasına imkan vardır. Bu google da olabilir, Amazon da olabilir. Merkezi token mekanizma sağlayan her türlü provider kullanılabilir.
Tumark WebAPI Cloud projesi, JWT token mekanizmasını kullanıyor olacaktır.
Saygılarımla,
Hüsamettin ELALMIŞ – 27.06.2021
husamettin.elalmis@arksoft.com.tr