如何在 angular 2 中使用自定义 http 刷新访问令牌?
how to refresh the access token using custom http in angular 2?
我在我的应用程序中使用基于令牌的身份验证。我的后端是使用 restful 服务 (spring) 开发的。后端代码很好地生成了所需的访问令牌和带有时间线的刷新令牌,所以我用以下内容覆盖了 http class :
export class customHttp extends Http {
headers: Headers = new Headers({ 'Something': 'Something' });
options1: RequestOptions = new RequestOptions({ headers: this.headers });
private refreshTokenUrl = AppSettings.REFRESH_TOKEN_URL;
constructor(backend: ConnectionBackend,
defaultOptions: RequestOptions,private refresh:OauthTokenService) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log("custom http ");
return super.request(url, options)
.catch((err) => {
if (err.status === 401) {
console.log(" custome http 401 ");
// refresh the token
this.refresh.refresh().subscribe((tokenObj)=>{
console.log("tokenobj ");
})
} else {
console.log("err " + err);
}
}); } }
由于出现循环依赖错误,我无法在 refresh() 方法中刷新令牌,因此我尝试在另一个模块中使用刷新服务,但没有成功。我使用的方法与此 中提到的方法相同。任何帮助都会很棒!
这对我有用:
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
//adding access token to each http request before calling super(..,..)
let token = this.authenticationService.token;
if (typeof url === 'string') {
if (!options) {
options = { headers: new Headers() };
}
options.headers.set('Authorization', `Bearer ${token}`);
}
else {
url.headers.set('Authorization', `Bearer ${token}`);
}
return super.request(url, options)
.catch((error) => {
//if got authorization error - try to update access token
if (error.status = 401) {
return this.authenticationService.updateToken()
.flatMap((result: boolean) => {
//if got new access token - retry request
if (result) {
return this.request(url, options);
}
//otherwise - throw error
else {
return Observable.throw(new Error('Can\'t refresh the token'));
}
})
}
else {
Observable.throw(error);
}
})
}
更新:authenticationService.updateToken() 实施应取决于您使用的授权 provider/authorization 机制。在我的例子中,它是 OAuth Athorization Server,所以实现基本上发送 post 请求,在正文中带有刷新令牌到配置的令牌 url 和 returns 更新的访问和刷新令牌。 tokenEndPointUrl 由 OAuth 配置并发布访问和刷新令牌(取决于发送的 grant_type)。因为我需要刷新令牌,所以我将 grant_type 设置为 refresh_token。代码类似于:
updateToken(): Observable<boolean> {
let body: string = 'refresh_token=' + this.refreshToken + '&grant_type=refresh_token';
return this.http.post(tokenEndPointUrl, body, this.options)
.map((response: Response) => {
var returnedBody: any = response.json();
if (typeof returnedBody.access_token !== 'undefined'){
localStorage.setItem(this.tokenKey, returnedBody.access_token);
localStorage.setItem(this.refreshTokenKey, returnedBody.refresh_token);
return true;
}
else {
return false;
}
})
}
希望对您有所帮助
感谢@dragonfly 的回复,这对我有用
post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
//check if the token is expired then set the latest token
if (this.isTokenExpired) {
options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
}
return super.post(url, body, options)
.catch((err) => {
//if authentication error
if (err.status === 401) {
this.isTokenExpired = true;
options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
//refresh the token
let refreshUrl = this.refreshTokenUrl;
//pass the refresh token
refreshUrl = refreshUrl.replace(':refreshToken', localStorage.getItem("refreshToken"));
//rest the access token
return super.get(refreshUrl).mergeMap((tokenObj) => {
localStorage.setItem("accessToken", tokenObj.json().value);
// reset the headers
options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
//retry the request with the new access token
return this.post(url, body, options)
})
.catch((refreshErr) => {
if (refreshErr.status == 400) {
console.log("refesh err");
window.location.href = 'request=logout';
}
return Observable.throw(refreshErr);
})
} else {
return err;
}
})
}
你能告诉我你是如何更新令牌的吗(this.authenticationService.updateToken())?
对于那些进入此页面但不了解其中任何内容的人。
通俗易懂:
创建刷新令牌方法:
//以某种方式检索刷新令牌
refreshToken(){
let refreshToken = sessionStorage.getItem('refresh_token');
let body = 'grant_type=refresh_token&refresh_token=' + refreshToken;
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Authorization','Basic ' + btoa('something:something'));
return this.http.post('your auth url',body,{headers: headers})
}
比起你的 http 请求(我的使用 angular-jwt authHttp 而不是 http)
testService(){
this.authHttp.get('your secured service url')
.map(res => {
return res;
})
.catch(error=> {
if (error.status === 401) {
return this.refreshToken().flatMap((newToken) => {
let newAccessToken = newToken.json();
sessionStorage.setItem('id_token', newAccessToken['access_token']);
sessionStorage.setItem('refresh_token', newAccessToken['refresh_token']);
return this.authHttp.request('your secured service url');
})
} else {
return Observable.throw(error);
}
})
.subscribe(res => console.log(res));
}
不要忘记导入你需要的东西,比如:
import { AuthHttp } from 'angular2-jwt';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
不要订阅您的刷新令牌方法。如果这样做,您将在服务调用方法的平面图中看到一个大错误。
我在我的应用程序中使用基于令牌的身份验证。我的后端是使用 restful 服务 (spring) 开发的。后端代码很好地生成了所需的访问令牌和带有时间线的刷新令牌,所以我用以下内容覆盖了 http class :
export class customHttp extends Http {
headers: Headers = new Headers({ 'Something': 'Something' });
options1: RequestOptions = new RequestOptions({ headers: this.headers });
private refreshTokenUrl = AppSettings.REFRESH_TOKEN_URL;
constructor(backend: ConnectionBackend,
defaultOptions: RequestOptions,private refresh:OauthTokenService) {
super(backend, defaultOptions);
}
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
console.log("custom http ");
return super.request(url, options)
.catch((err) => {
if (err.status === 401) {
console.log(" custome http 401 ");
// refresh the token
this.refresh.refresh().subscribe((tokenObj)=>{
console.log("tokenobj ");
})
} else {
console.log("err " + err);
}
}); } }
由于出现循环依赖错误,我无法在 refresh() 方法中刷新令牌,因此我尝试在另一个模块中使用刷新服务,但没有成功。我使用的方法与此
这对我有用:
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
//adding access token to each http request before calling super(..,..)
let token = this.authenticationService.token;
if (typeof url === 'string') {
if (!options) {
options = { headers: new Headers() };
}
options.headers.set('Authorization', `Bearer ${token}`);
}
else {
url.headers.set('Authorization', `Bearer ${token}`);
}
return super.request(url, options)
.catch((error) => {
//if got authorization error - try to update access token
if (error.status = 401) {
return this.authenticationService.updateToken()
.flatMap((result: boolean) => {
//if got new access token - retry request
if (result) {
return this.request(url, options);
}
//otherwise - throw error
else {
return Observable.throw(new Error('Can\'t refresh the token'));
}
})
}
else {
Observable.throw(error);
}
})
}
更新:authenticationService.updateToken() 实施应取决于您使用的授权 provider/authorization 机制。在我的例子中,它是 OAuth Athorization Server,所以实现基本上发送 post 请求,在正文中带有刷新令牌到配置的令牌 url 和 returns 更新的访问和刷新令牌。 tokenEndPointUrl 由 OAuth 配置并发布访问和刷新令牌(取决于发送的 grant_type)。因为我需要刷新令牌,所以我将 grant_type 设置为 refresh_token。代码类似于:
updateToken(): Observable<boolean> {
let body: string = 'refresh_token=' + this.refreshToken + '&grant_type=refresh_token';
return this.http.post(tokenEndPointUrl, body, this.options)
.map((response: Response) => {
var returnedBody: any = response.json();
if (typeof returnedBody.access_token !== 'undefined'){
localStorage.setItem(this.tokenKey, returnedBody.access_token);
localStorage.setItem(this.refreshTokenKey, returnedBody.refresh_token);
return true;
}
else {
return false;
}
})
}
希望对您有所帮助
感谢@dragonfly 的回复,这对我有用
post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
//check if the token is expired then set the latest token
if (this.isTokenExpired) {
options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
}
return super.post(url, body, options)
.catch((err) => {
//if authentication error
if (err.status === 401) {
this.isTokenExpired = true;
options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
//refresh the token
let refreshUrl = this.refreshTokenUrl;
//pass the refresh token
refreshUrl = refreshUrl.replace(':refreshToken', localStorage.getItem("refreshToken"));
//rest the access token
return super.get(refreshUrl).mergeMap((tokenObj) => {
localStorage.setItem("accessToken", tokenObj.json().value);
// reset the headers
options.headers.set('Authorization', 'Bearer ' + localStorage.getItem("accessToken"));
//retry the request with the new access token
return this.post(url, body, options)
})
.catch((refreshErr) => {
if (refreshErr.status == 400) {
console.log("refesh err");
window.location.href = 'request=logout';
}
return Observable.throw(refreshErr);
})
} else {
return err;
}
})
}
你能告诉我你是如何更新令牌的吗(this.authenticationService.updateToken())?
对于那些进入此页面但不了解其中任何内容的人。
通俗易懂:
创建刷新令牌方法: //以某种方式检索刷新令牌
refreshToken(){
let refreshToken = sessionStorage.getItem('refresh_token');
let body = 'grant_type=refresh_token&refresh_token=' + refreshToken;
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Authorization','Basic ' + btoa('something:something'));
return this.http.post('your auth url',body,{headers: headers})
}
比起你的 http 请求(我的使用 angular-jwt authHttp 而不是 http)
testService(){
this.authHttp.get('your secured service url')
.map(res => {
return res;
})
.catch(error=> {
if (error.status === 401) {
return this.refreshToken().flatMap((newToken) => {
let newAccessToken = newToken.json();
sessionStorage.setItem('id_token', newAccessToken['access_token']);
sessionStorage.setItem('refresh_token', newAccessToken['refresh_token']);
return this.authHttp.request('your secured service url');
})
} else {
return Observable.throw(error);
}
})
.subscribe(res => console.log(res));
}
不要忘记导入你需要的东西,比如:
import { AuthHttp } from 'angular2-jwt';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
不要订阅您的刷新令牌方法。如果这样做,您将在服务调用方法的平面图中看到一个大错误。