授权Bearer令牌Angular 5

44

我对如何在Angular 5中为一个简单的Get请求创建一个好的头部感到困惑。

这是我需要在Angular中做的: 输入图片描述

这是我目前所拥有的:

  getUserList(): Observable<UserList[]> {
    const headers = new Headers();
    let tokenParse = JSON.parse(this.token)             
    headers.append('Authorization', `Bearer ${tokenParse}`);
    const opts = new RequestOptions({ headers: headers });  
    console.log(JSON.stringify(opts));
    const users = this.http.get<UserList[]>(this.mainUrl, opts)
    return users
            .catch(this.handleError.handleError);         
  }

这是我控制台输出的响应:

{"method":null,"headers":{"Authorization":["Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImYwODZlM2FiYTk0ZjVhMjVmNDhiNzlkYmI2YWUwOWY4YzE2MTUyMzg2N2I5MDZiY2MzNWQyNWJiYTZmYTE4YjEwZjA1MjZiNThkZjE2Y2FjIn0.eyJhdWQiOiJmMDExY2M1OC00MGNlLTQzYTktOGY3MS04NDI0OTRlM2E5OTciLCJqdGkiOiJmMDg2ZTNhYmE5NGY1YTI1ZjQ4Yjc5ZGJiNmFlMDlmOGMxNjE1MjM4NjdiOTA2YmNjMzVkMjViYmE2ZmExOGIxMGYwNTI2YjU4ZGYxNmNhYyIsImlhdCI6MTUyMzU1MTQ0NSwibmJmIjoxNTIzNTUxNDQ1LCJleHAiOjE1MjM1NTQ0NDUsInN1YiI6IjIiLCJzY29wZXMiOlsiYXV0aGVudGljYXRlZCIsImFuZ3VkcnUiXX0.E-WdQTl7nPDW0gj0rohfql-QgnAinzvDxPR-pySMrG07XFY9tA6Ex7IL23pDBmKDmQO8RcZKa0L5r6SRQq9_iqzMWzn5Zxp94J9TJrpZ2KGMoLR_FbK_tpC5G5q5vUnCe3q34sH7cPdT_2cI704OWWaYmIUKKpXWUIG0PJw_uKSJ_uOifPv59RZGQkoaQ9gPywDKe-uamm1Faug-Kk2YnFMoEJq7ou19zyxgdpX80ZTPUae67uB0PGLRuvxfGaqVsJ8k0NunAY3-pyUnBwR_3eeuOxf4TyfW2aiOJ9kuPgsfV4Z1JD7nMpNtTHMJaXEyNkBW8RlYHD1pj4dkdnsDmw"]},"body":null,"url":null,"withCredentials":null,"responseType":null}

看起来很漂亮,但是给了我这个错误:

GET http://druang.dd:8080/user-list?_format=json 403 (Forbidden)

还有另一个线索可以解决这个谜团。在Sublime文本中,如果我将鼠标放在opts上,它会显示类似于以下内容的信息:

ERROR in src/app/services/userlist.service.ts(33,59): error TS2345: Argument of type 'RequestOptions' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'. Types of property 'headers' are incompatible. Type 'Headers' is not assignable to type 'HttpHeaders | { [header: string]: string | string[]; }'. Type 'Headers' is not assignable to type '{ [header: string]: string | string[]; }'. Index signature is missing in type 'Headers'.

有任何想法吗? 这是完整的Git repo。 感谢您的帮助!


我认为你导入了错误的Headers类,请查看Angular文档。在版本5中,http服务已被弃用,改用httpClient服务,但是httpClient在@angular/common中,而另一个在@angular/http中。你不能混合使用这些类,因为会导致错误。对我来说,你的错误看起来像是在"@angular/common"中使用了http,而在"@angular/http"中使用了Headers,而不是应该使用"@angular/common"中的HttpHeaders。 - Nicu
1
嗨,感谢@Nicu,这是我的导入:import { HttpClient, HttpHeaders } from '@angular/common/http'; 根据Angular 文档 - ValRob
太好了 :D @angular/http 将被替换为 @angular/common/http,我想在ng 6中删除。 - Nicu
9个回答

57

我建议使用HttpInterceptor在发送请求时设置默认的HTTP头,而不是为每个调用添加一个额外的HTTP头。

Angular官网 - HTTP客户端 - 设置默认头


在您的示例中,您可以执行以下操作:

import { Http, Headers, Response } from '@angular/http';

getLoggedInUser(auth_token): Observable<any> {
  const headers = new Headers({
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${auth_token}`
  })
  return this.http.get(apiUrl, { headers: headers })
}

7
补充一点,token 应该以 "Bearer " 开头。所以在你的示例中,应为 'Authorization': Bearer ${auth_token} - Alator
5
我需要使用HttpHeaders而不是Headers,但之后一切都正常了。 - Jim
只是出于好奇,使用 ` 符号来组合文本吗? - Agung Kessawa

23

对于get请求,我使用了以下代码并且它可行。

import { HttpClient, HttpHeaders } from '@angular/common/http';

getServerList(){
    var reqHeader = new HttpHeaders({ 
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + JSON.parse(localStorage.getItem('mpManagerToken'))
     });
    return this.http.get<Server[]>(`${environment.apiUrl}/api/Servers/GetServerList`, { headers: reqHeader });
}

9
在Angular 6和7中,可以使用此方法拦截所有HTTP请求并添加bearer令牌。
这里提供了实现教程。这个Youtube频道有所有的教程。
拦截器组件
import {
  HttpInterceptor,
  HttpRequest,
  HttpHandler,
  HttpUserEvent,
  HttpEvent
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { UserService } from '../shared/user.service';
import { tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private router: Router) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    if (req.headers.get('No-Auth') === 'True') {
      return next.handle(req.clone());
    }

    if (localStorage.getItem('userToken') != null) {
      const clonedreq = req.clone({
        headers: req.headers.set(
          'Authorization',
          'Bearer ' + localStorage.getItem('userToken')
        )
      });
      return next.handle(clonedreq).pipe(
        tap(
          succ => {},
          err => {
            if (err.status === 401) {
              // this.router.navigateByUrl('/login');
            } else if ((err.status = 403)) {
              // this.router.navigateByUrl('/forbidden');
              // alert(err.localStorage.getItem('userToken'));
            }
          }
        )
      );
    } else {
      this.router.navigateByUrl('/login');
    }
  }
}

守卫组件

import { Injectable } from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot,
  Router
} from '@angular/router';
import { Observable } from 'rxjs';
import { UserService } from '../shared/user.service';
import { ToastrService } from 'ngx-toastr';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private router: Router,
    private userService: UserService,
    private toastr: ToastrService
  ) {}
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean {
    if (localStorage.getItem('userToken') != null) {
      const roles = next.data['roles'] as Array<string>;
      if (roles) {
        const match = this.userService.roleMatch(roles);
        if (match) {
          return true;
        } else {
          // tslint:disable-next-line: quotemark
          this.toastr.info("You don't have access to this page");
          this.router.navigate(['/login']);
          // this.router.navigate(['/forbidden']);
          return false;
        }
      } else {
        return true;
      }
    }
    this.router.navigate(['/login']);
    return false;
  }
}

将其添加到app.modules.ts文件中。

providers: [
    ConfirmationDialogService,
    UserService,
    DoctorService,
    { provide: OwlDateTimeIntl, useClass: DefaultIntl },
    { provide: OWL_DATE_TIME_FORMATS, useValue: MY_MOMENT_FORMATS },
    AuthGuard,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ],

接下来,将该守卫添加到路由中。

 {
    path: 'adminPanel',
    component: AdminPanelComponent,
    canActivate: [AuthGuard],
    data: { roles: ['Admin'] }
  },

8

有两个问题:

  1. headers.append(...) 不会改变 headers 对象,因此您的授权标头未被发送。您需要执行 headers = headers.append(...)

  2. 尝试使用 this.http.get<UserList[]>(this.mainUrl, { headers: headers });


headers = headers.append 给我返回:无法分配 Headers,因为它是一个常量或只读属性。 - ValRob
1
将声明从 const headers 改为 let headers 或者执行 const headers = new Headers().append('Authorization', Bearer ${tokenParse}); - GreyBeardedGeek
使用 let 时,会提示:类型“void”不能赋值给类型“Headers” - ValRob
另一个选项是可行的,但当我设置 const opts = new RequestOptions({ headers: headers }); 时,我得到了以下错误:ERROR in src/app/services/userlist.service.ts(33,37): error TS2345: Argument of type '{ headers: void; }' is not assignable to parameter of type 'RequestOptionsArgs'. Types of property 'headers' are incompatible. Type 'void' is not assignable to type 'Headers'. - ValRob
另外,如果我忘记了选项,直接在http get请求中使用{headers: headers},我会得到以下错误:`ERROR in src/app/services/userlist.service.ts(35,59): error TS2345: Argument of type '{ headers: void; }' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'。属性“headers”的类型不兼容。类型“void”无法分配给类型“HttpHeaders | {[header: string]: string | string[];}”。 - ValRob
我找到了解决方案... 如果你能给我解释一下,谢谢!const users = this.http.get<UserList[]>(this.mainUrl, { headers:new HttpHeaders().set('Authorization', Bearer ${tokenParse})}) - ValRob

3
我已经尝试了上述所有建议的格式来附加包含访问令牌的标头,但在请求标头中添加null,如“Authorization Bearer null”。 如果我在添加/克隆标头的行之前打印accesstoken,则浏览器控制台会输出accesstoken值。这是我使用的格式。
console.log("Inside Interceptor accesstoken : " + this.oauthService.getAccessToken());
    req = req.clone({
      setHeaders: {
         Authorization: 'Bearer ' +  this.oauthService.getAccessToken()
      }
    });
    return next.handle(req);

2

我不是很擅长编程,但经过一些尝试和失败,我发现了以下内容:

  getUserList(): Observable<UserList[]> {
    let tokenParse = JSON.parse(this.token)    
    // let myHeaders = new Headers();
    // myHeaders.set('Authorization', `Bearer ${tokenParse}`);
    // let options = new RequestOptions({ headers: myHeaders});
    const users = this.http.get<UserList[]>(this.mainUrl, { headers:new HttpHeaders().append('Authorization', `Bearer ${tokenParse}`)})
    // const users = this.http.get<UserList[]>(this.mainUrl, options);
    return users
            .catch(this.handleError.handleError);         
  }

我使用 .set 或者 .append 都没关系,最终两种情况都能正常工作...

我不太清楚发生了什么,如果有人想在评论中解释一下,欢迎...


2
'Authorization': 'Bearer ' + access_token,

那样做很好。

2

@HassanRahman展示了如何处理get请求,但对于post请求,

import { HttpClient, HttpHeaders } from '@angular/common/http';

getServerList(){

    postData = { your data }
    var reqHeader = new HttpHeaders({ 
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + JSON.parse(localStorage.getItem('mpManagerToken'))
     });
    return this.http.post<Server[]>(`${environment.apiUrl}/api/Servers/GetServerList`, postData, { headers: reqHeader });
}

1
在我看来,这是非常重要的答案,我不知道为什么会被踩。在我的情况下,我在服务器上有一个没有参数的post方法,所以有了this.router.post<string>('https://localhost:5001/api/method', null, {headers: { 'Authorization': \Bearer ${localStorage.getItem('authToken')}`, }}).subscribe(data => { //现在使用数据 window.location.href = data; });当然最好使用拦截器,但我认为这个简单的基本示例同时使用postget`也很重要。 - Lazar Đorđević
@LazarĐorđević 感谢您的评论。 - Achala Dissanayake

0

另一种方法 使用set或append函数设置HttpHeader

const reqHeader = new HttpHeaders().set('Authorization', 'Bearer ' + this.accessToken);

返回 this.http.get<any[]>(this.webApiUrlEndPoint, { headers: reqHeader});


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接