لاگ یک بخش کلیدی از نگهداری و داشتن تضمین کارایی یک سیستم در طول زمانه. طبیعت یک سیستم نرمافزاری پیچیده است و قرار نیست همیشه همه چیز عالی و درست پیش بره. در هر لحظه یک سیستم نرمافزاری ممکنه با خطا و مشکل ناخواستهای مواجه بشه که در زمان طراحی و پیادهسازی کدها به اون توجهی نشده. لاگ کردن به ما کمک میکنه که برنامهی خودمون رو بهتر درک کنیم.
اشتراکها
در سالهای اخیر پروژههای نرمافزاری بزرگتر، پیچیدهتر و دارای نیازمندهای بیشتری شدهاند. شاید در دهههای قبلی یک پروژهی نرمافزاری شامل چند صد نفر ساعت کار و حجم دیتابیس پروژه بیش از یک گیگابایت نبود اما این روزها کار کردن بر روی پروژههای با چند ده هزار ساعت نفر کار و حجم دیتابیس ترابایتی حتی در ایران هم یک امر مرسوم و متداول شده است. با هم نیمنگاهی به تکنولوژیهای مطرح و معروف سال 2019 برای انجام یک پروژهی متوسط و یا بزرگ میاندازیم.
چند روز قبل، یکی از کانالهای فنی معروف یوتیوب با بیش از 15 میلیون مشترک، هک و پاک شد! که داستان آنرا در اینجا میتوانید پیگیری کنید. در این هک، مهاجم در سعی اول، پیشنهاد پشتیبانی مالی از شبکه را داده و در ایمیل دوم، پس از جلب اعتماد اولیه، یک فایل به ظاهر PDF مفاد قرارداد را ارسال کرده که با کلیک بر روی آن، تمام کوکیهای یوتیوب مالک کانال، سرقت و مورد سوء استفاده قرار گرفته! در یک چنین حالتی، مهم نیست که شما اعتبارسنجی دو مرحلهای را فعال کردهاید و یا از بهترین روشهای رمزنگاری برای امن کردن اطلاعات کوکی و یا توکن خود استفاده کردهاید، همینقدر که اصل محتوای کوکی و یا توکن شما در اختیار شخص دیگری قرار گیرد، میتواند بدون نیاز به لاگین و دانستن کلمهی عبور شما، بجای شما وارد سیستم شده و تغییرات دلخواهی را اعمال کند!
بنابراین سؤال اینجاست که ما (توسعه دهندگان) چگونه میتوانیم یک چنین حملاتی را مشکلتر کنیم؟ در این مطلب روشی را در جهت سعی در غیرمعتبر کردن توکنها و یا کوکیهای سرقت شده، در برنامههای مبتنی بر ASP.NET Core بررسی خواهیم کرد.
توسعهی یک سرویس تشخیص مرورگر و سیستم عامل شخص وارد شدهی به سیستم
یکی از روشهای غیرممکن کردن یک چنین حملاتی، درج مشخصات سیستم عامل و مرورگر شخص وارد شدهی به سیستم، در کوکی و همچنین توکن صادر شدهی حاصل از اعتبارسنجی موفق است. سپس زمانیکه قرار است از اطلاعات این کوکی و یا توکن در برنامه استفاده شود، این اطلاعات را با اطلاعات درخواست جاری کاربر مقایسه کرده و در صورت عدم تطابق، درخواست او را برگشت میزنیم. برای مثال اگر عملیات لاگین، در ویندوز انجام شده و اکنون توکن و یا کوکی حاصل، در سیستم عامل اندروید در حاصل استفادهاست، یعنی ... این عملیات مشکوک است و باید خاتمه یابد و کاربر باید مجبور به لاگین مجدد شود و نه اعتبارسنجی خودکار بدون زحمت!
برای این منظور میتوان از کتابخانهی UA-Parser استفاده کرد و توسط آن سرویس زیر را توسعه داد:
توضیحات:
اصل کار این سرویس در متد زیر رخ میدهد:
در اینجا با استفاده از کتابخانهی UA-Parser، سعی میکنیم تا جزئیات مرورگر و سیستم عامل شخص را تهیه کنیم. سپس در قسمت دیگری از این سرویس، این اطلاعات را هش میکنیم. از این جهت که هم حجم آن کاهش یابد و بیجهت کوکی و یا توکن ما را حجیم نکند و هم بررسی محتوای آن جهت شبیه سازی آن، غیرممکن شود. هر مشخصات دریافتی در حین لاگین، همواره یک هش مشخص و یکتا را دارد. به همین جهت متدهای هش کردن اطلاعات را هم در اینجا مشاهده میکنید. به علاوهی متد HasUserTokenValidDeviceDetails که کار آن، دریافت Claim مرتبط با این اطلاعات، از کوکی و یا توکن جاری و مقایسهی آن با اطلاعات Http Request جاری است. اگر این دو یکی نبودند، یعنی احتمال سوء استفادهی از اطلاعات شخص، وجود دارد.
اضافه کردن اطلاعات مشخصات دستگاه کاربر به کوکی و یا توکن او
همانطور که عنوان شد، در متد HasUserTokenValidDeviceDetails، ابتدا مشخصات دستگاه موجود در کوکی و یا توکن دریافتی، استخراج میشود. به همین جهت نیاز است این مشخصات را دقیقا در حین لاگین موفق، به صورت یک Claim جدید، برای مثال از نوع ClaimTypes.System به مجموعهی Claims کاربر اضافه کرد:
یکپارچه کردن DeviceDetectionService با اعتبارسنجهای کوکیها و توکنها
پس از افزودن مشخصات سیستم کاربر وارد شدهی به سیستم، به صورت یک Claim جدید به توکنها، روش اعتبارسنجی اطلاعات موجود در توکن رسیده، در رخداد گردان OnTokenValidated است که امکان دسترسی به HttpContext و محتوای توکن را میسر میکند:
و یا اگر از کوکیها استفاده میکنید، معادل آن به صورت زیر است:
در کل تمام تغییرات مورد نیاز مرتبط را جهت یک برنامهی تولید کنندهی JWT در اینجا و برای یک برنامهی مبتنی بر کوکیها در اینجا میتوانید مشاهده کنید.
بنابراین سؤال اینجاست که ما (توسعه دهندگان) چگونه میتوانیم یک چنین حملاتی را مشکلتر کنیم؟ در این مطلب روشی را در جهت سعی در غیرمعتبر کردن توکنها و یا کوکیهای سرقت شده، در برنامههای مبتنی بر ASP.NET Core بررسی خواهیم کرد.
توسعهی یک سرویس تشخیص مرورگر و سیستم عامل شخص وارد شدهی به سیستم
یکی از روشهای غیرممکن کردن یک چنین حملاتی، درج مشخصات سیستم عامل و مرورگر شخص وارد شدهی به سیستم، در کوکی و همچنین توکن صادر شدهی حاصل از اعتبارسنجی موفق است. سپس زمانیکه قرار است از اطلاعات این کوکی و یا توکن در برنامه استفاده شود، این اطلاعات را با اطلاعات درخواست جاری کاربر مقایسه کرده و در صورت عدم تطابق، درخواست او را برگشت میزنیم. برای مثال اگر عملیات لاگین، در ویندوز انجام شده و اکنون توکن و یا کوکی حاصل، در سیستم عامل اندروید در حاصل استفادهاست، یعنی ... این عملیات مشکوک است و باید خاتمه یابد و کاربر باید مجبور به لاگین مجدد شود و نه اعتبارسنجی خودکار بدون زحمت!
برای این منظور میتوان از کتابخانهی UA-Parser استفاده کرد و توسط آن سرویس زیر را توسعه داد:
using System.Security.Claims; using Microsoft.AspNetCore.Http; using Microsoft.Net.Http.Headers; using UAParser; namespace ASPNETCore2JwtAuthentication.Services; /// <summary> /// To invalidate an old user's token from a new device /// </summary> public class DeviceDetectionService : IDeviceDetectionService { private readonly IHttpContextAccessor _httpContextAccessor; private readonly ISecurityService _securityService; public DeviceDetectionService(ISecurityService securityService, IHttpContextAccessor httpContextAccessor) { _securityService = securityService ?? throw new ArgumentNullException(nameof(securityService)); _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); } public string GetCurrentRequestDeviceDetails() => GetDeviceDetails(_httpContextAccessor.HttpContext); public string GetDeviceDetails(HttpContext context) { var ua = GetUserAgent(context); if (ua is null) { return "unknown"; } var client = Parser.GetDefault().Parse(ua); var deviceInfo = client.Device.Family; var browserInfo = $"{client.UA.Family}, {client.UA.Major}.{client.UA.Minor}"; var osInfo = $"{client.OS.Family}, {client.OS.Major}.{client.OS.Minor}"; //TODO: Add the user's IP address here, if it's a banking system. return $"{deviceInfo}, {browserInfo}, {osInfo}"; } public string GetDeviceDetailsHash(HttpContext context) => _securityService.GetSha256Hash(GetDeviceDetails(context)); public string GetCurrentRequestDeviceDetailsHash() => GetDeviceDetailsHash(_httpContextAccessor.HttpContext); public string GetCurrentUserTokenDeviceDetailsHash() => GetUserTokenDeviceDetailsHash(_httpContextAccessor.HttpContext?.User.Identity as ClaimsIdentity); public string GetUserTokenDeviceDetailsHash(ClaimsIdentity claimsIdentity) { if (claimsIdentity?.Claims == null || !claimsIdentity.Claims.Any()) { return null; } return claimsIdentity.FindFirst(ClaimTypes.System)?.Value; } public bool HasCurrentUserTokenValidDeviceDetails() => HasUserTokenValidDeviceDetails(_httpContextAccessor.HttpContext?.User.Identity as ClaimsIdentity); public bool HasUserTokenValidDeviceDetails(ClaimsIdentity claimsIdentity) => string.Equals(GetCurrentRequestDeviceDetailsHash(), GetUserTokenDeviceDetailsHash(claimsIdentity), StringComparison.Ordinal); private static string GetUserAgent(HttpContext context) { if (context is null) { return null; } return context.Request.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgent) ? userAgent.ToString() : null; } }
اصل کار این سرویس در متد زیر رخ میدهد:
public string GetDeviceDetails(HttpContext context) { var ua = GetUserAgent(context); if (ua is null) { return "unknown"; } var client = Parser.GetDefault().Parse(ua); var deviceInfo = client.Device.Family; var browserInfo = $"{client.UA.Family}, {client.UA.Major}.{client.UA.Minor}"; var osInfo = $"{client.OS.Family}, {client.OS.Major}.{client.OS.Minor}"; //TODO: Add the user's IP address here, if it's a banking system. return $"{deviceInfo}, {browserInfo}, {osInfo}"; }
اضافه کردن اطلاعات مشخصات دستگاه کاربر به کوکی و یا توکن او
همانطور که عنوان شد، در متد HasUserTokenValidDeviceDetails، ابتدا مشخصات دستگاه موجود در کوکی و یا توکن دریافتی، استخراج میشود. به همین جهت نیاز است این مشخصات را دقیقا در حین لاگین موفق، به صورت یک Claim جدید، برای مثال از نوع ClaimTypes.System به مجموعهی Claims کاربر اضافه کرد:
new(ClaimTypes.System, _deviceDetectionService.GetCurrentRequestDeviceDetailsHash(), ClaimValueTypes.String, _configuration.Value.Issuer),
- نمونهی انجام اینکار در یک برنامهی تولید کنندهی JWT
- نمونهی انجام اینکار در یک برنامهی تولید کنندهی کوکی
یکپارچه کردن DeviceDetectionService با اعتبارسنجهای کوکیها و توکنها
پس از افزودن مشخصات سیستم کاربر وارد شدهی به سیستم، به صورت یک Claim جدید به توکنها، روش اعتبارسنجی اطلاعات موجود در توکن رسیده، در رخداد گردان OnTokenValidated است که امکان دسترسی به HttpContext و محتوای توکن را میسر میکند:
.AddJwtBearer(cfg => { cfg.Events = new JwtBearerEvents { OnTokenValidated = context => { var tokenValidatorService = context.HttpContext.RequestServices.GetRequiredService<ITokenValidatorService>(); return tokenValidatorService.ValidateAsync(context); }, }; });
.AddCookie(options => { options.Events = new CookieAuthenticationEvents { OnValidatePrincipal = context => { var cookieValidatorService = context.HttpContext.RequestServices.GetRequiredService<ICookieValidatorService>(); return cookieValidatorService.ValidateAsync(context); } }; });
- نمونهی کامل انجام اینکار در یک برنامهی تولید کنندهی JWT
- نمونهی کامل انجام اینکار در یک برنامهی تولید کنندهی کوکی
در کل تمام تغییرات مورد نیاز مرتبط را جهت یک برنامهی تولید کنندهی JWT در اینجا و برای یک برنامهی مبتنی بر کوکیها در اینجا میتوانید مشاهده کنید.
بوت استرپ، به همراه کامپوننتهایی برای پیاده سازی اعمال متداول طرحبندی صفحات است؛ مانند jumbotron ،media ،table و card.
کامپوننت jumbotron
از Jumbotron برای نمایش متنی مشخص در بالای یک صفحه، استفاده میشود. دو روش استفادهی از آن در بوت استرپ 4 وجود دارند:
- داخل container:
با این خروجی:
در اینجا با اعمال کلاس jumbotron، متن header، داخل یک قاب با گوشههای گرد قرار میگیرد و مشخصتر نمایش داده خواهد شد. همچنین با mt-4، فاصلهای را بین آن و بالای صفحه ایجاد کردهایم.
- خارج از container:
با این خروجی:
اگر میخواهیم این قاب، تمام عرض صفحه را پر کند و همچمنین لبههای گرد آن نیز حذف شوند، میتوان از کلاس jumbotron-fluid استفاده کرد و آنرا خارج از container قرار داد. سپس برای اینکه متن داخل آن با container زیر آن تراز شود، میتوان یک container را در اینجا داخل jumbotron تعریف کرد.
کنترل ظاهر جداول، در بوت استرپ 4
بوت استرپ 4 به همراه تعدادی کلاس ویژه است که برای بهبود ظاهر المان استاندارد جدول، ارائه شدهاند. آنها را در طی مثالهایی بررسی خواهیم کرد.
برای رسیدن به چنین تصویری، تغییرات زیر را بر روی یک جدول استاندارد HTML اعمال کردهایم:
- کلاس table، کلاس پایه اعمال شیوهنامههای بوت استرپ 4 به المان جدول است که سبب خواهد شد آیتمهای آن با فاصلهی بهتری نسبت به یکدیگر ظاهر شوند. با استفاده از کلاس table-dark میتوان یک قالب مشکی را به جدول اعمال کرد.
- کلاس table-striped سبب میشود تا ردیفها، یک در میان با رنگی متمایز نمایش داده شوند.
- با افزودن table-hover، رنگ ردیفهای جدول با عبور اشارهگر ماوس از روی آنها تغییر میکند.
- کلاس table-bordered کار نمایش قاب جدول را انجام میدهد.
- کلاس table-responsive سبب میشود تا در اندازههای کوچک صفحه، یک اسکرول بار افقی برای نمایش آیتمهای جدول ظاهر شود و یا میتوان از کلاس table-sm نیز استفاده کرد تا padding تعریف شدهی در جدول، کاهش یابند. این کلاس، قابلیت پذیرش break-pointها را نیز دارد؛ مانند table-responsive-md.
- کلاسهای thead-light و یا thead-dark که بر روی تگ thead قرار میگیرند، رنگ پس زمینهی هدر جدول را مشخص میکنند.
- برای تغییر رنگ پس زمینه و متن یک ردیف میتوان از کلاسهای bg-color و text-color استفاده کرد:
- برای تغییر رنگ سلولهای جدول از کلاسهای table-color استفاده میکنیم:
فرمولهای رنگهای قابل اعمال به ردیفها، سلولها و متون جداول بوت استرپ 4 را در تصویر ذیل مشاهده میکنید:
کامپوننت جدید card در بوت استرپ 4
پنلهای بوت استرپ 3 حذف و بجای آن کامپوننت جدیدی به نام card در نگارش 4 آن ارائه شدهاست که با افزودن کلاس آن به یک div، بلافاصله قابی با گوشههای گرد به آن اضافه میشود.
- برای اینکه عناصر داخل card با فاصلهی مناسبی از لبههای آن قرار گیرند و همچنین شیوهنامههای قسمتهای مختلف آن به درستی اعمال شوند، نیاز است محتوای section ای که با کلاس card مشخص شده (تعیین container)، داخل یک div با کلاس card-body قرار گیرد. در اینجا امکان تعریف card-header و card-footer نیز وجود دارد.
- سپس یک card میتواند دارای تصویری واکنشگرا باشد که عرض card را پوشش میدهد. این تصویر با کلاس card-img مشخص میشود.
در اینجا امکان تعریف card-img-top و card-img-bottom نیز وجود دارند. این موارد تصویر card را در بالا و یا پایین آن، بدون padding، نمایش میدهند. اگر میخواهید متنی را بر روی این تصویر نمایش دهید، از کلاس card-img-overlay استفاده کنید. در این حالتها باید تصویر را خارج از card-body قرار دهید.
- عنوان و زیرعنوان یک card، توسط کلاسهای card-title و card-subtitle تعیین میشوند.
- متن داخل آنرا با کلاس card-text مشخص میکنیم.
- لینکهای ذیل آن نیز توسط کلاس card-link در طی یک ردیف نمایش داده میشوند.
امکان تعیین رنگ پس زمینه، حاشیه و متن یک card نیز وجود دارند:
با این خروجی:
و فرمول کلی رنگهای آن نیز به صورت زیر میباشد:
میتوان برای یک card، هدر و فوتر نیز تعریف کرد:
در اینجا همان card قبلی را مشاهده میکنید که عناوین آن به card-header و لینکهای ذیل آن به card-footer منتقل شدهاند:
برای تعریف یک list-group در داخل یک card، به صورت زیر عمل میکنیم:
ابتدا list-group را به خارج از card-body منتقل میکنیم. سپس برای حذف حاشیهی آن و همچنین گوشههای گرد آن، جهت یکی شدن با قاب card، کلاس list-group-flush را به آن اضافه میکنیم:
تعیین نحوهی چیدمان cards در بوت استرپ 4
اگر چندین card در یک صفحه تعریف شدهاند، برای تعیین نحوهی قرارگیری آنها در کنار یکدیگر میتوان یا از سیستم طرحبندی متداول بوت استرپ استفاده کرده و یا امکان تعریف گروهی از آنها نیز وجود دارد. برای اینکار کافی است یک div با کلاس card-group را تعریف و سپس تمام cards را داخل آن قرار دهیم:
که سبب خواهد شد تمام cards در کنار یکدیگر بدون فاصلهای نمایش داده شوند. اگر بجای آن از کلاس card-deck استفاده شود، فاصلهای بین cards قرار میگیرد.
اگر از کلاس card-columns استفاده کنیم، تمام cards را به صورت خودکار در ستونها و ردیفها، قرار میدهد که بعضی از آنها بلندتر و بعضی دیگر کوتاهتر هستند (نوعی نمایش کاشیکاری شدهاست):
ولی در کل اگر نیاز به کنترل بیشتری دارید، از همان روش متداول تعریف ردیفها و ستونهای سیستم طرحبندی بوت استرپ استفاده کنید.
المان media در بوت استرپ 4
برای نمایش متداول متن و تصویر که قرار است تصویر، در یک ستون و متن، در ستونی دیگر باشد، بوت استرپ 4 به همراه کلاس media است که بر اساس Flexbox بازنویسی شدهاست.
با این خروجی:
ابتدا توسط کلاس media یک container را تعریف میکنیم. سپس تصویر، یک ستون و media-body ستون دیگر را تشکیل میدهد.
با استفاده از d-flex، المان تصویر را به یک Flexbox container تبدیل کرده و با استفاده از کلاس align-self-center، آنرا در میانهی ستون قرار میدهیم. همچنین در اینجا توسط mr-3، فاصلهی آنرا با متن ستون کناری تنظیم کردهایم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: Bootstrap4_09.zip
کامپوننت jumbotron
از Jumbotron برای نمایش متنی مشخص در بالای یک صفحه، استفاده میشود. دو روش استفادهی از آن در بوت استرپ 4 وجود دارند:
- داخل container:
<div class="container"> <header class="jumbotron mt-4"> <div class="display-2 mb-4">Our Mission</div> <p class="lead">Wisdom Pet Medicine strives to blend the best in traditional and alternative medicine in the diagnosis and treatment of companion animals including dogs, cats, birds, reptiles, rodents, and fish. We apply the wisdom garnered in the centuries old tradition of veterinary medicine, to find the safest treatments and cures.</p> </header>
در اینجا با اعمال کلاس jumbotron، متن header، داخل یک قاب با گوشههای گرد قرار میگیرد و مشخصتر نمایش داده خواهد شد. همچنین با mt-4، فاصلهای را بین آن و بالای صفحه ایجاد کردهایم.
- خارج از container:
<header class="jumbotron jumbotron-fluid"> <div class="container"> <div class="display-2 mb-4">Our Mission</div> <p class="lead">Wisdom Pet Medicine strives to blend the best in traditional and alternative medicine in the diagnosis and treatment of companion animals including dogs, cats, birds, reptiles, rodents, and fish. We apply the wisdom garnered in the centuries old tradition of veterinary medicine, to find the safest treatments and cures.</p> </div> </header>
اگر میخواهیم این قاب، تمام عرض صفحه را پر کند و همچمنین لبههای گرد آن نیز حذف شوند، میتوان از کلاس jumbotron-fluid استفاده کرد و آنرا خارج از container قرار داد. سپس برای اینکه متن داخل آن با container زیر آن تراز شود، میتوان یک container را در اینجا داخل jumbotron تعریف کرد.
کنترل ظاهر جداول، در بوت استرپ 4
بوت استرپ 4 به همراه تعدادی کلاس ویژه است که برای بهبود ظاهر المان استاندارد جدول، ارائه شدهاند. آنها را در طی مثالهایی بررسی خواهیم کرد.
برای رسیدن به چنین تصویری، تغییرات زیر را بر روی یک جدول استاندارد HTML اعمال کردهایم:
<table class="table table-striped table-hover table-bordered table-responsive"> <thead class="thead-light">
- کلاس table-striped سبب میشود تا ردیفها، یک در میان با رنگی متمایز نمایش داده شوند.
- با افزودن table-hover، رنگ ردیفهای جدول با عبور اشارهگر ماوس از روی آنها تغییر میکند.
- کلاس table-bordered کار نمایش قاب جدول را انجام میدهد.
- کلاس table-responsive سبب میشود تا در اندازههای کوچک صفحه، یک اسکرول بار افقی برای نمایش آیتمهای جدول ظاهر شود و یا میتوان از کلاس table-sm نیز استفاده کرد تا padding تعریف شدهی در جدول، کاهش یابند. این کلاس، قابلیت پذیرش break-pointها را نیز دارد؛ مانند table-responsive-md.
- کلاسهای thead-light و یا thead-dark که بر روی تگ thead قرار میگیرند، رنگ پس زمینهی هدر جدول را مشخص میکنند.
- برای تغییر رنگ پس زمینه و متن یک ردیف میتوان از کلاسهای bg-color و text-color استفاده کرد:
<tr class="bg-danger text-light">
<td class="table-success">$100.00 </td>
کامپوننت جدید card در بوت استرپ 4
پنلهای بوت استرپ 3 حذف و بجای آن کامپوننت جدیدی به نام card در نگارش 4 آن ارائه شدهاست که با افزودن کلاس آن به یک div، بلافاصله قابی با گوشههای گرد به آن اضافه میشود.
<section class="card mb-5" id="drwinthrop"> <div class="card-body"> <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg" alt="Doctor Winthrop Photo"> <h2 class="card-title">Dr. Stanley Winthrop</h2> <h5 class="card-subtitle">Behaviorist</h5> <p class="card-text">Dr. Winthrop is the guardian of Missy, a three-year old Llaso mix, who he adopted at the shelter. Dr. Winthrop is passionate about spay and neuter and pet adoption, and works tireless hours outside the clinic, performing free spay and neuter surgeries for the shelter.</p> <a class="card-link" href="#">About Me</a> <a class="card-link" href="#">My Pets</a> <a class="card-link" href="#">Client Slideshow</a> </div> </section>
- سپس یک card میتواند دارای تصویری واکنشگرا باشد که عرض card را پوشش میدهد. این تصویر با کلاس card-img مشخص میشود.
در اینجا امکان تعریف card-img-top و card-img-bottom نیز وجود دارند. این موارد تصویر card را در بالا و یا پایین آن، بدون padding، نمایش میدهند. اگر میخواهید متنی را بر روی این تصویر نمایش دهید، از کلاس card-img-overlay استفاده کنید. در این حالتها باید تصویر را خارج از card-body قرار دهید.
- عنوان و زیرعنوان یک card، توسط کلاسهای card-title و card-subtitle تعیین میشوند.
- متن داخل آنرا با کلاس card-text مشخص میکنیم.
- لینکهای ذیل آن نیز توسط کلاس card-link در طی یک ردیف نمایش داده میشوند.
امکان تعیین رنگ پس زمینه، حاشیه و متن یک card نیز وجود دارند:
<section class="card mb-5 bg-primary text-light border-warning" id="drchase">
و فرمول کلی رنگهای آن نیز به صورت زیر میباشد:
میتوان برای یک card، هدر و فوتر نیز تعریف کرد:
<section class="card mb-5" id="drsanders"> <div class="card-header"> <h2 class="card-title">Dr. Kenneth Sanders</h2> <h5 class="card-subtitle">Nutritionist</h5> </div> <div class="card-body"> <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg" alt="Doctor Sanders Photo"> <p class="card-text">Leroy walked into Dr. Sanders front door when she was moving into a new house. After searching for weeks for Leroy's guardians, she decided to make Leroy a part of her pet family, and now has three cats.</p> </div> <div class="card-footer"> <a class="card-link" href="#">About Me</a> <a class="card-link" href="#">My Pets</a> <a class="card-link" href="#">Client Slideshow</a> </div> </section>
برای تعریف یک list-group در داخل یک card، به صورت زیر عمل میکنیم:
<section class="card mb-5" id="drwong"> <div class="card-body"> <img class="card-img img-fluid" src="images/testimonial-mcphersons.jpg" alt="Doctor Wong Photo"> <h2 class="card-title">Dr. Olivia Wong</h2> <h5 class="card-subtitle">Preventive Care</h5> <p class="card-text">Dr. Wong is a cancer survivor who was fortunate enough to get to spend time with a therapy dog during her recovery. She became passionate about therapy animals, and has started her own foundation to train and provide education to patients in recovery. Now she gets her own dose of daily therapy from her husky, Lilla.</p> </div> <div class="list-group list-group-flush"> <a class="list-group-item" href="#">About Me</a> <a class="list-group-item" href="#">My Pets</a> <a class="list-group-item" href="#"> Client Slideshow </a> </div> </section>
تعیین نحوهی چیدمان cards در بوت استرپ 4
اگر چندین card در یک صفحه تعریف شدهاند، برای تعیین نحوهی قرارگیری آنها در کنار یکدیگر میتوان یا از سیستم طرحبندی متداول بوت استرپ استفاده کرده و یا امکان تعریف گروهی از آنها نیز وجود دارد. برای اینکار کافی است یک div با کلاس card-group را تعریف و سپس تمام cards را داخل آن قرار دهیم:
<div class="container"> <div class="card-group">
اگر از کلاس card-columns استفاده کنیم، تمام cards را به صورت خودکار در ستونها و ردیفها، قرار میدهد که بعضی از آنها بلندتر و بعضی دیگر کوتاهتر هستند (نوعی نمایش کاشیکاری شدهاست):
ولی در کل اگر نیاز به کنترل بیشتری دارید، از همان روش متداول تعریف ردیفها و ستونهای سیستم طرحبندی بوت استرپ استفاده کنید.
المان media در بوت استرپ 4
برای نمایش متداول متن و تصویر که قرار است تصویر، در یک ستون و متن، در ستونی دیگر باشد، بوت استرپ 4 به همراه کلاس media است که بر اساس Flexbox بازنویسی شدهاست.
<body> <div class="container"> <section class="media mb-5" id="drwinthrop"> <img class="d-flex align-self-center img-fluid rounded mr-3" style="width:30%" src="images/testimonial-mcphersons.jpg" alt="Doctor Winthrop Photo"> <div class="media-body"> <h2>Dr. Stanley Winthrop</h2> <h5>Behaviorist</h5> <p>Dr. Winthrop is the guardian of Missy, a three-year old Llaso mix, who he adopted at the shelter. Dr. Winthrop is passionate about spay and neuter and pet adoption, and works tireless hours outside the clinic, performing free spay and neuter surgeries for the shelter.</p> </section> </div> </body>
ابتدا توسط کلاس media یک container را تعریف میکنیم. سپس تصویر، یک ستون و media-body ستون دیگر را تشکیل میدهد.
با استفاده از d-flex، المان تصویر را به یک Flexbox container تبدیل کرده و با استفاده از کلاس align-self-center، آنرا در میانهی ستون قرار میدهیم. همچنین در اینجا توسط mr-3، فاصلهی آنرا با متن ستون کناری تنظیم کردهایم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: Bootstrap4_09.zip
اشتراکها
کتابخانه jcarousellite
jCarousel Lite is a jQuery plugin that carries you on a carousel
ride filled with images and HTML content. Put simply, you can navigate
images and/or HTML in a carousel-style widget. Demo
فایلهای پروژهها
PdfRpt-2.5.7z
- Updated the project to use iTextSharp 5.5.x - Fixed 'PDFTemplate FormFlattening removes filled data'. - Fixed disappearing of the DIV's, during XMLWorker processing. - Added default font support for XHtmlFooterProvider & XHtmlHeaderProvider. - Improved UnicodeFontProvider to support default font for HTML documents. - Improved RtlElementsCollector.
Visual Studio 2013 Update 3 CTP1 brings improvements to Code Map and
Debugger Map that you have requested via various feedback channels (user voice and connect in particular)
اشتراکها