مطالب
اهراز هویت با شبکه اجتماعی گوگل
در این مقاله نحوه‌ی ورود به یک سایت ASP.NET MVC را با حساب‌های کاربری سایت‌های اجتماعی، بررسی خواهیم کرد. در اینجا با ورود به سایت در وب فرم‌ها آشنا شدید. توضیحات مربوطه به OpenID هم در اینجا قرار دارد.

مقدمه:

شروع را با نصب ویژوال استودیوی نسخه رایگان 2013 برای وب و یا نسخه‌ی 2013 آغاز می‌کنیم. برای راهنمایی استفاده ازDropbox, GitHub, Linkedin, Instagram, buffer  salesforce  STEAM, Stack Exchange, Tripit, twitch, Twitter, Yahoo و بیشتر اینجا کلیک کنید.

توجه:

برای استفاده از Google OAuth 2 و دیباگ به صورت لوکال بدون اخطار SSL، شما می‌بایستی نسخه‌ی ویژوال استودیو 2013 آپدیت 3 و یا بالاتر را نصب کرده باشید.

ساخت اولین پروژه:

ویژوال استودیو را اجرا نماید. در سمت چپ بر روی آیکن Web کلیک کنید تا آیتم ASP.NET Web Application در دات نت 4.5.1 نمایش داده شود. یک نام را برای پروژه انتخاب نموده و OK را انتخاب نماید.
در دیالوگ بعدی آیتم MVC را انتخاب و اطمینان داشته باشید Individual User Accounts که با انتخاب Change Authentication به صورت دیالوگ برای شما نمایش داده می‌شود، انتخاب گردیده و در نهایت بر روی OK کلیک کنید.




فعال نمودن حساب کاربری گوگل و اعمال تنظیمات اولیه:

در این بخش در صورتیکه حساب کاربری گوگل ندارید، وارد سایت گوگل شده و یک حساب کاربری را ایجاد نماید. در غیر اینصورت اینجا کلیک کنید تا وارد بخش Google Developers Console شوید.
در بخش منو بر روی ایجاد پروژه کلیک کنید تا پروژه‌ای جدید ایجاد گردد.



در دیالوگ باز شده نام پروژه خودتان را وارد کنید و دکمه‌ی Create را زده تا عملیات ایجاد پروژه انجام شود. در صورتیکه با موفقیت پیش رفته باشید، این صفحه برای شما بارگزاری میگردد.


فعال سازی Google+API


در سمت چپ تصویر بالا آیتمی با نام APIs & auth خواهید دید که بعد از کلیک بر روی آن، زیر مجموعه‌ای برای این آیتم فعال میگردد که می‌بایستی بر روی APIs کلیک و در این قسمت به جستجوی آیتمی با نام Google+ API پرداخته و در نهایت این آیتم را برای پروژه فعال سازید.



ایجاد یک Client ID :

در بخش Credentials بر روی دکمه‌ی Create new Client ID کلیک نماید.


در دیالوگ باز شده از شما درخواست می‌شود تا نوع اپلیکشن را انتخاب کنید که در اینجا می‌بایستی آیتم اول (Web application ) را برای گام بعدی انتخاب کنید و با کلیک بر روی Configure consent screen به صفحه‌ی Consent screen هدایت خواهید شد. فیلد‌های مربوطه را به درستی پر کنید (این بخش به عنوان توضیحات مجوز ورود بین سایت شما و گوگل است).

 

  در نهایت بعد از کلیک بر روی Save به صفحه‌ی Client ID بازگشت داده خواهید شد که در این صفحه با این دیالوگ برخورد خواهید کرد.



پروژه‌ی  MVC خودتان را اجرا و لینک و پورت مربوطه را کپی کنید ( http://localhost:5063  ).

در Authorized JavaScript Origins لینک را کپی نماید و در بخش Authorized redirect URls لینک را مجدد کپی نماید. با این تفاوت که بعد از پورت signin-google  را هم قرار دهید. ( http://localhost:5063/signin-google  )

حال بر روی دکمه‌ی Create Client ID کلیک کنید.


پیکربندی فایل Startup.Auth :

فایل web.config را که در ریشه‌ی پروژه قرار دارد باز کنید. در داخل تگ appSettings کد زیر را کپی کنید. توجه شود بجای دو مقدار value، مقداری را که گوگل برای شما ثبت کرده است، وارد کنید.

  <appSettings>
    <!--Google-->
    <add key="GoogleClientId" value="555533955993-fgk9d4a9999ehvfpqrukjl7r0a4r5tus.apps.googleusercontent.com" />
    <add key="GoogleClientSecretId" value="QGEF4zY4GEwQNXe8ETwnVHfz" />
  </appSettings>

فایل Startup.Auth را باز کنید و دو پراپرتی و یک سازنده‌ی بدون ورودی را تعریف نماید. توضیحات بیشتر به صورت کامنت در کد زیر قرار گرفته است.

//فضا نام‌های استفاده شده در این کلاس
using System;
using System.Configuration;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Google;
using Owin;
using Login.Models;
//فضا نام جاری پروژه
namespace Login
{
    /// <summary>
    /// در ریشه سایت فایلی با نام استارت آپ که کلاسی هم نام این کلاس با یک تابع و یک ورودی از نوع اینترفیس  تعریف شده است
    ///که این دو کلاس به صورت پارشال مهروموم شده اند 
    /// </summary>
    public partial class Startup
    {
        /// <summary>
        /// این پراپرتی مقدار کلایت ای دی رو از وب دات کانفیگ در سازنده بدون ورودی در خودش ذخیره میکند
        /// </summary>
        public string GoogleClientId { get; set; }
        /// <summary>
        /// این پراپرتی مقدار کلایت  سیکرت ای دی رو از وب دات کانفیگ در سازنده بدون ورودی در خودش ذخیره میکند
        /// </summary>
        public string GoogleClientSecretId { get; set; }

        /// <summary>
        /// سازنده بدون ورودی
        /// به ازای هر بار نمونه سازی از کلاس، سازنده‌های بدون ورودی کلاس هر بار اجرا خواهند شد، توجه شود که می‌توان از 
        /// سازنده‌های استاتیک هم استفاده کرد، این سازنده فقط یک بار، در صورتی که از کلاس نمونه سازی شود ایجاد میگردد 
        /// </summary>
        public Startup()
        {
            //Get Client ID from Web.Config
            GoogleClientId = ConfigurationManager.AppSettings["GoogleClientId"];
            //Get Client Secret ID from Web.Config
            GoogleClientSecretId = ConfigurationManager.AppSettings["GoogleClientSecretId"];
        }

        ///// <summary>
        ///// سازنده استاتیک کلاس
        ///// </summary>
        //static Startup()
        //{
              //در صورتی که از این سازنده استفاده شود می‌بایست پراپرتی‌های تعریف شده در سطح کلاس به صورت استاتیک تعریف گردد تا 
              //بتوان در این سازنده سطح دسترسی گرفت
        //    GoogleClientId = ConfigurationManager.AppSettings["GoogleClientId"];
        //    GoogleClientSecretId = ConfigurationManager.AppSettings["GoogleClientSecretId"];
        //}
        // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
        public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context, user manager and signin manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            // Configure the sign in cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.  
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
            app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

            // Enables the application to remember the second login verification factor such as phone or email.
            // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
            // This is similar to the RememberMe option when you log in.
            app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

            //Initialize UseGoogleAuthentication
            app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
            {
                ClientId = GoogleClientId,
                ClientSecret = GoogleClientSecretId
            });
        }
    }
}

حال پروژه را اجرا کرده و به صفحه‌ی ورود کاربر رجوع نمائید. همانگونه که در تصوبر زیر مشاهده می‌کنید، دکمه‌ای با مقدار نمایشی گوگل در سمت راست، در بخش Use another service to log in اضافه شده است که بعد از کلیک بر روی آن، به صفحه‌ی ‌هویت سنجی گوگل ریداریکت می‌شوید.



در اینجا از کاربر سوال پرسیده میشود که آیا به سایت پذیرنده اجازه داده شود که اطلاعات و ایمیل شما ارسال گردند که بعد از انتخاب دکمه‌ی Accept، لاگین انجام گرفته و اطلاعات ارسال می‌گردد.

توجه: رمز عبور شما به هیچ عنوان برای سایت پذیرنده ارسال نمی‌گردد.


لاگین با موفقیت انجام شد.


در مطلب بعدی سایر سایت‌های اجتماعی قرار خواهند گرفت.

پروژه‌ی مطلب جاری را میتوانید از اینجا دانلود کنید.
بازخوردهای دوره
پیاده سازی دکمه «بیشتر» یا «اسکرول نامحدود» به کمک jQuery در ASP.NET MVC
- شاید خطایی وجود دارد که باید بررسی کنید: نحوه استفاده از افزونه Firebug برای دیباگ برنامه‌های ASP.NET مبتنی بر jQuery 
- در اینجا محدودیتی در مورد تعداد پارامترها ندارید. برای مثال لینک قسمت مطالب سایت جاری به این صورت است:
https://www.dntips.ir/postsarchive#/page/1/date/asc
در اینجا پارامترهای شماره صفحه، فیلد و صعودی و نزولی بودن آن ذکر شدند. پارامترهای دیگری را هم اگر علاقمند بودید (مانند زبان یا هر مورد دیگری) اضافه کنید. فایل jquery.InfiniteScroll.js پیوست شده یک مثال در این مورد است (نحوه تنظیم و دریافت پارامترها و نحوه‌ی واکنش به پارامترهای دریافتی). مستندات رسمی آن‌را هم فراموش نکنید.
اشتراک‌ها
پیاده سازی Domain-Driven Design با EF

The Intersection of Microservices, Domain-Driven Design and Entity Framework Core
Domain-Driven Design (DDD) provides much of the strategic design guidance that we can use to determine the boundaries around and interactions between Microservices in our solutions. DDD also follows up with tactical design patterns for your business logic. In this session we'll take a look at some of these patterns and how EF Core naturally, or with some additional configuration, persists the data that your microservices depend on. 

پیاده سازی Domain-Driven Design با EF
مطالب
استفاده از کتابخانه‌ی moment-jalaali در برنامه‌های Angular
چندی قبل مطلب «نمایش تاریخ شمسی توسط JavaScript در AngularJS» را در این سایت مطالعه کردید. در اینجا قصد داریم معادل Angular آن‌را تهیه کنیم (واژه‌ی AngularJS به نگارش‌های 1x اشاره می‌کند و Angular به تمام نگارش‌های پس از 2).


نصب پیشنیازهای کار با moment-jalaali

ابتدا نیاز است بسته‌ی npm این کتابخانه را نصب کنیم که به همراه فایل‌های js مرتبط با آن می‌باشد:
 npm install moment-jalaali --save

سپس جهت بهبود تجربه‌ی کاربری با آن در IDEهای امروزی، خصوصا VSCode، بهتر است typings آن‌را نیز نصب کنیم؛ تا علاوه بر داشتن Intellisense، بتوان به صورت strongly typed با آن کار کرد:
 npm install @types/moment-jalaali --save-dev


VSCode به صورت خودکار پوشه‌ی مخصوص node_modules\@types را تحت نظر قرار می‌دهد و نصب بسته‌های typings در آن، سبب بارگذاری آنی آن‌ها خواهد شد.
به علاوه اگر به فایل tsconfig.json واقع در ریشه‌ی پروژه نیز دقت کنید، وجود تعریف ذیل، امکان خوانده شدن این تعاریف را توسط کامپایلر TypeScript میسر می‌کند:
{
    "typeRoots": [
      "node_modules/@types"
    ]
}

 
نحوه‌ی کار Strongly Typed با کتابخانه‌ی moment-jalaali در برنامه‌های مبتنی بر TypeScript

پس از نصب پیشنیازهای یاد شده، ابتدا برای دسترسی به امکانات این کتابخانه، ماژول آن‌را import می‌کنیم:
import * as momentJalaali from "moment-jalaali";

export class MomentJalaaliTestComponent implements OnInit {
  now: string;
  nowLongDateFormat: string;
  nowExtraLongDateFormat: string;

  ngOnInit() {
    this.persianDateTests();
  }

  persianDateTests() {
    // https://github.com/jalaali/moment-jalaali
    momentJalaali.loadPersian(/*{ usePersianDigits: true }*/); // نمایش فارسی نام ماه‌ها، روزها و امثال آن

    this.now = momentJalaali().format("jYYYY/jMM/jDD HH:mm");
    this.nowLongDateFormat = momentJalaali().format("jD jMMMM jYYYY [ساعت] LT");
    this.nowExtraLongDateFormat = momentJalaali().format(
      "dddd، jD jMMMM jYYYY [ساعت] LT"
    );
  }
}
- پس از import ماژولی به نام moment-jalaali، اکنون نحوه‌ی استفاده‌ی از آن‌را در متد persianDateTests مشاهده می‌کنید.
- متد momentJalaali.loadPersian باید تنها یکبار فراخوانی شود. کار آن تبدیل نام‌های روزها و ماه‌های میلادی، به شمسی است.
- پس از آن از طریق متد format آن، می‌توان انواع و اقسام حالات مختلف را بررسی کرد که در اینجا سه نمونه را مشاهده می‌کنید.



نوشتن یک Pipe سفارشی برای تبدیل تاریخ‌های میلادی دریافتی از سرور به قالب شمسی

پس آشنا شدن با نحوه‌ی استفاده‌ی از این کتابخانه در یک برنامه‌ی تایپ‌اسکریپتی، تبدیل کردن آن به یک Pipe سفارشی بسیار ساده‌است. برای این منظور ابتدا یک Pipe جدید را به ماژول فرضی custom-pipe.module اضافه می‌کنیم:
 ng g p CustomPipe/moment-jalaali -m custom-pipe.module
با این محتوا:
import { Pipe, PipeTransform } from "@angular/core";

import * as momentJalaali from "moment-jalaali";

@Pipe({
  name: "momentJalaali"
})
export class MomentJalaaliPipe implements PipeTransform {
  transform(value: any, args?: any): any {
    return momentJalaali(value).format(args);
  }
}
در اینجا نیز ابتدا ماژول moment-jalaali تعریف شده‌است و سپس توسط آن، value به عنوان پارامتر متد momentJalaali و args به عنوان پارامتر متد format ارسال شده‌اند. در حین استفاده‌ی از Pipe، مقدار value همان تاریخ دریافتی است و args به فرمت خاصی که توسط استفاده کننده مشخص می‌شود، تنظیم خواهد شد.
به این ترتیب می‌توان یک چنین تبدیلات سمت کاربری را انجام داد که نمونه‌ای از خروجی آن‌را در تصویر فوق نیز ملاحظه می‌کنید:
<h2>Server side dates:</h2>
<div *ngFor="let date of dates">
  <span dir="ltr">{{date | momentJalaali:'jYYYY/jMM/jDD hh:mm' }}</span>,
  <span dir="rtl">{{date | momentJalaali:'jD jMMMM jYYYY [ساعت] LT'}}</span>
</div>


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید.
نظرات مطالب
بررسی روش آپلود فایل‌ها در ASP.NET Core
یک نکته‌ی تکمیلی: روش تعیین حداکثر اندازه‌ی فایل قابل آپلود در برنامه‌های ASP.NET Core
الف) اگر برنامه توسط IIS هاست شده‌است
<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <!-- To customize the asp.net core module uncomment and edit the following section. 
 For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
 <system.webServer>
  <handlers>
  <remove name="aspNetCore"/>
  <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
  </handlers>
  <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
  <security>
   <requestFiltering>
    <!-- This will handle requests up to 50MB -->
    <requestLimits maxAllowedContentLength="52428800" />
   </requestFiltering>
  </security>
  </system.webServer>
</configuration>
در اینجا (در فایل web.config برنامه) مقدار خاصیت maxAllowedContentLength است که تعیین کننده‌ی حداکثر اندازه‌ی فایل قابل آپلود است. به علاوه مطمئن شوید که فایل web.config شما حتما publish شده.
همچنین requestTimeout را نیز به صورت زیر می‌توان مقدار دهی کرد:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <aspNetCore requestTimeout="00:20:00"  .... />
  </system.webServer>
</configuration>
به همراه این تنظیمات:
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<IISServerOptions>(options =>
            {
                options.MaxRequestBodySize = int.MaxValue;
            });
            services.Configure<FormOptions>(options =>
            {
                options.ValueLengthLimit = int.MaxValue;
                options.MultipartBodyLengthLimit = long.MaxValue; // <-- ! long.MaxValue
                options.MultipartBoundaryLengthLimit = int.MaxValue;
                options.MultipartHeadersCountLimit = int.MaxValue;
                options.MultipartHeadersLengthLimit = int.MaxValue;
            });
 
ب) اگر برنامه در لینوکس و بر اساس وب سرور Kestrel هاست شده‌است
روش اول: استفاده از ویژگی RequestSizeLimit که از نگارش ASP.NET Core 2.0 به بعد قابل استفاده‌است و صرفا به قسمتی از برنامه اعمال می‌شود:
[HttpPost]
[RequestSizeLimit(40000000)]
public async Task<IActionResult> UploadFiles(IFormFile file)
روش دوم: تنظیم سراسری آن برای کل برنامه:
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder
                        .UseStartup<Startup>()
                        .ConfigureKestrel(kestrelServerOptions =>
                        {
                            kestrelServerOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(10);
                            kestrelServerOptions.Limits.MaxRequestBodySize = 52428800; //50MB
                        });
                });
بدیهی است تنظیم این دو روش در حالت استفاده‌ی از IIS تاثیری نخواهند داشت.
مطالب
یافتن اکشن متد‌های به اشتباه کش شده در ASP.NET MVC
مرسوم است برای کش کردن خروجی یک اکشن متد در ASP.NET MVC از ویژگی OutputCache استفاده شود. نکته‌ی مهمی که در مورد نحوه پیاده سازی آن وجود دارد، استفاده از OutputCacheModule استاندارد ASP.NET است. در این حالت پس از فراخوانی ابتدایی اکشن متد و کش شدن محتوای حاصل از آن، در دفعه‌ی بعد فراخوانی این آدرس خاص، اصلا چرخه کاری یک کنترلر روی نداده و تمام مسایل توسط OutputCacheModule به صورت مستقل و پیش از رسیدن آن به کنترلر، مدیریت می‌شوند.
خوب، تا اینجا ممکن است مشکلی به نظر نرسد و هدف از کش کردن اطلاعات یک اکشن متد نیز همین مورد است. اما اگر این اکشن متد کش شده، به اشتباه در یک کنترلر مزین شده با ویژگی Authorize قرار گیرد، چه خواهد شد؟ مثلا این کنترلر امن، برای ارائه فایل‌ها یا حتی نمایش قسمتی از صفحه یا کل صفحه، از کش استفاده کرده است. در بار اول دریافت فایل، بدیهی است که تمام مسایل اعتبارسنجی باید مطابق طول عمر یک کنترلر روی دهند. اما در بار دوم فراخوانی آدرس دریافت صفحه یا فایل، اصلا کار به فراخوانی کنترلر نمی‌رسد. به عبارتی کلیه کاربران سایت (اعم از لاگین شده، نشده، دارای دسترسی مشاهده صفحه یا آدرس امن و یا بدون دسترسی)، به این محتوای خاص بدون مشکلی دسترسی خواهند داشت (فقط کافی است که از آدرس نهایی به نحوی مطلع شوند).

سؤال: چگونه می‌توان کلیه اکشن متدهای یک پروژه ASP.NET MVC را که دارای ویژگی OutputCache در یک کنترلر امن هستند، یافت؟

using System;
using System.Linq;
using System.Reflection;
// Add a ref. to \Program Files\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Web.Mvc.dll
using System.Web.Mvc;
// Add a ref. to System.Web
using System.Web.UI;

namespace FindOutputCaches
{
    class Program
    {
        static void Main(string[] args)
        {
            var path = @"D:\site\bin\Web.dll";
            var asmTarget = Assembly.LoadFrom(path);

            checkSecuredControllers(asmTarget);

            Console.WriteLine("Press a key...");
            Console.Read();
        }

        private static void checkSecuredControllers(Assembly asmTarget)
        {
            // یافتن کلیه کنترلرهایی که فیلتر اوتورایز دارند
            var securedControllers = asmTarget.GetTypes()
                                              .Where(type => typeof(IController).IsAssignableFrom(type) &&
                                                             Attribute.IsDefined(type, typeof(AuthorizeAttribute)) &&
                                                             !type.Name.StartsWith("T4MVC"))
                                              .ToList();

            foreach (var controller in securedControllers)
            {
                // یافتن کلیه اکشن متدهای کنترلر جاری
                var actionMethods = controller.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                              .Where(method => typeof(ActionResult).IsAssignableFrom(method.ReturnType))
                                              .ToList();

                foreach (var method in actionMethods)
                {
                    // یافتن متدهایی که دارای آوت پوت کش هستند
                    var attributes = method.GetCustomAttributes(typeof(OutputCacheAttribute), true);
                    if (attributes == null || !attributes.Any())
                        continue;

                    var outputCache = (OutputCacheAttribute)attributes[0]; // AllowMultiple = false
                    if (outputCache.Location == OutputCacheLocation.None)
                        continue; //سبب عدم کش شدن شده است؛ مثلا برای کارهای ای‌جکسی

                    Console.WriteLine("Detected incorrect usage of OutputCache in:\n {0}-->{1}",
                                           controller.FullName, method.Name);
                }
            }
        }
    }
}
کدهای کامل این بررسی را در اینجا ملاحظه می‌کنید.
ابتدا مسیر اسمبلی کامپایل شده پروژه ASP.NET MVC که حاوی کنترلرهای برنامه است، باید مشخص گردد.
سپس در این اسمبلی کلیه نوع‌های تعریف شده، یافت گردیده و آن‌هایی که پیاده سازی کننده IController هستند (یعنی کلاس‌های کنترلر واقعی برنامه) و همچنین دارای ویژگی AuthorizeAttribute نیز می‌باشند، جدا خواهند شد.
در ادامه، در هر کنترلر امن یافت شده، متدهایی را بررسی خواهیم کرد که دارای خروجی از نوع ActionResult باشند (فقط اکشن متدها مدنظر هستند). اگر این اکشن متد یافت شده دارای ویژگی OutputCacheAttribute بود و همچنین Location آن به None تنظیم نشده بود ... یعنی مشکل امنیتی وجود دارد که باید برطرف شود.

البته برای تکمیل این مطلب باید دو حالت زیر هم پیاده سازی و بررسی شوند:
- کلیه Viewهای برنامه بررسی شوند. اگر در View خاصی که متعلق است به یک کنترلر یا حتی اکشن متد امن، ارجاعی به اکشن متدی کش شده در کنترلری دیگر وجود داشت، این مورد هم یک باگ امنیتی است.
- کلیه کنترلرهای عمومی که دارای اکشن متدی امن هستند نیز باید جهت یافتن OutputCache بررسی شوند.
مطالب
آشنایی با Contained Databases در SQL Server 2012
مقدمه:
بعضی وقت‌ها به هر دلیلی لازم است پایگاه داده برنامه هایمان را به سرور دیگری انتقال دهیم . برخلاف Oracle که انتقال پایگاه داده به سرور دیگر کار مشکل و تخصصی می‌باشد در SQL Server براحتی با یک Detach و Attach مجدد این انتقال انجام خواهد شد. اما اطلاعات Logins در پایگاه داده Master  سرور ذخیره شده است و با فایل پایگاه داده انتقال نمی‌یابد و به هیمن خاطر برخی مواقع پس از Attach پایگاه داده با پیغام The database is not accessible یا Cannot open user default database مواجع خواهیم شد که مشکل بخاطر همین نبود کاربران سرور قبلی و Owner متفاوت پایگاه داده می‌باشد که با استفاده از رویه ذخیره شده sp_change_users_login این مشکل قابل حل می‌باشد اما در SQL Server 2012 راحل بهتری فراهم شده است.

Contained Databases در Sql Server 2012
یکی از امکانات اضافه شده در SQL Server 2012  امکان Contained Databases  هست که برای حل همین مشکل و رفع کامل وابستی پایگاه داده با Instance نصب شده و خاصیت قابل حمل کامل database‌ها می‌باشد. در ادامه نحوه استفاده از این امکان را بررسی خواهیم کرد:
  1. فعال کردن خاصیت Contained Databases 

    قبل از استفاده از Contained Databases می بایست این امکان را فعال کرد. برای این کار می‌توانید از SQL Server Management Studio یا T-SQL commands استفاده نمایید. بر روی نام Instance راست کلیت کنید و گزینه Properties را انتخاب نمایید. از گزینه Advanced که در شکل زیر مشاهده می‌نمایید خاصیت Enable Contained Databases را بر روی True قرار دهید.

    یا می‌توایند از sp_configure این کار را انجام دهید.دستورات زیر این موضوع را نشان می‌دهد.
    sp_configure 'show advanced options',1
    GO
    RECONFIGURE WITH OVERRIDE
    GO
    sp_configure 'contained database authentication',1
    GO
    RECONFIGURE WITH OVERRIDE
    GO

  2. ایجاد یا تغییر یک پایگاه داده از نوع Contained Databases

    برای ایجاد یک پایگاه داده با این خاصیت یا تغییر پایگاه داده موجود کافیست مقدار گزینه Containment type را بر روی Partial قرار دهید. برای پایگاه داده موجود از پنجره Properties پایگاه داده صفحه Options را انتخاب کنید.


  3. ایجاد یک کاربر برای پایگاه داده Contained Databases

    برای تعریف یک کاربر در سطح پایگاه داده پوشه Security پایگاه داده خود را باز کنید بر روی پوشه Users راست کیلک و گزینه New User را انتخاب نمایید از گزینه User type که در شکل زیر نشان داده شده است SQL user with password را انتخاب نمایید و نام کاربر و رمز عبور و تکرار آن را وارد نمایید. کاربر ایجاد شده در سطح پایگاه داده می‌باشد و با انتقال به سرور دیگر نیر قابل دسترسی می‌باشد.

  4.  اتصال به پایگاه داده Contained Databases

    برای اتصال به پایگاه داده کافیست در حالت SQL Server Authentication نام کاربری و رمز عبور جدید را وارد و گزینه Options  را انتخاب و از برگه Additional Connection Parameters نام پایگاه داده مورد نظر را مانند شکل زیر وارد نمایید پس از ورود تنها پایگاه داده خود را مشاهده می‌نمایید. یکی از کاربرهای این قابلیت برای مدیران سرور پایگاه داده می‌باشد که بدون استفاده از مجوز sysadmin  به کاربران اجازه دسترسی را می‌دهد.