امنیت
توسعه وب
ویندوز 7
Silverlight و WPF
PHP
دات نت فریم ورک
سی شارپ
دلفی
شیرپوینت
متفرقه
import { Injectable } from '@angular/core'; import { CanActivate } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { } }
>ng g guard user/auth -m user/user.module
installing guard create src\app\user\auth.guard.spec.ts create src\app\user\auth.guard.ts update src\app\user\user.module.ts
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate, Router } from '@angular/router'; import { AuthService } from './auth.service'; @Injectable() export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.checkLoggedIn(state.url); } checkLoggedIn(url: string): boolean { if (this.authService.isLoggedIn()) { return true; } this.authService.redirectUrl = url; this.router.navigate(['/login']); return false; } }
export class AuthService { currentUser: IUser; redirectUrl: string;
export class AuthGuard implements CanActivate { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
isLoggedIn(): boolean { return !this.currentUser; }
import { AuthGuard } from './../user/auth.guard'; const routes: Routes = [ { path: 'products', canActivate: [ AuthGuard ], children: [ ] } ];
if (this.authService.login(userName, password)) { if (this.authService.redirectUrl) { this.router.navigateByUrl(this.authService.redirectUrl); } else { this.router.navigate(['/products']); } }
>ng g guard product/ProductEdit -m product/product.module
installing guard create src\app\product\product-edit.guard.spec.ts create src\app\product\product-edit.guard.ts update src\app\product\product.module.ts
export class ProductEditGuard implements CanDeactivate<ProductEditComponent> { canDeactivate(component: ProductEditComponent): boolean {
get product(): IProduct { return this.currentProduct; } set product(value: IProduct) { this.currentProduct = value; // Clone the object to retain a copy this.originalProduct = Object.assign({}, value); } get isDirty(): boolean { return JSON.stringify(this.originalProduct) !== JSON.stringify(this.currentProduct); }
import { Injectable } from '@angular/core'; import { CanDeactivate } from '@angular/router'; import { ProductEditComponent } from './product-edit/product-edit.component'; @Injectable() export class ProductEditGuard implements CanDeactivate<ProductEditComponent> { canDeactivate(component: ProductEditComponent): boolean { if (component.isDirty) { let productName = component.product.productName || 'New Product'; return confirm(`Navigate away and lose all changes to ${productName}?`); } return true; } }
import { ProductEditGuard } from './product-edit.guard'; const routes: Routes = [ { path: 'products', canActivate: [ AuthGuard ], children: [ { path: '', component: ProductListComponent }, { path: ':id', component: ProductDetailComponent, resolve: { product: ProductResolverService } }, { path: ':id/edit', component: ProductEditComponent, resolve: { product: ProductResolverService }, canDeactivate: [ ProductEditGuard ], children: [ { path: '', redirectTo: 'info', pathMatch: 'full' }, { path: 'info', component: ProductEditInfoComponent }, { path: 'tags', component: ProductEditTagsComponent } ] } ] } ];
reset(): void { this.dataIsValid = null; this.currentProduct = null; this.originalProduct = null; }
onSaveComplete(message?: string): void { if (message) { this.messageService.addMessage(message); } this.reset(); // Navigate back to the product list this.router.navigate(['/products']); }
یک نکتهی تکمیلی: کار با اینترفیس IMiddleware جهت تعریف میانافزارهای سفارشی
اگر امروز قصد تعریف میانافزارهای سفارشی را دارید، بهتر است از روش باز public async Task Invoke(HttpContext context) که در این مطلب معرفی شد، دیگر استفاده نکنید؛ چون مهمترین محدودیتهای آن، داشتن طول عمر Singleton غیرقابل تغییر و همچنین عدم امکان پیاده سازی اینترفیس IDisposable در آن جهت پاکسازی خودکار منابع است. امروز روش توصیه شده، استفاده از اینترفیس IMiddleware است. در این حالت متد Task Invoke فوق، به متد مشخص و ثابت Task InvokeAsync(HttpContext context, RequestDelegate next) تغییر میکند. چون در این حالت دیگر نمیتوان پارامترهای این متد مشخص را مانند قبل که اینترفیسی را پیاده سازی نمیکرد، به صورت پویا کم و زیاد کرد، میتوان سرویسهای مدنظر را به سازندهی کلاس، تزریق کرد. به همین جهت نیاز است، آنرا به نحو زیر به سیستم تزریق وابستگیها معرفی کرد:
builder.Services.AddTransient<MyNewMiddleware>();
این الزام به تعریف آن به صورت یک سرویس رسمی، مزیتهای زیر را به همراه دارد:
الف) میتوان طول عمری، غیر از Singleton را هم در صورت نیاز، تعریف کرد (و مشکل کار با سرویسهایی با طول عمرهای غیر از Singleton کمتر میشود).
ب) چون طول عمر این میانافزار اکنون توسط سیستم تزریق وابستگیها مدیریت میشود، اگر این میانافزار اینترفیس IDisposable را پیاده سازی کند، کار پاکسازی منابع آن خودکار خواهد شد.
نکته 1: روش معرفی آن به سیستم تزریق وابستگیها، به صورت Concrete type است؛ یعنی اصل کلاس باید معرفی شود (مانند سطر فوق) و نه اینکه به صورت متداول زیر به همراه ذکر اینترفیس IMiddleware باشد:
builder.Services.AddTransient<IMiddleware, MyNewMiddleware>();
مابقی کار با آن، با میانافزارهای متداول، تفاوتی ندارد. یعنی قسمت UseMiddleware آن یکی است:
app.UseMiddleware<MyNewMiddleware>();
بنابراین این روش نسبت به روش متداول قبلی، دو تفاوت پیاده سازی اینترفیس مشخص IMiddleware و ثبت کلاس آن به صورت یک سرویس رسمی را دارد؛ مابقی نکات آن، مانند قبل است.
نکته 2: اگر از Scrutor برای ثبت خودکار سرویسهای برنامه استفاده میکنید، روش ثبت خودکار اینگونه سرویسها به صورت زیر و با استفاده از متد ()AsSelf است:
services.Scan(scan => scan.FromAssembliesOf(typeof(IDataSeedersRunner)) .AddClasses(classes => classes.Where(type => { var allInterfaces = type.GetInterfaces(); return allInterfaces.Contains(typeof(IMiddleware)) && allInterfaces.Contains(typeof(ISingletonService)); })) .AsSelf() .WithSingletonLifetime());
در این مثال، تمام IMiddleware هایی که با نشانگر ISingletonService هم مزین شدهاند، یافت شده و به صورت Concrete type هایی، با طول عمر Singleton، به سیستم تزریق وابستگیها اضافه میشوند.
<customErrors mode="On" defaultRedirect="error"> <error statusCode="404" redirect="error/notfound" /> <error statusCode="403" redirect="error/forbidden" /> </customErrors>
public void Configure(IApplicationBuilder app) { if (env.IsDevelopment()) { app.UseDatabaseErrorPage(); app.UseDeveloperExceptionPage(); } app.UseExceptionHandler("/error/index/500"); app.UseStatusCodePagesWithReExecute("/error/index/{0}");
public class ErrorController : Controller { private readonly ILogger<ErrorController> _logger; public ErrorController(ILogger<ErrorController> logger) { _logger = logger; } public IActionResult Index(int? id) { var logBuilder = new StringBuilder(); var statusCodeReExecuteFeature = HttpContext.Features.Get<IStatusCodeReExecuteFeature>(); logBuilder.AppendLine($"Error {id} for {Request.Method} {statusCodeReExecuteFeature?.OriginalPath ?? Request.Path.Value}{Request.QueryString.Value}\n"); var exceptionHandlerFeature = this.HttpContext.Features.Get<IExceptionHandlerFeature>(); if (exceptionHandlerFeature?.Error != null) { var exception = exceptionHandlerFeature.Error; logBuilder.AppendLine($"<h1>Exception: {exception.Message}</h1>{exception.StackTrace}"); } foreach (var header in Request.Headers) { var headerValues = string.Join(",", value: header.Value); logBuilder.AppendLine($"{header.Key}: {headerValues}"); } _logger.LogError(logBuilder.ToString()); if (id == null) { return View("Error"); } switch (id.Value) { case 401: case 403: return View("AccessDenied"); case 404: return View("NotFound"); default: return View("Error"); } } }
npm uninstall -g @angular/cli npm cache verify # if npm version is < 5 then use `npm cache clean` npm install -g @angular/cli@latest
npm i -g npm
npm install -g rxjs-tslint rxjs-5-to-6-migrate -p src/tsconfig.app.json
npm update -g
ng update @angular/cli ng update @angular/core ng update rxjs
ng update @angular/material
ng update --all --force
npm install npm-check-updates -g ncu -u npm install
// api.model.ts export interface Customer { id: number; name: string; } export interface User { id: number; isActive: boolean; }
// using the interfaces import { Customer, User } from './api.model'; export class MyComponent { cust: Customer; }
// api.model.ts namespace ApiModel { export interface Customer { id: number; name: string; } export interface User { id: number; isActive: boolean; } }
// using the interfaces export class MyComponent { cust: ApiModel.Customer; }
// api.v2.model.ts namespace ApiModel { export interface Order { id: number; total: number; } }
export class MyComponent { cust: ApiModel.Customer; order: ApiModel.Order; }
// api.model.d.ts interface Customer { id: number; name: string; }
// using the interfaces of d file export class MyComponent { cust: Customer; }
// lib.es5.d.ts type Partial<T> = { [P in keyof T]?: T[P]; };
import { Customer } from './api.model'; export class MyComponent { cust: Partial<Customer>; / ngOninit() { this.cust = { name: 'jane' }; } }
if (false) { console.log('x'); }
if (false) { // @ts-ignore console.log('x'); }
جاوااسکریپت یک زبان پیچیده است که شما برای کار با آن، نیاز است قسمتهایی را که باید از آنها دوری کنید و قسمتهای مهمی را که باید استفاده کنید، بشناسید. همانطور که Sun Tzu گفته "دشمن خود را بشناس"، ما نیز در این قسمت میخواهیم برای شناخت بیشتر قسمتهای تاریک و روشن جاوااسکریپت به آن بپردازیم.
همانطور که در قسمتهای قبل گفته شد، CoffeeScript تنها به یک syntax محدود نمیشود و توانایی برطرف کردن برخی از مشکلات جاوااسکریپت را نیز دارد. با این حال، با توجه به این واقعیت که کدهای CoffeeScript به صورت مستقیم به جاوااسکریپت تبدیل میشوند و نمیتوانند تمامی مشکلاتی را که در جاوااسکریپت وجود دارند، حل کنند، پس برخی از مسائل وجود دارند که شما باید از آنها آگاهی داشته باشید.
اول از قسمتهایی که توسط CoffeeScript حل شدهاند شروع میکنیم.
with یک دستور بسیار زمانبر است و مضر شناخته شده است و نباید از آن استفاده کنید. with با ایجاد یک ساختار خلاصه نویسی، برای جستجو بر روی خصوصیات اشیاء در نظر گرفته شده بود. برای نمونه به جای نوشتن:
dataObj.users.vahid.email = "info@vmt.ir";
with(dataObj.users.vahid) { email = "info@vmt.ir"; }
به طور پیش فرض تمامی برنامههای جاوااسکریپت در دامنه global اجرا میشوند و تمامی متغیرهایی که ساخته میشوند به طور پیش فرض در ناحیهی global قرار میگیرند. اگر شما بخواهید متغیری را در ناحیهی local ایجاد کنید، باید از کلمه کلیدی var استفاده کنید.
usersCount = 1; // Global var groupsCount = 2; // Global (function(){ pagesCount = 3; // Global var postsCount = 4; // Local })()
outerScope = true do -> innerScope = true
var outerScope; outerScope = true; (function() { var innerScope; return innerScope = true; })();
package = require('./package') class Test build: -> # Overwrites outer variable! package = @testPackage.compile() testPackage: -> package.create()
class window.Asset constructor: ->
function() {} (window.options || {}).property
function() {}(window.options || {}).property
import { InjectionToken } from "@angular/core"; export let APP_CONFIG = new InjectionToken<string>("app.config"); export interface IAppConfig { apiEndpoint: string; loginPath: string; logoutPath: string; refreshTokenPath: string; accessTokenObjectKey: string; refreshTokenObjectKey: string; } export const AppConfig: IAppConfig = { apiEndpoint: "http://localhost:5000/api", loginPath: "account/login", logoutPath: "account/logout", refreshTokenPath: "account/RefreshToken", accessTokenObjectKey: "access_token", refreshTokenObjectKey: "refresh_token" };
import { AppConfig, APP_CONFIG } from "./app.config"; @NgModule({ providers: [ { provide: APP_CONFIG, useValue: AppConfig } ] }) export class CoreModule {}
ng g s Core/services/Auth
create src/app/Core/services/auth.service.ts (110 bytes)
import { AuthService } from "./services/auth.service"; @NgModule({ providers: [ // global singleton services of the whole app will be listed here. BrowserStorageService, AuthService, { provide: APP_CONFIG, useValue: AppConfig } ] }) export class CoreModule {}
import { BehaviorSubject } from "rxjs/BehaviorSubject"; @Injectable() export class AuthService { private authStatusSource = new BehaviorSubject<boolean>(false); authStatus$ = this.authStatusSource.asObservable(); constructor() { this.updateStatusOnPageRefresh(); } private updateStatusOnPageRefresh(): void { this.authStatusSource.next(this.isLoggedIn()); }
export enum AuthTokenType { AccessToken, RefreshToken }
import { BrowserStorageService } from "./browser-storage.service"; export enum AuthTokenType { AccessToken, RefreshToken } @Injectable() export class AuthService { private rememberMeToken = "rememberMe_token"; constructor(private browserStorageService: BrowserStorageService) { } rememberMe(): boolean { return this.browserStorageService.getLocal(this.rememberMeToken) === true; } getRawAuthToken(tokenType: AuthTokenType): string { if (this.rememberMe()) { return this.browserStorageService.getLocal(AuthTokenType[tokenType]); } else { return this.browserStorageService.getSession(AuthTokenType[tokenType]); } } deleteAuthTokens() { if (this.rememberMe()) { this.browserStorageService.removeLocal(AuthTokenType[AuthTokenType.AccessToken]); this.browserStorageService.removeLocal(AuthTokenType[AuthTokenType.RefreshToken]); } else { this.browserStorageService.removeSession(AuthTokenType[AuthTokenType.AccessToken]); this.browserStorageService.removeSession(AuthTokenType[AuthTokenType.RefreshToken]); } this.browserStorageService.removeLocal(this.rememberMeToken); } private setLoginSession(response: any): void { this.setToken(AuthTokenType.AccessToken, response[this.appConfig.accessTokenObjectKey]); this.setToken(AuthTokenType.RefreshToken, response[this.appConfig.refreshTokenObjectKey]); } private setToken(tokenType: AuthTokenType, tokenValue: string): void { if (this.rememberMe()) { this.browserStorageService.setLocal(AuthTokenType[tokenType], tokenValue); } else { this.browserStorageService.setSession(AuthTokenType[tokenType], tokenValue); } } }
{"access_token":"...","refresh_token":"..."}
export interface Credentials { username: string; password: string; rememberMe: boolean; }
@Injectable() export class AuthService { constructor( @Inject(APP_CONFIG) private appConfig: IAppConfig, private http: HttpClient, private browserStorageService: BrowserStorageService ) { this.updateStatusOnPageRefresh(); } login(credentials: Credentials): Observable<boolean> { const headers = new HttpHeaders({ "Content-Type": "application/json" }); return this.http .post(`${this.appConfig.apiEndpoint}/${this.appConfig.loginPath}`, credentials, { headers: headers }) .map((response: any) => { this.browserStorageService.setLocal(this.rememberMeToken, credentials.rememberMe); if (!response) { this.authStatusSource.next(false); return false; } this.setLoginSession(response); this.authStatusSource.next(true); return true; }) .catch((error: HttpErrorResponse) => Observable.throw(error)); } }
@Injectable() export class AuthService { constructor( @Inject(APP_CONFIG) private appConfig: IAppConfig, private http: HttpClient, private router: Router ) { this.updateStatusOnPageRefresh(); } logout(navigateToHome: boolean): void { this.http .get(`${this.appConfig.apiEndpoint}/${this.appConfig.logoutPath}`) .finally(() => { this.deleteAuthTokens(); this.unscheduleRefreshToken(); this.authStatusSource.next(false); if (navigateToHome) { this.router.navigate(["/"]); } }) .map(response => response || {}) .catch((error: HttpErrorResponse) => Observable.throw(error)) .subscribe(result => { console.log("logout", result); }); } }
> npm install jwt-decode --save
import * as jwt_decode from "jwt-decode";
getDecodedAccessToken(): any { return jwt_decode(this.getRawAuthToken(AuthTokenType.AccessToken)); }
{ "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" }
getDisplayName(): string { return this.getDecodedAccessToken().DisplayName; }
getAccessTokenExpirationDateUtc(): Date { const decoded = this.getDecodedAccessToken(); if (decoded.exp === undefined) { return null; } const date = new Date(0); // The 0 sets the date to the epoch date.setUTCSeconds(decoded.exp); return date; }
isAccessTokenTokenExpired(): boolean { const expirationDateUtc = this.getAccessTokenExpirationDateUtc(); if (!expirationDateUtc) { return true; } return !(expirationDateUtc.valueOf() > new Date().valueOf()); }
isLoggedIn(): boolean { const accessToken = this.getRawAuthToken(AuthTokenType.AccessToken); const refreshToken = this.getRawAuthToken(AuthTokenType.RefreshToken); const hasTokens = !this.isEmptyString(accessToken) && !this.isEmptyString(refreshToken); return hasTokens && !this.isAccessTokenTokenExpired(); } private isEmptyString(value: string): boolean { return !value || 0 === value.length; }
Tools -> Code Snippets Manager (Ctrl+K,Ctrl+B)
<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <header> <title>title</title> <author>author</author> <shortcut>shortcut</shortcut> <description>description</description> <snippettypes> <snippettype>SurroundsWith</snippettype> <snippettype>Expansion</snippettype> </snippettypes> </header> <snippet> <declarations> <literal> <id>name</id> <default>value</default> </literal> </declarations> <code language="XML"> <!--[CDATA[<test--> <name>$name$</name> $selected$ $end$]]> </code> </snippet> </codesnippet>
<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <header> ... <snippettypes> <snippettype>SurroundsWith</snippettype> <snippettype>Expansion</snippettype> </snippettypes> </header> ... </codesnippet>
<snippettypes> <snippettype>Expansion</snippettype> </snippettypes>
<Header> <Title>Try Catch Finally</Title> <Author>mohsen.d</Author> <Shortcut>trycf</Shortcut> <Description>Adds a try-catch-finally block</Description>
..... <declarations> <literal> <id>ExceptionName</id> <default>Exception</default> </literal> </declarations> ...
<code language="CSharp"> <!--[CDATA[ try { $end$ } catch($ExceptionName$) { } finally { } ]]--> </code>
<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <header> <title>Try Catch Finally</title> <author>mohsen.d</author> <shortcut>trycf</shortcut> <description>Adds a try-catch-finally block</description> <snippettypes> <snippettype>Expansion</snippettype> </snippettypes> </header> <snippet> <declarations> <literal> <id>ExceptionName</id> <default>Exception</default> </literal> </declarations> <code language="CSharp"> <!--[CDATA[ try { $end$ } catch($ExceptionName$) { } finally { } ]]--> </code> </snippet> </codesnippet>
C:\Users\<UserName>\Documents\Visual Studio 2010\Code Snippets\
Tools -> Code Snippets Manager (Ctrl+K,Ctrl+B)