angular - Angular 6:刷新令牌未按预期工作
问题描述
我正在使用Angular 6
和使用 OAuth2 实现的Django REST Framework
API端点。
我是我的 Angular 应用程序,我正在存储access_token
, refresh_token
, expires_in
, generate_time
, user_valid
,token_type
而用户使用他的username
and password
, 在localStorage中登录
由于在expires_in
几分钟内非常低,因此即使用户在页面上处于活动状态,access_token 也会过期。因此,我需要access_token
使用 saved重新生成refresh_token
。
我有auth.interceptor.ts来添加每个请求access_token
以授权请求。
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable} from 'rxjs';
import {AuthService} from './auth.service';
import {Injectable} from '@angular/core';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(
public Auth: AuthService
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
console.log('interceptor');
request = request.clone({
setHeaders: {
Authorization: `${this.Auth.tokenType()} ${this.Auth.accessToken()}`
}
});
return next.handle(request);
}
}
和auth-guard.service.ts来保护受保护的 URL,并检查令牌是否过期,如果令牌过期则重新生成访问令牌。
import { Injectable } from '@angular/core';
import {CanActivate, Router} from '@angular/router';
import {AuthService} from './auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuardService implements CanActivate {
constructor(
public Auth: AuthService,
public router: Router
) { }
/**
* this method is used to check if user is authenticated or not
* if user is not authenticated, is redirected to login page
*/
canActivate(): boolean {
// if userValid is true and user is not authenticated,
// probably access token has been expired,
// refresh token
if (this.Auth.userValid()) {
if (!this.Auth.isAuthenticated()) {
this.Auth.refreshAuthToken();
}
}
// if user is not authenticated,
// redirect to login page
if (!this.Auth.isAuthenticated()) {
this.router.navigate(['auth/login']);
}
return true;
}
}
有一个auth.service.ts来检查用户是否经过身份验证和有效,并且有token.service.ts来管理令牌并保存在localStorage中并从存储中检索。
import { Injectable } from '@angular/core';
import {HttpClient, HttpRequest} from '@angular/common/http';
import {ResourceProviderService} from '../resource-provider.service';
import {Observable} from 'rxjs';
import {AuthToken} from './auth.model';
import {TokenService} from './token.service';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(
private resource: ResourceProviderService,
private http: HttpClient,
private token: TokenService
) { }
/**
* returns access token from token service
*/
public accessToken(): string {
return this.token.accessToken();
}
/**
* Get authentication token credentials like access token, token secret, expires in
* from the server for the authenticated user
*/
getAuthToken(username: any, password: any): Observable<AuthToken> {
const authFormData = new FormData();
authFormData.append('grant_type', this.resource.grant_type);
authFormData.append('client_id', this.resource.client_key);
authFormData.append('client_secret', this.resource.client_secret);
authFormData.append('scope', this.resource.scope);
authFormData.append('username', username);
authFormData.append('password', password);
return this.http.post<AuthToken>(this.resource.url + '/auth/token/', authFormData);
}
/**
* refresh token of the user using the refresh token stored
*/
refreshAuthToken() {
console.log('refresh token');
const authFormData = new FormData();
authFormData.append('grant_type', this.resource.grant_type_refresh_token);
authFormData.append('client_id', this.resource.client_key);
authFormData.append('client_secret', this.resource.client_secret);
authFormData.append('refresh_token', this.token.refreshToken());
this.http.post<AuthToken>(this.resource.url + '/auth/token/', authFormData).subscribe(
response => {
console.log('setting credentials');
this.token.setCredentials(response);
}, error => {
console.log(error.status, error.error.error);
console.log(error);
}
);
}
/**
* Method set credentials using token service and
* stores in local storage of the browser
*/
setCredentials(response: AuthToken): void {
this.token.setCredentials(response);
}
/**
* Method is called to logout the user
*/
clearCredentials(): void {
this.token.clearCredentials();
}
isAuthenticated(): boolean {
// Check whether the token is expired and return
// true or false
return !this.token.isTokenExpired();
}
userValid(): boolean {
return this.token.userValid();
}
tokenType(): string {
return this.token.tokenType();
}
}
每当access_token
过期时,isAuthenticated()
返回 false 并从auth-guard服务this.Auth.refreshAuthToken()
调用。
但它首先重定向到登录页面,并refreshAuthToken()
在最后一次调用,刷新令牌后网络请求中断。
1.即使刷新访问令牌后也不会重定向回页面。
2. 为什么会加载登录页面?我想在令牌过期时静默刷新访问令牌。
解决方案
你应该重写你的canActivate
方法来解释你的方法的异步行为refreshAuthToken
:
canActivate(): Observable<boolean> | boolean {
if (!this.Auth.userValid()) {
return this.cannotActivate();
} else if (this.Auth.isAuthenticated()) {
return true;
} else {
return this.Auth.refreshAuthToken().pipe(
map(() => this.Auth.isAuthenticated() || this.cannotActivate())
);
}
}
cannotActivate(): boolean {
this.router.navigate(['auth/login']);
return false;
}
但是为了让它起作用,你还应该从你的refreshAuthToken
方法中返回一个 Observable:
refreshAuthToken(): Observable<any> {
//...
return this.http.post<AuthToken>(this.resource.url + '/auth/token/', authFormData).pipe(
tap((response) => this.token.setCredentials(response))
)
}
推荐阅读
- javascript - chrome更新后Jquery UI可拖动不起作用
- scala - Spark UDF 现在抛出 ArrayIndexOutOfBoundsException
- json - 如何将我的数据保存在 json 中并将其存储在数据库中(Django rest 框架)
- dompdf - dompdf 每个页面标题显示在左侧缩略图中
- ruby-on-rails - 更改设计邮件程序的模板路径而不是默认的设计邮件程序视图
- javascript - webpack.resolve.alias 不解析节点包?
- javascript - 三个 js TextureLoader() 在部署时不起作用
- forms - 竖版格式 Extjs
- android-studio-4.2 - android studio 4.2.1 分辨率降低问题
- python - GridSearchCV 返回的精度比默认值差