با سلام؛ در معماری پیاز، ساخت jwt، وظیفه کدوم بخش هست؟ کلاینت؟ دامین سرویس؟ اپلیکیشن سرویس ؟ اینفرا استراکچر؟
نظرات مطالب
<Project Sdk="Microsoft.NET.Sdk.Web"> <ItemGroup> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc2" /> </ItemGroup> </Project>
namespace OpenAPISwaggerDoc.Web { public class Startup { public void ConfigureServices(IServiceCollection services) { // ... services.AddSwaggerGen(setupAction => { setupAction.SwaggerDoc( name: "LibraryOpenAPISpecification", info: new Microsoft.OpenApi.Models.OpenApiInfo() { Title = "Library API", Version = "1", Description = "Through this API you can access authors and their books.", Contact = new Microsoft.OpenApi.Models.OpenApiContact() { Email = "name@site.com", Name = "DNT", Url = new Uri("https://www.dntips.ir") }, License = new Microsoft.OpenApi.Models.OpenApiLicense() { Name = "MIT License", Url = new Uri("https://opensource.org/licenses/MIT") } }); }); }
namespace OpenAPISwaggerDoc.Web { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ... app.UseHttpsRedirection(); app.UseSwagger(); // ... }
https://localhost:5001/swagger/LibraryOpenAPISpecification/swagger.json
[HttpGet("{authorId}")] public async Task<ActionResult<Author>> GetAuthor(Guid authorId) { var authorFromRepo = await _authorsService.GetAuthorAsync(authorId); if (authorFromRepo == null) { return NotFound(); } return Ok(_mapper.Map<Author>(authorFromRepo)); }
namespace OpenAPISwaggerDoc.Web { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ... app.UseHttpsRedirection(); app.UseSwagger(); app.UseSwaggerUI(setupAction => { setupAction.SwaggerEndpoint( "/swagger/LibraryOpenAPISpecification/swagger.json", "Library API"); }); // ... }
app.UseSwaggerUI(setupAction => { setupAction.SwaggerEndpoint( "/swagger/LibraryOpenAPISpecification/swagger.json", "Library API"); setupAction.RoutePrefix = ""; });
POST /accounts/ClientLogin HTTP/1.1
User-Agent: curl/7.15.1 (i486-pc-linux-gnu) libcurl/7.15.1
OpenSSL/0.9.8a zlib/1.2.3 libidn/0.5.18
Host: www.google.com
Accept: */*
Content-Length: 103
Content-Type: application/x-www-form-urlencoded
accountType=GOOGLE&Email=userName@google.com&Passwd=myPasswrd&source=curl-tester-1.0&service=analytics
HTTP/1.1 200 OK
Content-Type: text/plain
Cache-control: no-cache
Pragma: no-cache
Date: Mon, 02 Jun 2008 22:08:51 GMT
Content-Length: 497
SID=DQ...
LSID=DQAA...
Auth=DQAAAG8...
string getSecurityToken()
{
if (string.IsNullOrEmpty(Email))
throw new NullReferenceException("Email is required!");
if (string.IsNullOrEmpty(Password))
throw new NullReferenceException("Password is required!");
WebRequest request = WebRequest.Create("https://www.google.com/accounts/ClientLogin");
request.Method = "POST";
string postData = "accountType=GOOGLE&Email=" + Email + "&Passwd=" + Password + "&service=analytics&source=vahid-testapp-1.0";
byte[] byteArray = Encoding.ASCII.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
using (Stream dataSt = request.GetRequestStream())
{
dataSt.Write(byteArray, 0, byteArray.Length);
}
string auth = string.Empty;
using (WebResponse response = request.GetResponse())
{
using (Stream dataStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(dataStream))
{
string responseFromServer = reader.ReadToEnd().Trim();
string[] tokens = responseFromServer.Split('\n');
foreach (string token in tokens)
{
if (token.StartsWith("SID="))
continue;
if (token.StartsWith("LSID="))
continue;
if (token.StartsWith("Auth="))
{
auth = token.Substring(5);
}
else
{
throw new AuthenticationException("Error authenticating Google user " + Email);
}
}
}
}
}
return auth;
}
string getAvailableProfiles(string authToken)
{
return fetchPage("https://www.google.com/analytics/feeds/accounts/default", authToken);
}
string url = string.Format("https://www.google.com/analytics/feeds/data?ids={0}&metrics=ga:pageviews&start-date={1}&end-date={2}", id, from, to);
return fetchPage(url, auth);
CGoogleAnalytics cga = new CGoogleAnalytics
{
Email = "username@gmail.com",
Password = "password",
From = DateTime.Now.Subtract(TimeSpan.FromDays(1)),
To = DateTime.Now.Subtract(TimeSpan.FromDays(1))
};
List<CGoogleAnalytics.SitePagePreviews> pagePreviews =
cga.GetTotalNumberOfPageViews();
foreach (var list in pagePreviews)
{
//string site = list.Site;
//int pw = list.PagePreviews;
}
public class IdentityUser<TKey> where TKey : IEquatable<TKey> { [PersonalData] public virtual TKey Id { get; set; }
var personalData = new Dictionary<string, string>(); var personalDataProps = typeof(TUser).GetProperties().Where( prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); foreach (var p in personalDataProps) { personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); }
Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); return new FileContentResult(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(personalData)), "text/json");
var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } RequirePassword = await _userManager.HasPasswordAsync(user); if (RequirePassword) { if (!await _userManager.CheckPasswordAsync(user, Input.Password)) { ModelState.AddModelError(string.Empty, "Password not correct."); return Page(); } }
var result = await _userManager.DeleteAsync(user);
await _signInManager.SignOutAsync();
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" /> <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="4.0.1" /> <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="4.5.2" />
public static class ServiceCollectionExtensions { public static void AddCustomSwagger(this IServiceCollection services) { services.AddSwaggerGen(options => { options.EnableAnnotations(); options.DocumentFilter<AuthenticationDocumentFilter>(); options.SwaggerDoc("v1", new Info { Version = "v1", Title = "Test API" }); }); } public static void UseSwaggerAndUI(this IApplicationBuilder app) { app.UseSwagger(); app.UseSwaggerUI(options => { options.DocExpansion(DocExpansion.None); options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API Docs"); }); } }
public class AuthenticationDocumentFilter : IDocumentFilter { private readonly IHttpContextAccessor httpContextAccessor; public AuthenticationDocumentFilter(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor; } public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) { if (!httpContextAccessor.HttpContext.User.Identity.IsAuthenticated) { swaggerDoc.Definitions = new Dictionary<string, Schema>(); swaggerDoc.Paths = new Dictionary<string, PathItem>(); } } }
public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context) { if (!httpContextAccessor.HttpContext.User.Identity.IsAuthenticated) { foreach (var item in swaggerDoc.Paths) { item.Value.Post = null; item.Value.Put = null; } } }
public void ConfigureServices(IServiceCollection services) { services.AddHttpContextAccessor(); services.AddAuthorization(); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options=> { options.AccessDeniedPath = "/Login"; options.Cookie.HttpOnly = true; options.LoginPath = "/Login"; options.LogoutPath = "/Login"; options.ExpireTimeSpan = TimeSpan.FromDays(15); options.SlidingExpiration = true; options.Cookie.IsEssential = true; options.ReturnUrlParameter = "returnUrl"; }); services.AddMvc(); services.AddCustomSwagger(); }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseAuthentication(); app.UseSwaggerAndUI(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
using System; using System.Linq; using System.Web.Mvc; using System.Security.Cryptography; using System.Text; using System.Web.Caching; namespace Parsnet.Core { public class StopSpamAttribute : ActionFilterAttribute { // حداقل زمان مجاز بین درخواستها برحسب ثانیه public int DelayRequest = 10; // پیام خطایی که در صورت رسیدن درخواست غیرمجاز باید صادر کنیم public string ErrorMessage = "درخواستهای شما در مدت زمان معقولی صورت نگرفته است."; //خصوصیتی برای تعیین اینکه آدرس درخواست هم به شناسه یکتا افزوده شود یا خیر public bool AddAddress = true; public override void OnActionExecuting(ActionExecutingContext filterContext) { // درسترسی به شئی درخواست var request = filterContext.HttpContext.Request; // دسترسی به شیئ کش var cache = filterContext.HttpContext.Cache; // کاربر IP بدست آوردن var IP = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress; // مشخصات مرورگر var browser = request.UserAgent; // در اینجا آدرس درخواست جاری را تعیین میکنیم var targetInfo = (this.AddAddress) ? (request.RawUrl + request.QueryString) : ""; // شناسه یکتای درخواست var Uniquely = String.Concat(IP, browser, targetInfo); //در اینجا با کمک هش یک امضا از شناسهی درخواست ایجاد میکنیم var hashValue = string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(Uniquely)).Select(s => s.ToString("x2"))); // ابتدا چک میکنیم که آیا شناسهی یکتای درخواست در کش موجود نباشد if (cache[hashValue] != null) { // یک خطا اضافه میکنیم ModelState اگر موجود بود یعنی کمتر از زمان موردنظر درخواست مجددی صورت گرفته و به filterContext.Controller.ViewData.ModelState.AddModelError("ExcessiveRequests", ErrorMessage); } else { // اگر موجود نبود یعنی درخواست با زمانی بیشتر از مقداری که تعیین کردهایم انجام شده // پس شناسه درخواست جدید را با پارامتر زمانی که تعیین کرده بودیم به شیئ کش اضافه میکنیم cache.Add(hashValue, true, null, DateTime.Now.AddSeconds(DelayRequest), Cache.NoSlidingExpiration, CacheItemPriority.Default, null); } base.OnActionExecuting(filterContext); } } }
[HttpPost] [StopSpam(DelayRequest = 5)] [ValidateAntiForgeryToken] public virtual async Task<ActionResult> SendFile(HttpPostedFileBase file, int userid = 0) { } [HttpPost] [StopSpam(DelayRequest = 30, ErrorMessage = "زمان لازم بین ارسال هر مطلب 30 ثانیه است")] [ValidateAntiForgeryToken] public virtual async Task<ActionResult> InsertPost(NewPostModel model) { }
[HttpPost] [StopSpam(AddAddress = true, DelayRequest = 20)] [ValidateAntiForgeryToken] public Task<ActionResult> InsertPost(NewPostModel model) { if (ModelState.IsValid) { var newPost = dbContext.InsertPost(model); if (newPost != null) { ViewBag.ExecuteResult = true; } } if (ModelState.IsValidField("ExcessiveRequests") == true)
{
ViewBag.ExecuteResult = false;
}
return View(); }
ng build -prod --output-hashing=none
<script type="text/javascript" asp-src-include="*.js"></script>
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="icon" type="image/x-icon" href="favicon.ico"> <title>ng2-lab</title> <base href="/"> <environment names="Development"> </environment> <environment names="Staging,Production"> <link rel="stylesheet" asp-href-include="~/styles*.css" /> </environment> </head> <body> @RenderBody() <app-root></app-root> <environment names="Development"> <script type="text/javascript" src="/inline.bundle.js"></script> <script type="text/javascript" src="/polyfills.bundle.js"></script> <script type="text/javascript" src="/scripts.bundle.js"></script> <script type="text/javascript" src="/styles.bundle.js"></script> <script type="text/javascript" src="/vendor.bundle.js"></script> <script type="text/javascript" src="/main.bundle.js"></script> </environment> <environment names="Production,Staging"> <script type="text/javascript" asp-src-include="~/inline*.js"></script> <script type="text/javascript" asp-src-include="~/polyfills*.js"></script> <script type="text/javascript" asp-src-include="~/scripts*.js"></script> <script type="text/javascript" asp-src-include="~/vendor*.js"></script> <script type="text/javascript" asp-src-include="~/main*.js"></script> </environment> </body> </html>
[name].[hash].bundle.js
app.Use(async (context, next) => { await next(); var path = context.Request.Path.Value; if (path != null && context.Response.StatusCode == 404 && !Path.HasExtension(path) && !path.StartsWith("/api/", StringComparison.OrdinalIgnoreCase)) { context.Request.Path = "/index.html"; await next(); } });
//context.Request.Path = "/index.html"; context.Request.Path = "/"; // since we are using views/shared/_layout.cshtml now.
سلام؛ ممنون بابت پاسختون. این طوری به forgeryToken نگاه نکرده بودم.
در صفحه برای Delete و ثبت اطلاعات و یا حتی فیلترکردن اطلاعات و هرجا که کاربر اطلاعاتی وارد میکند از AntiForgeryToken استفاده کردم و حذف و ورود اطلاعات و فیلتر کلا در یک صفحه اما هرکدام در تگ Form مجزایی ارسال و دریافت میشود و در هر تگ Form یک ForgeryToken گذاشتم.
برای اونها چه کار کنم؟ اصلا کار بنده درست بوده؟ من هرکجا کاربر اطلاعاتی وارد کرده از Token استفاده کردم ولی مشکل اینجاست که با توجه به طراحی کل اونها نیازه که در یک صفحه باشه.
می تونم یک AntiForgeryToken کلی داشته باشم و با جاوا اسکریپت اون و بخونم و به Form.Serilaise() اضافه کنم. اما میخواستم راه بهینه اون و بپرسم.
«... تمامی توکنهای شخص حذف میشوند ...»