我有一个有效的身份验证。流。只有一个奇怪的错误。当我以第一个用户身份登录时,我首先必须刷新页面才能在视图中获取数据。当我用该用户注销并下一个用户登录时,我的视图中仍然有user1的数据。如果我再次使用user2注销并使用user3登录,我的视图中将包含user2的数据。因此,似乎总是落后一步。我试图通过销毁订阅来解决此问题,但这并不能解决问题。同样,当我认为该错误开始时,我必须刷新页面以获取我的第一个用户数据,销毁订阅也不能成为错误解决方案。
这是我的代码:
auth.service:我在其中发布用户凭据,交还令牌,并存储它和user_id以便在视图中获取经过身份验证的用户数据。
import { Storage } from '@ionic/storage';
import { JwtHelperService } from '@auth0/angular-jwt';
export const TOKEN_KEY = 'access_token';
export const USERNAME_KEY = 'username_key';
export const USER_ID = 'user_id';
...
user = null;
refreshToken = null;
authenticationState = new BehaviorSubject(false);
constructor(private storage: Storage, private helper: JwtHelperService) {
this.checkToken();
}
checkToken() {
this.storage.get(TOKEN_KEY).then(access => {
if (access) {
this.user = this.helper.decodeToken(access);
this.authenticationState.next(true);
}
});
}
apilogin(username: string, password: string) {
return this.http.post<any>(`${this.url}`, { username, password })
.pipe(
tap(res => {
this.storage.set(TOKEN_KEY, res['access']);
this.storage.set(USERNAME_KEY, username);
this.storage.set(USER_ID, this.user['user_id']);
this.user = this.helper.decodeToken(res['access']);
console.log('my user: ', this.user);
this.authenticationState.next(true);
}));
}
apilogout() {
this.storage.remove(USER_ID);
this.storage.remove(USERNAME_KEY);
this.storage.remove(TOKEN_KEY).then(() => {
this.authenticationState.next(false);
});
}
page.ts:这里获得在视图中显示的数据。 (用户服务只检索一个用户。最后我销毁了订阅)
import { Storage } from '@ionic/storage';
import { USER_ID } from 'src/app/services/auth.service';
import { SubscriptionLike } from 'rxjs';
information = null;
id: number;
key: string;
subscription: SubscriptionLike;
constructor(private storage: Storage, private activatedRoute: ActivatedRoute,
private userService: UserService, private authService: AuthService) { }
ngOnInit() {
// How to get just the authenticated api?
if (this.authService.authenticationState) {
console.log(this.storage);
this.storage.get(USER_ID).then(val => {
this.id = val;
this.subscription = this.userService.getUserDetails(this.id).subscribe(result => {
this.information = result;
console.log(this.information);
});
});
}
}
ngOnDestroy() {
this.subscription.unsubscribe();
this.information = null;
}
login.ts(处理我到主页的路由)
// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
...
apiSubmit() {
console.log('Hello World');
this.submitted = true;
// if form is invalid => stop
if (this.loginForm.invalid) {
return;
}
this.isLoading = true;
this.loadingEl.present();
this.authService.apilogin(
this.f.username,
this.f.password)
.pipe(tap(x => this.loadingEl.dismiss()),
)
.subscribe(
data => {
console.log('0');
this.router.navigate([this.returnUrl]);
},
error => {
console.log('1');
this.loadingEl.dismiss();
this.error = error;
this.isLoading = false;
}
);
}
authGuard
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authService: AuthService
) { }
canActivate(): boolean {
return this.authService.isAuthenticated();
}
}
答案 0 :(得分:3)
您实际上应该订阅此可观察的内容,而不是if (this.authService.authenticationState) {
。另外,我们还需要记住,对离子Storage
进行设置或从中删除实际上是异步的。我们需要等待动作已执行,然后再执行其他操作。我还建议不要在您的可观察对象上调用next
,而只需调用可以执行检查的checkToken()
,然后在BehaviorSubject上调用next
。
这应该有效:
服务:
import { BehaviorSubject, of, forkJoin } from 'rxjs';
import { tap, switchMap, map } from 'rxjs/operators';
// ...
private authenticationState = new BehaviorSubject(false);
public authenticationState$ = this.authenticationState.asObservable();
checkToken() {
this.storage.get(TOKEN_KEY).then(access => {
if (access) {
this.authenticationState.next(true);
} else {
this.authenticationState.next(false);
}
});
}
apilogin(username: string, password: string) {
return this.http.post<any>(`${this.url}`, { username, password }).pipe(
// switch to inner observable
switchMap((data: any) => {
// run all in paralell
return forkJoin(
this.storage.set(TOKEN_KEY, 'access'),
this.storage.set(USERNAME_KEY, 'username'),
this.storage.set(USER_ID, 'id'),
)
}),
// now we know for sure storage values have been set,
// therefore call checkToken()
tap(() => this.checkToken()),
)
}
// seems you are not currently subscribing to this function in the
// component, so I guess you can subscribe here, but I'd subscribe in comp
apilogout() {
forkJoin(
this.storage.remove(USER_ID),
this.storage.remove(REFRESH_TOKEN_KEY),
this.storage.remove(USERNAME_KEY),
this.storage.remove(TOKEN_KEY)
).subscribe(() => this.checkToken())
}
然后该组件将订阅您的BehaviorSubject并执行您需要做的一切:
ngOnInit(): void {
this.sub = this.authService.authenticationState$.pipe(
switchMap((data) => {
return data ? this.storage.get(USER_ID) : of(null)
}),
switchMap((id: any) => {
this.id = id;
return id ? this.userService.getUserDetails(id) : of(null)
})
).subscribe(val => {
// do stuff!
})
}
ngOnDestroy() {
this.sub.unsubscribe();
}
PS。不要使用any
,将数据输入模型:)