Angular 6 - 为啥生产构建中缺少承载令牌? (在开发版本中工作正常)

Posted

技术标签:

【中文标题】Angular 6 - 为啥生产构建中缺少承载令牌? (在开发版本中工作正常)【英文标题】:Angular 6 - Why is Bearer Token missing in production build? (works fine in dev build)Angular 6 - 为什么生产构建中缺少承载令牌? (在开发版本中工作正常) 【发布时间】:2019-04-02 01:07:51 【问题描述】:

我正在使用带有 HTTP 拦截器的 Angular 6,该拦截器配置为将承载令牌应用于传出请求。

在开发版本 (ng serve) 中,应用了令牌并且一切正常。 :-)

在生产版本 (ng serve --prod) 中发送请求时没有持有者令牌。 :-(

在 prod 构建中,我通过在应用标头后将标头转储到控制台来验证正在应用标头。

我不知道为什么它们被排除在 http 请求之外。

我的environment 文件没有任何区别。

我还应该看什么?

我能做些什么来解决这个问题?

起初我认为这是我的本地环境和暂存环境之间的问题,但后来我尝试在本地运行 ng serve --prod 并看到相同的结果。

总而言之,除了一个是生产版本和一个是开发版本之外,一切都是相同的。

jwt-拦截器:

import  Injectable  from '@angular/core';
import  HttpRequest, HttpHandler, HttpEvent, HttpInterceptor  from '@angular/common/http';
import  Observable  from 'rxjs';

@Injectable()
export class JwtInterceptor implements HttpInterceptor 
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 

        // add authorization header with jwt token if available
        let currentUser = JSON.parse(localStorage.getItem('currentUser'));

        if (currentUser && currentUser.token) 
            request = request.clone(
                setHeaders: 
                    Authorization: `Bearer $currentUser.token`
                
            );
            console.log('headers:', request.headers); // <---- I can see headers in console output
        

        return next.handle(request);
    

这是我在控制台中看到的:

app.module.ts

import  HttpClientModule, HttpClient, HttpInterceptor  from '@angular/common/http';
import  BrowserModule  from '@angular/platform-browser';
import  NgModule  from '@angular/core';
import  HTTP_INTERCEPTORS  from '@angular/common/http';
import  PortalModule  from '@angular/cdk/portal';
import  FormsModule, ReactiveFormsModule  from '@angular/forms';

import  JwtInterceptor  from './jwt-interceptor';
import  ENV  from '../environments/environment';
import  AppComponent  from './app.component';
import  AppRoutingModule  from './app-routing.module';
... 
import  myApiService  from './services/my-api.service';
import  myModalComponent  from './_components/my-modal/my-modal.component';
import  myModalService  from './services/my-modal.service';

import  AngularLaravelEchoModule, PusherEchoConfig, EchoInterceptor  from 'angular-laravel-echo/angular-laravel-echo';

export const echoConfig: PusherEchoConfig = 
    userModel: 'App.User',
    notificationNamespace: 'App\\Notifications',
    options: 
        broadcaster: 'pusher',
        key: ENV.pusherConfig.key,
        cluster: ENV.pusherConfig.cluster,
        host: ENV.apiRoot,
        authEndpoint: ENV.apiRoot + '/broadcasting/auth',
    
;

@NgModule(
    declarations: [
        AppComponent,
        ...
    ],
    imports: [
        BrowserModule,
        HttpClientModule,
        BrowserModule,
        AppRoutingModule,
        FormsModule,
        ReactiveFormsModule,
        PortalModule,
        AngularLaravelEchoModule.forRoot(echoConfig)
    ],
    providers: [
        myApiService,
        myModalService,
        
            provide: HTTP_INTERCEPTORS,
            useClass: JwtInterceptor,
            multi: true,
        ,
        
            provide: HTTP_INTERCEPTORS,
            useClass: EchoInterceptor,
            multi: true
        
    ],
    bootstrap: [AppComponent],
    entryComponents: [ 
        myModalComponent
    ]
)

export class AppModule 

【问题讨论】:

你检查过currentUser有什么价值吗? 是的,值在那里,而且,应用了具有定义令牌值的标头(我可以在拦截器的控制台输出中看到这一点) 嗯.. AOT 可能有问题 向我展示 dev/prod 在文件方面的区别。你在 dev 中有什么在 prod 中没有的? 在生产过程中,角度看environment.prod.ts 那里有什么不同吗?同样在您的AppModule 中,您是否有任何仅用于生产的内容? 【参考方案1】:

我在 StackBlitz 中编写了这个应用程序,当我使用 ng serve --prod 在本地运行它时,它运行良好。

https://stackblitz.com/edit/angular-yzckos

下载并运行它,看看您的网络标签中是否仍然收到undefined。如果您可以看到正确发送的标头,那么您的代码中肯定有一些有趣的东西。

试试下面的:

1- 尝试运行 `ng serve --port=aDifferentPort // 像 2098

也许该端口上正在运行某些东西并发送身份验证标头

2- 尝试将 AOT 设为 false,想不出为什么会导致任何问题

3- 确保您的浏览器没有任何覆盖 Auth 标头的扩展程序或尝试其他浏览器

4- 关闭你的其他 HTTP 拦截器,也许其中一个会做一些意想不到的事情

5- 将标题名称从Authorizaion 更改为MyAuthorization,看看你是否仍然未定义,如果你没有,那么它被某个东西覆盖了,检查你的package.json 和确保你没有在生产服务器上运行任何其他东西。

6- 完全关闭JwtInterceptor 并尝试将授权标头附加到您的HTTP 请求中,看看您是否仍然收到undefined

7- 如果没有帮助,您真的需要向我们发送更多代码 :)

【讨论】:

非常感谢您提供要检查的事项清单!就我而言,执行 aot=false 可以防止 prod 构建中的问题。然后,您的建议 #5 我看到了真正的问题所在。对于这个 http 调用,我可以看到拦截器根本没有应用标头。我没有注意到这一点的原因是因为我正在使用一个具有配置选项的包来为它所做的调用设置授权标头,所以它正在设置标头。由于 AOT 的某些原因,我在该包的配置中放入的代码从本地存储中获取令牌值返回未定义。 所以,我不打算调查为什么会这样。而是弄清楚为什么我的拦截器没有将标头应用于此请求。再次感谢您的建议!我现在有了前进的方向。 AOT 确实有问题且不可信,它会吞下错误并有很多意想不到的行为。如果您想要 AOT,请确保运行您的 ng serve --aot=true。但这已被破坏(从 v5 开始),对于每个重大的文件更改,您都必须重新启动它。 好吧,我对此了解不多。我并没有特别“想要”它,但它似乎提供了一些好处,主要是更小的文件大小。正如您所提到的,我注意到一些错误和吞咽错误。 就像在我的情况下,我设置标题的代码,AOT 之后的代码完全删除了包含函数调用的行,例如 JSON.parse 或 'localStorage.getItem`。这就是产品构建对我来说破坏它的原因。但是 CLI 没有警告说存在问题。【参考方案2】:

我在服务器完全忽略Authorization 标头的生产环境中遇到了几乎类似的问题。 Angular 6 正确发送 Authorization 标头,但服务器完全剥离(由于大多数生产服务器,共享托管安全设置)。我知道这可能不是您要找的答案。但是,我只是想给你一个线索。

所以,最后为了让我能正常工作,我不得不使用不同的标头参数,例如 php-Auth-Digest,就像这样。

request = request.clone(
    setHeaders: 
      "Php-Auth-Digest": `Bearer $currentUser.token`,
    
  );

作为一种解决方法,请尝试更改您的标头参数名称。

干杯!

【讨论】:

感谢@Anjana 的回复。我会检查一下。但是,在这两种情况下(我的本地计算机)我都使用相同的环境,并且仅在使用 --prod 标志时我仍然看到标题丢失。服务器中的差异可能会导致这种情况是有道理的,但是由于我的服务器是相同的,因此生成的代码似乎在客户端是不同的。你认为这在这种情况下仍然适用吗? @BizzyBob - 我会尝试更改header 名称。当你主持时,有一天你可能会遇到这个问题。所以,无论如何都值得一试。 forums.cpanel.net/threads/…【参考方案3】:

您可以尝试在实际的 api 调用中设置标头吗?比如,例如:

put(path: string, body: Object = ): Observable<any> 
return this.http.put(`$environment.api_url$path`, body,  headers: 
     this.setHeaders() )
     .map((res: Response) => 
        return res;
     );


private setHeaders(): HttpHeaders 
    const headersConfig = 
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': 'Bearer ' + this.oauthService.getAccessToken()
    ;
    return new HttpHeaders(headersConfig);

而拦截器将只有

request.clone() 

【讨论】:

【参考方案4】:

您可以尝试在 request.clone() 方法中手动克隆标头。这对我有用:

export class HttpHeaderInterceptor implements HttpInterceptor 
  // ...
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    // ...
    const clonedRequest = req.clone( 
      headers: req.headers.set('Authorization', 'Bearer ' + currentUser.token) 
    );
    return next.handle(clonedRequest).pipe(
      catchError(err =>  /* Error handling here */ )
    );
  

希望这会有所帮助:-)

【讨论】:

【参考方案5】:

我对此有一个想法 - 但我不确定它是否可行,请检查

HttpHeaders 是可变的,如果您添加任何标头,它会更新现有标头并附加值 - 所以这会导致我在附加标头时出现问题,因此请遵循以下方法:

private getHeaders(): HttpHeaders 
    let headers = new HttpHeaders();
    headers = headers.append("Content-Type", "application/json");
    return headers;
  

因为,我附加了新的标头并将对象分配给原始对象并返回了对象 - 这对我来说在 prod 和 dev 构建中都很好

但在您的情况下,您可以在HttpInterceptor 中使用上述相同的方法,或者尝试将setheaders 更改为headers,如下所述的示例

if (currentUser && currentUser.token) 
            request = request.clone(
                headers: new HttpHeaders(
                    Authorization: `Bearer $currentUser.token`
                )
            );
            console.log('headers:', request.headers); 
        

我相信这将解决您在两个版本中的问题 - 如果它不起作用,请尝试让我知道 - 希望它能起作用,谢谢 - 编码愉快!!

【讨论】:

【参考方案6】:

试试这个

if (currentUser && currentUser.token) 
        request = request.clone(
            setHeaders: 
                Authorization: `Bearer $currentUser.token`
            
        );
        console.log('headers:', request.headers); // <---- I can see headers in console output
    
if (typeof $ != 'undefined') 
    $.ajaxSetup(
      beforeSend: function (xhr: any) 
        xhr.setRequestHeader('Authorization', 'Bearer ' + currentUser.token);
      
    );
  
    return next.handle(request);

【讨论】:

以上是关于Angular 6 - 为啥生产构建中缺少承载令牌? (在开发版本中工作正常)的主要内容,如果未能解决你的问题,请参考以下文章

PHP Angular - JWT 授权承载令牌

使用承载令牌在 C# 中构建 post HttpClient 请求

为啥我们更喜欢授权标头将承载令牌发送到服务器而不是 URL 编码等其他技术

Servicestack导致承载令牌无效

生产构建后重新加载Angular 6应用程序中断

在 Angular 6 + 节点 js 中使用令牌的身份验证问题