我正在尝试使用Angular2路由器防护来限制对我的应用中的某些页面的访问。我正在使用Firebase身份验证。为了检查用户是否使用Firebase登录,我必须使用回调调用.subscribe()
对象上的FirebaseAuth
。这是警卫的代码:
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFireAuth } from "angularfire2/angularfire2";
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AngularFireAuth, private router: Router) {}
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
this.auth.subscribe((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
});
}
}
当导航到具有防护功能的页面时,authenticated
或not authenticated
将打印到控制台(等待来自firebase的响应延迟一段时间后)。但是,导航永远不会完成。此外,如果我没有登录,我将被重定向到/login
路由。因此,我遇到的问题是return true
没有向用户显示请求的页面。我假设这是因为我正在使用回调,但我无法弄清楚如何做到这一点。有什么想法吗?
答案 0 :(得分:81)
canActivate
需要返回完成的Observable
:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AngularFireAuth, private router: Router) {}
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean>|boolean {
return this.auth.map((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
}).first(); // this might not be necessary - ensure `first` is imported if you use it
}
}
缺少return
我使用map()
代替subscribe()
因为subscribe()
返回Subscription
而不是Observable
答案 1 :(得分:11)
canActivate
可以返回Promise
来解析boolean
答案 2 :(得分:7)
您可以使用Observable
来处理异步逻辑部分。以下是我测试的代码:
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { DetailService } from './detail.service';
@Injectable()
export class DetailGuard implements CanActivate {
constructor(
private detailService: DetailService
) {}
public canActivate(): boolean|Observable<boolean> {
if (this.detailService.tempData) {
return true;
} else {
console.log('loading...');
return new Observable<boolean>((observer) => {
setTimeout(() => {
console.log('done!');
this.detailService.tempData = [1, 2, 3];
observer.next(true);
observer.complete();
}, 1000 * 5);
});
}
}
}
答案 3 :(得分:5)
扩展最受欢迎的答案。 AngularFire2的Auth API有所改变。这是实现AngularFire2 AuthGuard的新签名:
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
@Injectable()
export class AuthGuardService implements CanActivate {
constructor(
private auth: AngularFireAuth,
private router : Router
) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean>|boolean {
return this.auth.authState.map(User => {
return (User) ? true : false;
});
}
}
注意:这是一个相当天真的测试。您可以控制日志记录User实例,以查看是否要针对用户的某些更详细的方面进行测试。但至少应该帮助保护路线免受未登录用户的侵害。
答案 4 :(得分:3)
您可以返回true | false作为承诺。
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {AuthService} from "../services/authorization.service";
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private router: Router, private authService:AuthService) { }
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return new Promise((resolve, reject) => {
this.authService.getAccessRights().then((response) => {
let result = <any>response;
let url = state.url.substr(1,state.url.length);
if(url == 'getDepartment'){
if(result.getDepartment){
resolve(true);
} else {
this.router.navigate(['login']);
resolve(false);
}
}
})
})
}
}
答案 5 :(得分:3)
在AngularFire的最新版本中,以下代码有效(与最佳答案有关)。请注意“管道”方法的用法。
import { Injectable } from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
import {AngularFireAuth} from '@angular/fire/auth';
import {map} from 'rxjs/operators';
import {Observable} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthGuardService implements CanActivate {
constructor(private afAuth: AngularFireAuth, private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return this.afAuth.authState.pipe(
map(user => {
if(user) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
})
);
}
}
答案 6 :(得分:2)
就我而言,我需要处理不同的行为取决于响应状态错误。这就是RxJS 6+对我而言的工作方式:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AngularFireAuth, private router: Router) {}
public canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | boolean {
return this.auth.pipe(
tap({
next: val => {
if (val) {
console.log(val, 'authenticated');
return of(true); // or if you want Observable replace true with of(true)
}
console.log(val, 'acces denied!');
return of(false); // or if you want Observable replace true with of(true)
},
error: error => {
let redirectRoute: string;
if (error.status === 401) {
redirectRoute = '/error/401';
this.router.navigateByUrl(redirectRoute);
} else if (error.status === 403) {
redirectRoute = '/error/403';
this.router.navigateByUrl(redirectRoute);
}
},
complete: () => console.log('completed!')
})
);
}
}
在某些情况下,至少在tap
operator的next
部分,这可能不起作用。删除它并添加旧商品map
,如下所示:
public canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | boolean {
return this.auth.pipe(
map((auth) => {
if (auth) {
console.log('authenticated');
return true;
}
console.log('not authenticated');
this.router.navigateByUrl('/login');
return false;
}),
tap({
error: error => {
let redirectRoute: string;
if (error.status === 401) {
redirectRoute = '/error/401';
this.router.navigateByUrl(redirectRoute);
} else if (error.status === 403) {
redirectRoute = '/error/403';
this.router.navigateByUrl(redirectRoute);
}
},
complete: () => console.log('completed!')
})
);
}
答案 7 :(得分:0)
为了显示另一种实现方式。根据{{3}},并在其他答案中提到,CanActivate的返回类型也可以是解析为布尔值的Promise。
注意:所示示例在Angular 11中实现,但适用于Angular 2+版本。
示例:
import {
Injectable
} from '@angular/core';
import {
ActivatedRouteSnapshot,
CanActivate,
CanActivateChild,
Router,
RouterStateSnapshot,
UrlTree
} from '@angular/router';
import {
Observable
} from 'rxjs/Observable';
import {
AuthService
} from './auth.service';
@Injectable()
export class AuthGuardService implements CanActivate, CanActivateChild {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot, state: RouterStateSnapshot
): Observable < boolean | UrlTree > | Promise < boolean | UrlTree > | boolean | UrlTree {
return this.checkAuthentication();
}
async checkAuthentication(): Promise < boolean > {
// Implement your authentication in authService
const isAuthenticate: boolean = await this.authService.isAuthenticated();
return isAuthenticate;
}
canActivateChild(
childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot
): Observable < boolean | UrlTree > | Promise < boolean | UrlTree > | boolean | UrlTree {
return this.canActivate(childRoute, state);
}
}
答案 8 :(得分:0)
使用异步等待...你等待承诺解决
async getCurrentSemester() {
let boolReturn: boolean = false
let semester = await this.semesterService.getCurrentSemester().toPromise();
try {
if (semester['statusCode'] == 200) {
boolReturn = true
} else {
this.router.navigate(["/error-page"]);
boolReturn = false
}
}
catch (error) {
boolReturn = false
this.router.navigate(["/error-page"]);
}
return boolReturn
}
<块引用>
这是我的授权 (@angular v7.2)
async canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
let security: any = null
if (next.data) {
security = next.data.security
}
let bool1 = false;
let bool2 = false;
let bool3 = true;
if (this.webService.getCookie('token') != null && this.webService.getCookie('token') != '') {
bool1 = true
}
else {
this.webService.setSession("currentUrl", state.url.split('?')[0]);
this.webService.setSession("applicationId", state.root.queryParams['applicationId']);
this.webService.setSession("token", state.root.queryParams['token']);
this.router.navigate(["/initializing"]);
bool1 = false
}
bool2 = this.getRolesSecurity(next)
if (security && security.semester) {
// ---- watch this peace of code
bool3 = await this.getCurrentSemester()
}
console.log('bool3: ', bool3);
return bool1 && bool2 && bool3
}
<块引用>
路线是
{ path: 'userEvent', component: NpmeUserEvent, canActivate: [AuthGuard], data: { security: { semester: true } } },