مطلب جالبی است که کمتر مورد استفاده قرار میگیره. شاید ذکر مثالهای دنیای واقعی اون در اینجا مکمل خوبی باشه. اگر دوستان هم مثالی مدنظرشون بود، لطفا عنوان کنند.
An easier way to manage INotifyPropertyChanged
Custom Explicit and Implicit Operators in C#
Why does this code work?
ASP.NET MVC Model binding with implicit operators
Forgotten C# language features: implicit operator
How to fake Enums in EF 4
An easier way to manage INotifyPropertyChanged
Custom Explicit and Implicit Operators in C#
Why does this code work?
ASP.NET MVC Model binding with implicit operators
Forgotten C# language features: implicit operator
How to fake Enums in EF 4
دقیقا همینطور هست. در اینجا فناوریهای سمت سرور فقط تبدیل به ارائه کنندهی وب سرویس میشوند و نه بیشتر.
مزیت آن این است که شما با Razor فقط یک سری فرمهای ابتدایی و اعتبارسنجی آنها را به خوبی میتوانید مدیریت کنید. یک مقدار کار UI که پیچیدهتر شد، به صفحهای میرسید که درون آن 100ها سطر کد جیکوئری با کدهای Razor مخلوط شدن و 2 ماه بعد حتی جرات نمیکنید به این صفحه دست بزنید و تغییری را در آن اعمال کنید. اینجا است که فریم ورکهای SPA ارزش خودشان را نشان میدهند.
برای فرمهای معمولی، Razor عالی است. برای UI پیچیده، ترکیب Razor و jQuery اصلا قابلیت نگهداری ندارد (صفحاتی پر از قطعات جاوا اسکریپت که نه فضای نامی دارند، به راحتی تداخل میکنند، عموما از TypeScript استفاده نمیکنند و مبتنی بر قابلیتهای جدید این زبان نیستند). در یک چنین حالتی قابلیت تست UI را هم از دست خواهید داد. همچنین مدام هم محدود به افزونههای جیکوئری خواهید بود، اما با فریم ورکهای SPA، خیلی از این افزونهها، کارهای معمولی آنها هستند. به علاوه زمانیکه back-end (سمت سرور) و front-end (مدیریت کدهای سمت کلاینت) را از هم جدا میکنید، بهتر میتوانید از مزیت کار با طراحان UI استفاده کنید. کسانیکه عموما قرار نیست با کدهای سمت سرور کار کنند.
مزیت آن این است که شما با Razor فقط یک سری فرمهای ابتدایی و اعتبارسنجی آنها را به خوبی میتوانید مدیریت کنید. یک مقدار کار UI که پیچیدهتر شد، به صفحهای میرسید که درون آن 100ها سطر کد جیکوئری با کدهای Razor مخلوط شدن و 2 ماه بعد حتی جرات نمیکنید به این صفحه دست بزنید و تغییری را در آن اعمال کنید. اینجا است که فریم ورکهای SPA ارزش خودشان را نشان میدهند.
برای فرمهای معمولی، Razor عالی است. برای UI پیچیده، ترکیب Razor و jQuery اصلا قابلیت نگهداری ندارد (صفحاتی پر از قطعات جاوا اسکریپت که نه فضای نامی دارند، به راحتی تداخل میکنند، عموما از TypeScript استفاده نمیکنند و مبتنی بر قابلیتهای جدید این زبان نیستند). در یک چنین حالتی قابلیت تست UI را هم از دست خواهید داد. همچنین مدام هم محدود به افزونههای جیکوئری خواهید بود، اما با فریم ورکهای SPA، خیلی از این افزونهها، کارهای معمولی آنها هستند. به علاوه زمانیکه back-end (سمت سرور) و front-end (مدیریت کدهای سمت کلاینت) را از هم جدا میکنید، بهتر میتوانید از مزیت کار با طراحان UI استفاده کنید. کسانیکه عموما قرار نیست با کدهای سمت سرور کار کنند.
در قسمت سوم، کار ورود به سیستم و امکان مشاهدهی صفحهی محافظت شدهی پس از لاگین را پیاده سازی کردیم. در این قسمت میخواهیم امکان دسترسی به مسیر http://localhost:4200/protectedPage را کنترل کنیم. تا اینجا اگر کاربر بدون لاگین کردن نیز این مسیر را درخواست کند، میتواند حداقل ساختار ابتدایی آنرا مشاهده کند که باید مدیریت شود و این مدیریت دسترسی میتواند بر اساس وضعیت لاگین کاربر و همچنین نقشهای او در سیستم باشد:
طراحی بخشهایی از این قسمت، از پروژهی «کنترل دسترسیها در Angular با استفاده از Ng2Permission» ایده گرفته شدهاند.
استخراج اطلاعات کاربر وارد شدهی به سیستم از توکن دسترسی او
یکی از روشهای دسترسی به اطلاعات کاربر در سمت کلاینت، مانند نقشهای او، تدارک متدی در سمت سرور و بازگشت Claims او به سمت کلاینت است:
اما با توجه به اینکه در زمان لاگین، نقشهای او (و سایر Claims دلخواه) نیز به توکن دسترسی نهایی اضافه میشوند، میتوان این کوئری گرفتن مدام از سرور را تبدیل به روش بسیار سریعتر استخراج آنها از توکنی که هم اکنون در کش مرورگر ذخیره شدهاست، کرد.
همچنین باید دقت داشت چون این توکن دارای امضای دیجیتال است، کوچکترین تغییری در آن در سمت کاربر، سبب برگشت خوردن خودکار درخواست ارسالی به سمت سرور میشود (یکی از مراحل اعتبارسنجی کاربر در سمت سرور، اعتبارسنجی توکن دریافتی (قسمت cfg.TokenValidationParameters) و همچنین بررسی خودکار امضای دیجیتال آن است). بنابراین نگرانی از این بابت وجود ندارد.
اگر اطلاعات کاربر در سمت سرور تغییر کنند، با اولین درخواست ارسالی به سمت سرور، رخداد OnTokenValidated وارد عمل شده و درخواست ارسالی را برگشت میزند (در مورد پیاده سازی سمت سرور این مورد، در مطلب «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» بیشتر بحث شدهاست). در این حالت کاربر مجبور به لاگین مجدد خواهد شد که این مورد سبب به روز رسانی خودکار اطلاعات توکنهای ذخیره شدهی او در مرورگر نیز میشود.
اگر از قسمت دوم این سری بهخاطر داشته باشید، توکن decode شدهی برنامه، چنین شکلی را دارد:
به همین جهت متدی را برای تبدیل این اطلاعات به شیء کاربر، ایجاد خواهیم کرد و در سراسر برنامه از این اطلاعات آماده، برای تعیین دسترسی او به قسمتهای مختلف برنامهی سمت کلاینت، استفاده میکنیم.
برای این منظور اینترفیس src\app\core\models\auth-user.ts را به صورت ذیل ایجاد میکنیم:
پس از آن متد getAuthUser را جهت استخراج خواص ویژهی توکن دسترسی decode شدهی فوق، به صورت ذیل به سرویس Auth اضافه میکنیم:
کار با این متد بسیار سریع است و نیازی به رفت و برگشتی به سمت سرور ندارد؛ چون تمام اطلاعات استخراجی توسط آن هم اکنون در کش سریع مرورگر کلاینت موجود هستند. استفادهی از متد Object.freeze هم سبب read-only شدن این خروجی میشود.
همچنین در اینجا تمام نقشهای دریافتی، تبدیل به LowerCase شدهاند. با اینکار مقایسهی بعدی آنها با نقشهای درخواستی، حساس به بزرگی و کوچکی حروف نخواهد بود.
تعریف نقشهای دسترسی به مسیرهای مختلف سمت کلاینت
مرسوم است اطلاعات اضافی مرتبط با هر مسیر را به خاصیت data آن route انتساب میدهند. به همین جهت به فایل dashboard-routing.module.ts مراجعه کرده و نقشهای مجاز به دسترسی به مسیر protectedPage را به خاصیت data آن به صورت ذیل اضافه میکنیم:
که در اینجا ساختار اینترفیس AuthGuardPermission، در فایل جدید app\core\models\auth-guard-permission.ts به صورت ذیل تعریف شدهاست:
به این ترتیب هر قسمتی از برنامه که نیاز به اطلاعات سطوح دسترسی مسیری را داشت، ابتدا به دنبال route.data["permission"] خواهد گشت (کلیدی به نام permission در خاصیت data یک مسیر) و سپس اطلاعات آنرا بر اساس ساختار اینترفیس AuthGuardPermission، تحلیل میکند.
در اینجا تنها باید یکی از خواص permittedRoles (نقشهای مجاز به دسترسی/صدور دسترسی فقط برای این نقشهای مشخص، منهای مابقی) و یا deniedRoles (نقشهای غیرمجاز به دسترسی/دسترسی همهی نقشهای ممکن، منهای این نقشهای تعیین شده)، مقدار دهی شوند.
افزودن کامپوننت «دسترسی ندارید» به ماژول Authentication
در ادامه میخواهیم اگر کاربری به مسیری دسترسی نداشت، به صورت خودکار به صفحهی «دسترسی ندارید» هدایت شود. به همین جهت این کامپوننت را به صورت ذیل به ماژول authentication اضافه میکنیم:
با این خروجی که سبب درج خودکار آن در قسمت declaration فایل authentication.module نیز میشود:
سپس به فایل authentication-routing.module.ts مراجعه کرده و مسیریابی آنرا نیز اضافه میکنیم:
قالب access-denied.component.html را نیز به صورت ذیل تکمیل میکنیم:
که دکمهی Back آن به کمک سرویس Location، صورت ذیل پیاده سازی شدهاست:
در اینجا اگر کاربر به سیستم وارد نشده باشد، دکمهی لاگین نیز به او نمایش داده میشود. همچنین وجود "queryParamsHandling="merge در لینک مراجعهی به صفحهی لاگین، سبب خواهد شد تا query string موجود در صفحه نیز حفظ شود و به صفحهی لاگین انتقال پیدا کند. در صفحهی لاگین نیز جهت پردازش این نوع کوئری استرینگها، تمهیدات لازم درنظر گرفته شدهاند.
دکمهی back آن نیز توسط سرویس توکار Location واقع در مسیر angular/common@ پیاده سازی شدهاست.
ایجاد یک محافظ مسیر سمت کلاینت برای بررسی وضعیت کاربر جاری و همچنین نقشهای او
پس از تعریف متد getAuthUser و استخراج اطلاعات کاربر از توکن دسترسی دریافتی که شامل نقشهای او نیز میشود، اکنون میتوان متد بررسی این نقشها را نیز به سرویس Auth اضافه کرد:
متد some در جاوا اسکریپت شبیه به متد Any در C# LINQ عمل میکند. همچنین در مقایسهی صورت گرفته، با توجه به اینکه user.roles را پیشتر به LowerCase تبدیل کردیم، حساسیتی بین نقش Admin و admin و کلا کوچکی و بزرگی نام نقشها وجود ندارد.
اکنون در هر قسمتی از برنامه که نیاز به بررسی امکان دسترسی یک کاربر به نقش یا نقشهایی خاص وجود داشته باشد، میتوان AuthService را به سازندهی آن تزریق و سپس از متد فوق جهت بررسی نهایی، استفاده کرد.
در ادامه یک Route Guard جدید را در مسیر app\core\services\auth.guard.ts ایجاد میکنیم. کار آن بررسی خودکار امکان دسترسی به یک مسیر درخواستی است:
در اینجا در ابتدا وضعیت لاگین کاربر بررسی میگردد. این وضعیت نیز از طریق سرویس Auth که به سازندهی کلاس تزریق شدهاست، تامین میشود. اگر کاربر هنوز لاگین نکرده باشد، به صفحهی عدم دسترسی هدایت خواهد شد.
سپس خاصیت permission اطلاعات مسیر استخراج میشود. اگر چنین مقداری وجود نداشت، همینجا کار با موفقیت خاتمه پیدا میکند.
در آخر وضعیت دسترسی به نقشهای استخراجی deniedRoles و permittedRoles که از اطلاعات مسیر دریافت شدند، توسط متد isAuthUserInRoles سرویس Auth بررسی میشوند.
در متد showAccessDenied کار ارسال آدرس درخواستی (state.url) به صورت یک کوئری استرینگ (queryParams) با کلید returnUrl به صفحهی accessDenied صورت میگیرد. در این صفحه نیز دکمهی لاگین به همراه "queryParamsHandling="merge است. یعنی کامپوننت لاگین برنامه، کوئری استرینگ returnUrl را دریافت میکند:
و پس از لاگین موفق، در صورت وجود این کوئری استرینگ، هدایت خودکار کاربر، به مسیر returnUrl پیشین صورت خواهد گرفت:
محل معرفی این AuthGuard جدید که در حقیقت یک سرویس است، در ماژول Core، در قسمت providers آن، به صورت ذیل میباشد:
در آخر برای اعمال این Guard جدید، به فایل dashboard-routing.module.ts مراجعه کرده و خاصیت canActivate را مقدار دهی میکنیم:
به این ترتیب با درخواست این مسیر، پیش از فعالسازی و نمایش آن، توسط AuthGuard معرفی شدهی به آن، کار بررسی وضعیت کاربر و نقشهای او که از خاصیت permission خاصیت data دریافت میشوند، صورت گرفته و اگر عملیات تعیین اعتبار اطلاعات با موفقیت به پایان رسید، آنگاه کاربر مجوز دسترسی به این قسمت از برنامه را خواهد یافت.
اگر قصد آزمایش آنرا داشتید، فقط کافی است بجای نقش Admin، مثلا Admin1 را در permittedRoles مقدار دهی کنید، تا صفحهی access denied را در صورت درخواست مسیر protectedPage، بتوان مشاهده کرد.
کدهای کامل این سری را از اینجا میتوانید دریافت کنید.
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژهی ASPNETCore2JwtAuthentication.AngularClient وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o، برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد (و یا همان اجرای فایل ng-serve.bat). همچنین باید به پوشهی ASPNETCore2JwtAuthentication.WebApp نیز مراجعه کرده و فایل dotnet_run.bat را اجرا کنید، تا توکن سرور برنامه نیز فعال شود.
طراحی بخشهایی از این قسمت، از پروژهی «کنترل دسترسیها در Angular با استفاده از Ng2Permission» ایده گرفته شدهاند.
استخراج اطلاعات کاربر وارد شدهی به سیستم از توکن دسترسی او
یکی از روشهای دسترسی به اطلاعات کاربر در سمت کلاینت، مانند نقشهای او، تدارک متدی در سمت سرور و بازگشت Claims او به سمت کلاینت است:
public IActionResult Get() { var user = this.User.Identity as ClaimsIdentity; var config = new { userName = user.Name, roles = user.Claims.Where(x => x.Type == ClaimTypes.Role).Select(x => x.Value).ToList() }; return Ok(config); }
همچنین باید دقت داشت چون این توکن دارای امضای دیجیتال است، کوچکترین تغییری در آن در سمت کاربر، سبب برگشت خوردن خودکار درخواست ارسالی به سمت سرور میشود (یکی از مراحل اعتبارسنجی کاربر در سمت سرور، اعتبارسنجی توکن دریافتی (قسمت cfg.TokenValidationParameters) و همچنین بررسی خودکار امضای دیجیتال آن است). بنابراین نگرانی از این بابت وجود ندارد.
اگر اطلاعات کاربر در سمت سرور تغییر کنند، با اولین درخواست ارسالی به سمت سرور، رخداد OnTokenValidated وارد عمل شده و درخواست ارسالی را برگشت میزند (در مورد پیاده سازی سمت سرور این مورد، در مطلب «اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity» بیشتر بحث شدهاست). در این حالت کاربر مجبور به لاگین مجدد خواهد شد که این مورد سبب به روز رسانی خودکار اطلاعات توکنهای ذخیره شدهی او در مرورگر نیز میشود.
اگر از قسمت دوم این سری بهخاطر داشته باشید، توکن decode شدهی برنامه، چنین شکلی را دارد:
{ "jti": "d1272eb5-1061-45bd-9209-3ccbc6ddcf0a", "iss": "http://localhost/", "iat": 1513070340, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "1", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "Vahid", "DisplayName": "وحید", "http://schemas.microsoft.com/ws/2008/06/identity/claims/serialnumber": "709b64868a1d4d108ee58369f5c3c1f3", "http://schemas.microsoft.com/ws/2008/06/identity/claims/userdata": "1", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [ "Admin", "User" ], "nbf": 1513070340, "exp": 1513070460, "aud": "Any" }
برای این منظور اینترفیس src\app\core\models\auth-user.ts را به صورت ذیل ایجاد میکنیم:
export interface AuthUser { userId: string; userName: string; displayName: string; roles: string[]; }
getAuthUser(): AuthUser { if (!this.isLoggedIn()) { return null; } const decodedToken = this.getDecodedAccessToken(); let roles = decodedToken["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]; if (roles) { roles = roles.map(role => role.toLowerCase()); } return Object.freeze({ userId: decodedToken["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"], userName: decodedToken["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"], displayName: decodedToken["DisplayName"], roles: roles }); }
همچنین در اینجا تمام نقشهای دریافتی، تبدیل به LowerCase شدهاند. با اینکار مقایسهی بعدی آنها با نقشهای درخواستی، حساس به بزرگی و کوچکی حروف نخواهد بود.
تعریف نقشهای دسترسی به مسیرهای مختلف سمت کلاینت
مرسوم است اطلاعات اضافی مرتبط با هر مسیر را به خاصیت data آن route انتساب میدهند. به همین جهت به فایل dashboard-routing.module.ts مراجعه کرده و نقشهای مجاز به دسترسی به مسیر protectedPage را به خاصیت data آن به صورت ذیل اضافه میکنیم:
import { ProtectedPageComponent } from "./protected-page/protected-page.component"; import { AuthGuardPermission } from "../core/models/auth-guard-permission"; const routes: Routes = [ { path: "protectedPage", component: ProtectedPageComponent, data: { permission: { permittedRoles: ["Admin"], deniedRoles: null } as AuthGuardPermission } } ];
export interface AuthGuardPermission { permittedRoles?: string[]; deniedRoles?: string[]; }
در اینجا تنها باید یکی از خواص permittedRoles (نقشهای مجاز به دسترسی/صدور دسترسی فقط برای این نقشهای مشخص، منهای مابقی) و یا deniedRoles (نقشهای غیرمجاز به دسترسی/دسترسی همهی نقشهای ممکن، منهای این نقشهای تعیین شده)، مقدار دهی شوند.
افزودن کامپوننت «دسترسی ندارید» به ماژول Authentication
در ادامه میخواهیم اگر کاربری به مسیری دسترسی نداشت، به صورت خودکار به صفحهی «دسترسی ندارید» هدایت شود. به همین جهت این کامپوننت را به صورت ذیل به ماژول authentication اضافه میکنیم:
>ng g c Authentication/AccessDenied
AccessDenied create src/app/Authentication/access-denied/access-denied.component.html (32 bytes) create src/app/Authentication/access-denied/access-denied.component.ts (296 bytes) create src/app/Authentication/access-denied/access-denied.component.css (0 bytes) update src/app/Authentication/authentication.module.ts (550 bytes)
import { LoginComponent } from "./login/login.component"; import { AccessDeniedComponent } from "./access-denied/access-denied.component"; const routes: Routes = [ { path: "login", component: LoginComponent }, { path: "accessDenied", component: AccessDeniedComponent } ];
<h1 class="text-danger"> <span class="glyphicon glyphicon-ban-circle"></span> Access Denied </h1> <p>Sorry! You don't have access to this page.</p> <button class="btn btn-default" (click)="goBack()"> <span class="glyphicon glyphicon-arrow-left"></span> Back </button> <button *ngIf="!isAuthenticated" class="btn btn-success" [routerLink]="['/login']" queryParamsHandling="merge"> Login </button>
import { Component, OnInit } from "@angular/core"; import { Location } from "@angular/common"; import { AuthService } from "../../core/services/auth.service"; @Component({ selector: "app-access-denied", templateUrl: "./access-denied.component.html", styleUrls: ["./access-denied.component.css"] }) export class AccessDeniedComponent implements OnInit { isAuthenticated = false; constructor( private location: Location, private authService: AuthService ) { } ngOnInit() { this.isAuthenticated = this.authService.isLoggedIn(); } goBack() { this.location.back(); // <-- go back to previous location on cancel } }
در اینجا اگر کاربر به سیستم وارد نشده باشد، دکمهی لاگین نیز به او نمایش داده میشود. همچنین وجود "queryParamsHandling="merge در لینک مراجعهی به صفحهی لاگین، سبب خواهد شد تا query string موجود در صفحه نیز حفظ شود و به صفحهی لاگین انتقال پیدا کند. در صفحهی لاگین نیز جهت پردازش این نوع کوئری استرینگها، تمهیدات لازم درنظر گرفته شدهاند.
دکمهی back آن نیز توسط سرویس توکار Location واقع در مسیر angular/common@ پیاده سازی شدهاست.
ایجاد یک محافظ مسیر سمت کلاینت برای بررسی وضعیت کاربر جاری و همچنین نقشهای او
پس از تعریف متد getAuthUser و استخراج اطلاعات کاربر از توکن دسترسی دریافتی که شامل نقشهای او نیز میشود، اکنون میتوان متد بررسی این نقشها را نیز به سرویس Auth اضافه کرد:
isAuthUserInRoles(requiredRoles: string[]): boolean { const user = this.getAuthUser(); if (!user || !user.roles) { return false; } return requiredRoles.some(requiredRole => user.roles.indexOf(requiredRole.toLowerCase()) >= 0); } isAuthUserInRole(requiredRole: string): boolean { return this.isAuthUserInRoles([requiredRole]); }
اکنون در هر قسمتی از برنامه که نیاز به بررسی امکان دسترسی یک کاربر به نقش یا نقشهایی خاص وجود داشته باشد، میتوان AuthService را به سازندهی آن تزریق و سپس از متد فوق جهت بررسی نهایی، استفاده کرد.
در ادامه یک Route Guard جدید را در مسیر app\core\services\auth.guard.ts ایجاد میکنیم. کار آن بررسی خودکار امکان دسترسی به یک مسیر درخواستی است:
import { Injectable } from "@angular/core"; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router"; import { AuthService } from "./auth.service"; import { AuthGuardPermission } from "../models/auth-guard-permission"; @Injectable() export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { if (!this.authService.isLoggedIn()) { this.showAccessDenied(state); return false; } const permissionData = route.data["permission"] as AuthGuardPermission; if (!permissionData) { return true; } if (Array.isArray(permissionData.deniedRoles) && Array.isArray(permissionData.permittedRoles)) { throw new Error("Don't set both 'deniedRoles' and 'permittedRoles' in route data."); } if (Array.isArray(permissionData.permittedRoles)) { const isInRole = this.authService.isAuthUserInRoles(permissionData.permittedRoles); if (isInRole) { return true; } this.showAccessDenied(state); return false; } if (Array.isArray(permissionData.deniedRoles)) { const isInRole = this.authService.isAuthUserInRoles(permissionData.deniedRoles); if (!isInRole) { return true; } this.showAccessDenied(state); return false; } } private showAccessDenied(state: RouterStateSnapshot) { this.router.navigate(["/accessDenied"], { queryParams: { returnUrl: state.url } }); } }
سپس خاصیت permission اطلاعات مسیر استخراج میشود. اگر چنین مقداری وجود نداشت، همینجا کار با موفقیت خاتمه پیدا میکند.
در آخر وضعیت دسترسی به نقشهای استخراجی deniedRoles و permittedRoles که از اطلاعات مسیر دریافت شدند، توسط متد isAuthUserInRoles سرویس Auth بررسی میشوند.
در متد showAccessDenied کار ارسال آدرس درخواستی (state.url) به صورت یک کوئری استرینگ (queryParams) با کلید returnUrl به صفحهی accessDenied صورت میگیرد. در این صفحه نیز دکمهی لاگین به همراه "queryParamsHandling="merge است. یعنی کامپوننت لاگین برنامه، کوئری استرینگ returnUrl را دریافت میکند:
this.returnUrl = this.route.snapshot.queryParams["returnUrl"];
if (this.returnUrl) { this.router.navigate([this.returnUrl]); } else { this.router.navigate(["/protectedPage"]); }
محل معرفی این AuthGuard جدید که در حقیقت یک سرویس است، در ماژول Core، در قسمت providers آن، به صورت ذیل میباشد:
import { AuthGuard } from "./services/auth.guard"; @NgModule({ providers: [ AuthGuard ] }) export class CoreModule {}
import { ProtectedPageComponent } from "./protected-page/protected-page.component"; import { AuthGuardPermission } from "../core/models/auth-guard-permission"; import { AuthGuard } from "../core/services/auth.guard"; const routes: Routes = [ { path: "protectedPage", component: ProtectedPageComponent, data: { permission: { permittedRoles: ["Admin"], deniedRoles: null } as AuthGuardPermission }, canActivate: [AuthGuard] } ];
اگر قصد آزمایش آنرا داشتید، فقط کافی است بجای نقش Admin، مثلا Admin1 را در permittedRoles مقدار دهی کنید، تا صفحهی access denied را در صورت درخواست مسیر protectedPage، بتوان مشاهده کرد.
کدهای کامل این سری را از اینجا میتوانید دریافت کنید.
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژهی ASPNETCore2JwtAuthentication.AngularClient وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o، برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد (و یا همان اجرای فایل ng-serve.bat). همچنین باید به پوشهی ASPNETCore2JwtAuthentication.WebApp نیز مراجعه کرده و فایل dotnet_run.bat را اجرا کنید، تا توکن سرور برنامه نیز فعال شود.
از ویژگیهای جدید و البته جالب شیرپوینت 2013 امکان استفاده از Appها میباشد. برای شناخت بیشتر appها پیشنهاد میکنم به MSDN مراجعه کنید. در این پست قصد دارم مراحل استفاده از SharePoint Marketplace مایکروسافت را برای دریافت و نصب app در سرور شیرپوینت و طریقه پیکر بندی سروربیان کنم.
اگر برای بار اول بخواهید یک app را روی سرور شیرپوینت نصب کنید ممکن است این پیغام به شما نمایش داده شود :
Sorry, apps are turned off. If you know who tuns the server, tell them to enable apps.
دقت کنید که کم رنگ بودن آیکون App به معنی عدم پشتیبانی در سرور شیرپوینت شما است و در صورت تلاش برای نصب آن این پیغام را خواهید دید :
دلیل این پیغام ( apps are turned off) تنظیم نبودن سرور شیرپوینت (Front-End) برای پشتیبانی و میزبانی از Appها میباشد . برای استفاده از appها در شیرپوینت نیازمند یک sub-domain و دیگر تنظیمات هستید تا بتوانید از appها استفاده کنید . برای این منظور مراحل زیر را پی بگیرید :
وارد سایت Office Store مایکروسافت شده و app مورد نظر خود را بیابید . در اینجا من از appهای رایگان1 مورد را انتخاب میکنم و با آن شروع میکنم : نمایش وضعیت آب و هوا در شیرپوینت .
روی Add کلیک کنید تا جزییات app و شناسه آن نمایش داده شود . سپس آن شناسه را کپی کنید : ( شناسه app مذکور WA103062091 است )
در اینجا یک بار مراحل را برای نصب app تا رسیدن به خطا پیگیری میکنیم. سپس راه حل آن بیان میشود. توجه داشته باشید که باید پس از رفع مشکل ، مراحل این قسمت از نصب app مجدد انجام شوند .
وارد سایت شده و روی آیکون چرخدنده (تنظیمات) کلیک کنید :
در پنجره باز شده شناسه app را paste کنید و جستجو را آغاز کنید :
باید در نتیجه جستجو نمایش داده شود که app در SharePoint Store یافت شد
روی لینک کلیک کنید تا نتیجه جستجو در Store نمایش داده شود :
توجه داشته باشید که در صفحه باز شده حتما یک واحد پولی و یک زبان را انتخاب نمایید .
ودر این مرحله خطای مذکور که گفته شد نمایش داده میشود :
حال به بیان راه حل میپردازیم :
برای استفاده از Appها در شیرپوینت باید سرویسهای مرتبط و زیر دامنه (CNAME) سرور مرتبط برای آن تنظیم شده باشد .
برای این منظور ابتدا تنظیمات DNS را انجام میدهیم :
برای دامنه جاری یک CNAME تعریف کنید :
Alias Name پنجره فوق به این معنا است که تمام appها در مسیری با فرمت زیر مدیریت میشوند :
AppID.app.vm-seifollahi.iri
اگر به جای *.app فقط * قرار دهید ، هر شناسه app به عنوان زیر دامنه آدرس دهی میشود که در کل تفاوتی ندارد و برای مشخص شدن بهتر این کار را انجام دادم .
برای چک کردن صحت تنظیمات خود روی مسیری مانند Apps-12345678ABCDEF.app.vm-seifollahi.iri دستور ping را اجرا نمایید.
پس از تایید این تنظیمات باید وارد CA شوید و سرویسها را تنظیم کنید : باید دو سرویس App Management Service و Subscription Setting Service در وضعیت Started باشند .
پس از چک کردن سرویسها باید تنظیمات مربوط به App Poolهای IIS و دیتابیس برای App Managemetn Service و Subscription Service تنظیم شود . برای این منظور از Power Shell کمک میگیریم و دستورات زیر را در آن اجرا میکنیم (توضیحات در کامنتها وجود دارند ) :
$account = Get-SPManagedAccount "vmseifollahi\administrator"
# Gets the name of the managed account and sets it to the variable $account for later use.
$appPoolSubSvc = New-SPServiceApplicationPool -Name SettingsServiceAppPool -Account $account
# Creates an application pool for the Subscription Settings service application.
# Uses a managed account as the security account for the application pool.
# Stores the application pool as a variable for later use.
$appPoolAppSvc = New-SPServiceApplicationPool -Name AppServiceAppPool -Account $account
# Creates an application pool for the Application Management service application.
# Uses a managed account as the security account for the application pool.
# Stores the application pool as a variable for later use.
$appSubSvc = New-SPSubscriptionSettingsServiceApplication –ApplicationPool $appPoolSubSvc –Name SettingsServiceApp –DatabaseName MBS_SettingsServiceDB
# Creates the Subscription Settings service application, using the variable to associate it with the application pool that was created earlier.
# Stores the new service application as a variable for later use.
$proxySubSvc = New-SPSubscriptionSettingsServiceApplicationProxy –ServiceApplication $appSubSvc
# Creates a proxy for the Subscription Settings service application.
$appAppSvc = New-SPAppManagementServiceApplication -ApplicationPool $appPoolAppSvc -Name AppServiceApp -DatabaseName MBS_AppServiceDB
# Creates the Application Management service application, using the variable to associate it with the application pool that was created earlier.
# Stores the new service application as a variable for later use.
$proxyAppSvc = New-SPAppManagementServiceApplicationProxy -ServiceApplication $appAppSvc
# Creates a proxy for the Application Management service application.
پس از نصب مشاهده میکنید که دیتابیسها با موفقیت نصب شدند :
حال به CA رفته ( DOMAIN/_admin/ServiceApplications.aspx ) و از Start بودن سرویسهای تنظیم شده اطمینان پیدا کنید : (از همین صفحه نیز میتوانید تنظیماتی که قبلا در power shell انجام شد را انجام دهید)
حال در CA به صفحه Apps میرویم :
و روی Configure App URL کلیک کنید :
در صورتی که پیغام زیر را مشاهده کردید ، IIS را باز کنید :
در قسمت Application Pools به دنبال SharePoint Web Service Root بگردید و آن را Start نمایید :
حال صفحه تنظیمات باز میشود . مقادیر domain و prefix را تنظیم کنید :
سپس روی OK کلیک کنید در این مرحله تنظیمات سرور شیرپوینت تمام شد و باید به ترتیب زیر آنها را restart کنید :
ابتدا SharePoint Timer service را Stop کنید.
سپس سرویس IIS را Restart کنید
حال SharePoint Timer service را Start کنید .
اکنون مراحل را مجدد از سر بگیرید یعنی روی منوی تنظیمات سایت و روی add App کلیک کنید و app را جستجو کنید و مراحل نصب را اجرا کنید تا به مرحله Add کردن app برسید . حال مشاهده میکنید که دکمه فعال بوده و میتوانید آن را نصب کنید :
پس از کلیک روی add به store preview منتقل خواهید شد : (این تصویر مربوط به محصولی دیگر است)
ممکن است پس از زدن دکمه continue خطایی مانند تصویر زیر را مشاهده کنید :
در این صورت احتمالا با کاربر System Account وارد سیستم شده اید که باید از آن خارح شده و با نام کاربری دیکری که دسترسی لازم را دارد وارد شوید .
با کلیک روی continue به marketplace مایکروسافت منتقل خواهید شد که نیازمند یک حساب کاربری در مایکروسافت میباشد :
حال پنجره زیر نمایش داده میشود و به شما اجازهی دانلود app داده میشود :
روی return to site کلیک کنید تا پنجره بعدی برای گرفتن اعتماد شما برای نصب نمایش داده شود :
روی trust it کلیک کنید تا به صفحه site Content منتقل شوید :
همانطور که مشاهده میکنید app در حال دانلود شدن است :
حال در سمت چپ سایت روی نام App کلیک کنید (ترجیحا از مرور گر IE و ورژن 9 یا 10 استفاده کنید )
حال وارد تنظمیات app میشوید (در صورت درخواست نام کاربری و کلمه عبور آن را وارد کنید)
و نتیجه این هفت خوان رستم :
مطالب
لینکهای هفته سوم دی
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
ASP. Net
- ویدیویی دربارهی ASP.Net 4 (بهبودهای حاصل شده در web forms از جهت کار با اسکریپتها خصوصا با تاکید بر jQuery و همچنین ذکر اینکه با آمدن ASP.Net MVC ، وب فرمها کهنه نشده و همچنان توسعه و بهبود داده خواهند شد)، یا مقالهای در این مورد
طراحی و توسعه وب
PHP
اسکیوال سرور
سی شارپ
عمومی دات نت
مسایل اجتماعی و انسانی برنامه نویسی
متفرقه
نظرات مطالب