检查用户是否登录 Angular 2 中的任何页面更改
Check if the user logged in on any page change in Angular 2
Angular2 进展缓慢但稳步推进。现在我面临以下挑战。我想检查用户是否在每次页面更改时登录(换句话说,在加载每个组件时)。当然,我可以在每一个中实现 OnInit 接口,但那是代码味道。
是否有任何有效的方法来执行应用程序每个页面上所需的任何内容?我很想听听有关如何处理此任务的最佳实践的任何其他建议。
我正在使用这个库 (https://auth0.com/blog/2015/11/10/introducing-angular2-jwt-a-library-for-angular2-authentication/) 进行基于 jwt 的登录,我已经有了一个很好的服务 class,它封装了所有与身份验证相关的功能。因此,实际检查用户登录的位置已经完成并经过测试。
谢谢,
我认为扩展 Router Outlet 是实现此目的的常用方法
CaptainCodeman 不久前在 Gitter 中发布的示例(我自己还没有测试)
import {Directive, Injector, Attribute, ElementRef, DynamicComponentLoader} from 'angular2/core';
import {Router, RouteData, RouterOutlet, RouteParams, Instruction, ComponentInstruction} from 'angular2/router';
/*
Example implementation
Given a route:
@RouteConfig([
{ path: '/thing/:id', component: ThingComponent, name: 'Thing', data: { public: false, roles:['thinger'] } }
])
authorize(instruction: ComponentInstruction):boolean {
// simplest case - route is public
if (<boolean>instruction.routeData.data['public']) {
return true;
}
// if not public then we at least need an authenticated user
if (this.isAuthenticated()) {
var routeRoles = <any[]>instruction.routeData.data['roles'];
var userRoles = <string[]>this.roles();
// no roles required for route = user just needs to be authenticated
var authorized = routeRoles.length === 0 || routeRoles.some(routeRole => userRoles.indexOf(routeRole) >= 0);
return authorized;
}
return false;
}
*/
export abstract class IAuthService {
abstract isAuthenticated():boolean;
authorize(instruction: ComponentInstruction, params:any):boolean {
// authorized if route allows public access or user is authenticated
return this.isAuthenticated() || <boolean>instruction.routeData.data['public']
}
}
@Directive({selector: 'secure-outlet'})
export class SecureRouterOutlet extends RouterOutlet {
signin:string;
unauthorized:string;
injector:Injector;
private parentRouter: Router;
private authService: IAuthService;
constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string,
authService:IAuthService,
injector:Injector,
@Attribute('signin') signinAttr: string,
@Attribute('unauthorized') unauthorizedAttr: string) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
this.authService = authService;
this.injector = injector;
this.signin = signinAttr;
this.unauthorized = unauthorizedAttr;
}
activate(nextInstruction: ComponentInstruction): Promise<any> {
var params = this.getAllRouteParams(this.injector);
var isAuthorized = this.authService.authorize(nextInstruction, params);
if (isAuthorized) {
return super.activate(nextInstruction);
}
if (this.authService.isAuthenticated()) {
var ins = this.parentRouter.generate([this.unauthorized]);
return super.activate(ins.component);
} else {
var ins = this.parentRouter.generate([this.signin,{url:location.pathname}]);
return super.activate(ins.component);
}
}
reuse(nextInstruction: ComponentInstruction): Promise<any> {
return super.reuse(nextInstruction);
}
getAllRouteParams(injector) {
let params = null;
while(injector) {
const routeParams = injector.getOptional(RouteParams);
if (routeParams) {
if (params === null) {
params = {};
} else {
params = Object.create(params);
}
Object.assign(params, routeParams.params);
}
injector = injector.parent;
}
return params;
}
}
如果你使用路由(你说的似乎就是这样:"on every page change"),你可以利用几件事:
创建一个自定义路由器出口(RouterOutlet
的子 class),它会调用其 activate
方法检查身份验证。在这种情况下,您可以拥有一些全球性的东西。类似的东西:
@Directive({
selector: 'auth-outlet'
})
export class AuthOutlet extends RouterOutlet {
(...)
activate(oldInstruction: ComponentInstruction) {
var url = this.parentRouter.lastNavigationAttempt;
if (isAuthenticated()) {
return super.activate(oldInstruction);
} else {
(...)
}
}
}
有关详细信息,请参阅此问题:
利用 CanActivate
装饰器检查组件是否可以激活。在您的情况下,您可以在此级别执行身份验证检查。
也可以在 RouterLink 级别做一些事情来显示/隐藏路由链接。在这种情况下,您可以根据相关路由配置和当前用户提示在这些链接上应用角色。有关详细信息,请参阅此问题:
这也可以在 HTTP 拦截器(扩展 Http
的 class 中处理)。在这种情况下,当请求正在执行时,您可以插入一些身份验证检查:
@Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log('request...');
if (isAuthenticated()) {
return super.request(url, options).catch(res => {
// do something
});
} else {
// Redirect to login page
// Or throw an exception: return Observable.throw(new Error(...));
}
}
(...)
}
有关详细信息,请参阅此问题:
我正在向您展示 Angular2 的简单实现。您可以利用 @CanActivate 钩子,如下图所示,使用 isLoggedIn 函数 检查用户是否登录 returns 承诺.
注意:下面的实现是在访问任何组件之前检查用户是否loggedIn
。希望通过一些修改可以达到你想要的效果。
Auth.ts
import {Observable} from 'rxjs/Observable';
export class Auth {
constructor() {
this.loggedIn = false;
}
login() {
this.loggedIn = true;
}
logout() {
this.loggedIn = false;
}
check() {
return Observable.of(this.loggedIn);
}
}
isLoggedIn.ts
import {Injector} from 'angular2/core';
import {appInjector} from './appInjector';
import {Auth} from './Auth';
import {Router, ComponentInstruction} from 'angular2/router';
export const isLoggedIn = (next: ComponentInstruction, previous: ComponentInstruction) => {
let injector: Injector = appInjector(); // get the stored reference to the injector
let auth: Auth = injector.get(Auth);
let router: Router = injector.get(Router);
// return a boolean or a promise that resolves a boolean
return new Promise((resolve) => {
auth.check()
.subscribe((result) => {
if (result) {
resolve(true);
} else {
router.navigate(['/Login']);
resolve(false);
}
});
});
};
appInjector.ts
import {Injector} from 'angular2/core';
let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
if (injector) {
appInjectorRef = injector;
}
return appInjectorRef;
};
somecomponent.ts
import {Component, View,ViewChild} from 'angular2/core';
import {CanActivate} from 'angular2/router';
import {isLoggedIn} from './isLoggedIn';
@Component({
selector: 'some',
template: 'some text'
})
@CanActivate((next: ComponentInstruction, previous: ComponentInstruction) => {
return isLoggedIn(next, previous); // this will tell whether user is loggedIn or not.
})
export class Protected {
}
boot.ts
.
.
import { provide, ComponentRef } from 'angular2/core';
import { appInjector } from './app-injector';
.
.
bootstrap(AppComponent, [...]).then((appRef: ComponentRef) => {
// store a reference to the application injector
appInjector(appRef.injector);
});
在这篇很棒的文章 Authentication in Angular 2
中显示和实施了两种限制访问的方法 Custom Router Outlet
和 CanActivate Decorator
这就是我所做的,在app.routing.ts
中使用了canActive 属性
{
path: 'dashboard',
loadChildren: './dashboard',
canActivate:[AuthGuard]
},
请按照以下 5 分钟的视频教程进行操作
https://www.youtube.com/watch?v=0Qsg8fyKwO4
注意:此解决方案适用于 Angular 4
Angular2 进展缓慢但稳步推进。现在我面临以下挑战。我想检查用户是否在每次页面更改时登录(换句话说,在加载每个组件时)。当然,我可以在每一个中实现 OnInit 接口,但那是代码味道。
是否有任何有效的方法来执行应用程序每个页面上所需的任何内容?我很想听听有关如何处理此任务的最佳实践的任何其他建议。
我正在使用这个库 (https://auth0.com/blog/2015/11/10/introducing-angular2-jwt-a-library-for-angular2-authentication/) 进行基于 jwt 的登录,我已经有了一个很好的服务 class,它封装了所有与身份验证相关的功能。因此,实际检查用户登录的位置已经完成并经过测试。
谢谢,
我认为扩展 Router Outlet 是实现此目的的常用方法
CaptainCodeman 不久前在 Gitter 中发布的示例(我自己还没有测试)
import {Directive, Injector, Attribute, ElementRef, DynamicComponentLoader} from 'angular2/core';
import {Router, RouteData, RouterOutlet, RouteParams, Instruction, ComponentInstruction} from 'angular2/router';
/*
Example implementation
Given a route:
@RouteConfig([
{ path: '/thing/:id', component: ThingComponent, name: 'Thing', data: { public: false, roles:['thinger'] } }
])
authorize(instruction: ComponentInstruction):boolean {
// simplest case - route is public
if (<boolean>instruction.routeData.data['public']) {
return true;
}
// if not public then we at least need an authenticated user
if (this.isAuthenticated()) {
var routeRoles = <any[]>instruction.routeData.data['roles'];
var userRoles = <string[]>this.roles();
// no roles required for route = user just needs to be authenticated
var authorized = routeRoles.length === 0 || routeRoles.some(routeRole => userRoles.indexOf(routeRole) >= 0);
return authorized;
}
return false;
}
*/
export abstract class IAuthService {
abstract isAuthenticated():boolean;
authorize(instruction: ComponentInstruction, params:any):boolean {
// authorized if route allows public access or user is authenticated
return this.isAuthenticated() || <boolean>instruction.routeData.data['public']
}
}
@Directive({selector: 'secure-outlet'})
export class SecureRouterOutlet extends RouterOutlet {
signin:string;
unauthorized:string;
injector:Injector;
private parentRouter: Router;
private authService: IAuthService;
constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string,
authService:IAuthService,
injector:Injector,
@Attribute('signin') signinAttr: string,
@Attribute('unauthorized') unauthorizedAttr: string) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
this.authService = authService;
this.injector = injector;
this.signin = signinAttr;
this.unauthorized = unauthorizedAttr;
}
activate(nextInstruction: ComponentInstruction): Promise<any> {
var params = this.getAllRouteParams(this.injector);
var isAuthorized = this.authService.authorize(nextInstruction, params);
if (isAuthorized) {
return super.activate(nextInstruction);
}
if (this.authService.isAuthenticated()) {
var ins = this.parentRouter.generate([this.unauthorized]);
return super.activate(ins.component);
} else {
var ins = this.parentRouter.generate([this.signin,{url:location.pathname}]);
return super.activate(ins.component);
}
}
reuse(nextInstruction: ComponentInstruction): Promise<any> {
return super.reuse(nextInstruction);
}
getAllRouteParams(injector) {
let params = null;
while(injector) {
const routeParams = injector.getOptional(RouteParams);
if (routeParams) {
if (params === null) {
params = {};
} else {
params = Object.create(params);
}
Object.assign(params, routeParams.params);
}
injector = injector.parent;
}
return params;
}
}
如果你使用路由(你说的似乎就是这样:"on every page change"),你可以利用几件事:
创建一个自定义路由器出口(
RouterOutlet
的子 class),它会调用其activate
方法检查身份验证。在这种情况下,您可以拥有一些全球性的东西。类似的东西:@Directive({ selector: 'auth-outlet' }) export class AuthOutlet extends RouterOutlet { (...) activate(oldInstruction: ComponentInstruction) { var url = this.parentRouter.lastNavigationAttempt; if (isAuthenticated()) { return super.activate(oldInstruction); } else { (...) } } }
有关详细信息,请参阅此问题:
利用
CanActivate
装饰器检查组件是否可以激活。在您的情况下,您可以在此级别执行身份验证检查。也可以在 RouterLink 级别做一些事情来显示/隐藏路由链接。在这种情况下,您可以根据相关路由配置和当前用户提示在这些链接上应用角色。有关详细信息,请参阅此问题:
这也可以在 HTTP 拦截器(扩展 Http
的 class 中处理)。在这种情况下,当请求正在执行时,您可以插入一些身份验证检查:
@Injectable()
export class CustomHttp extends Http {
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log('request...');
if (isAuthenticated()) {
return super.request(url, options).catch(res => {
// do something
});
} else {
// Redirect to login page
// Or throw an exception: return Observable.throw(new Error(...));
}
}
(...)
}
有关详细信息,请参阅此问题:
我正在向您展示 Angular2 的简单实现。您可以利用 @CanActivate 钩子,如下图所示,使用 isLoggedIn 函数 检查用户是否登录 returns 承诺.
注意:下面的实现是在访问任何组件之前检查用户是否loggedIn
。希望通过一些修改可以达到你想要的效果。
Auth.ts
import {Observable} from 'rxjs/Observable';
export class Auth {
constructor() {
this.loggedIn = false;
}
login() {
this.loggedIn = true;
}
logout() {
this.loggedIn = false;
}
check() {
return Observable.of(this.loggedIn);
}
}
isLoggedIn.ts
import {Injector} from 'angular2/core';
import {appInjector} from './appInjector';
import {Auth} from './Auth';
import {Router, ComponentInstruction} from 'angular2/router';
export const isLoggedIn = (next: ComponentInstruction, previous: ComponentInstruction) => {
let injector: Injector = appInjector(); // get the stored reference to the injector
let auth: Auth = injector.get(Auth);
let router: Router = injector.get(Router);
// return a boolean or a promise that resolves a boolean
return new Promise((resolve) => {
auth.check()
.subscribe((result) => {
if (result) {
resolve(true);
} else {
router.navigate(['/Login']);
resolve(false);
}
});
});
};
appInjector.ts
import {Injector} from 'angular2/core';
let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
if (injector) {
appInjectorRef = injector;
}
return appInjectorRef;
};
somecomponent.ts
import {Component, View,ViewChild} from 'angular2/core';
import {CanActivate} from 'angular2/router';
import {isLoggedIn} from './isLoggedIn';
@Component({
selector: 'some',
template: 'some text'
})
@CanActivate((next: ComponentInstruction, previous: ComponentInstruction) => {
return isLoggedIn(next, previous); // this will tell whether user is loggedIn or not.
})
export class Protected {
}
boot.ts
.
.
import { provide, ComponentRef } from 'angular2/core';
import { appInjector } from './app-injector';
.
.
bootstrap(AppComponent, [...]).then((appRef: ComponentRef) => {
// store a reference to the application injector
appInjector(appRef.injector);
});
在这篇很棒的文章 Authentication in Angular 2
中显示和实施了两种限制访问的方法Custom Router Outlet
和 CanActivate Decorator
这就是我所做的,在app.routing.ts
中使用了canActive 属性 {
path: 'dashboard',
loadChildren: './dashboard',
canActivate:[AuthGuard]
},
请按照以下 5 分钟的视频教程进行操作
https://www.youtube.com/watch?v=0Qsg8fyKwO4
注意:此解决方案适用于 Angular 4