微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

具有动态标头授权的 Angular Apollo WebSocketLink

如何解决具有动态标头授权的 Angular Apollo WebSocketLink

我正在使用 graphql 订阅;最初标头有一个空的授权令牌,登录后在 localstorage 中生成一个令牌变量。我想要做的是在登录自动更新订阅标头中的令牌变量,我已经按照文档所述进行了尝试,但令牌从未更新并且始终将其发送为空。

我需要 WebSocketLink 中的标头在 connectionParams 中是动态的

这是我的 GraphQLModule 文件,希望有人能帮助我...

import { NgModule } from '@angular/core';
import { ApolloClientOptions,InMemoryCache,split,ApolloLink} from '@apollo/client/core';
import { APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import {WebSocketLink} from '@apollo/client/link/ws';
import {getMainDeFinition} from '@apollo/client/utilities';
import { environment } from 'environments/environment';
import * as CryptoJS from 'crypto-js';  
import { setContext } from '@apollo/client/link/context';
import {onError} from '@apollo/client/link/error';
import Swal from 'sweetalert2';
import { CoreTranslationService } from '@core/services/translation.service';
import { locale as en } from 'app/main/pages/i18n/en';
import { locale as es } from 'app/main/pages/i18n/es';

const uri = environment.apiUrl; // <-- endpoint1 gql
const urisub = environment.apiSubs;// <-- endpoint2 gql



function operationFilter(operationName:string):boolean{  
  if(operationName!="checkToken") return true;    
  else return false;      //and the others
 }

export function createApollo(httpLink: HttpLink,_coreTranslationService: CoreTranslationService): ApolloClientOptions<any> {

  _coreTranslationService.translate(en,es);

  const basic = setContext((operation,context) => ({
    headers: {
      Accept: 'charset=utf-8'
    }
  }));

  const auth = setContext((operation,context) => {
      const token = localStorage.getItem('token');
      
  
      if (token === null) {
        return {};
      } else {
        let token_decrypt= CryptoJS.AES.decrypt(token,environment.key_decrypt).toString(CryptoJS.enc.Utf8)
        return {
          headers: {
            Authorization: `Bearer ${token_decrypt}`
          }
        };
      }
    });

  const http = httpLink.create({
    uri(operation){ 
      return operationFilter(operation.operationName)? uri : urisub;
    } 
  });
 
  const ws = new WebSocketLink({
    uri:`ws://localhost:3005/subscriptions`,options:{      
      lazy: true,reconnect: true,connectionParams: async () => {
        const token =  localStorage.getItem('token');
        let token_decrypt= null
        if (token) {
           token_decrypt= CryptoJS.AES.decrypt(token,environment.key_decrypt).toString(CryptoJS.enc.Utf8) 
        }               
        return {                 
            Authorization: token ? `Bearer ${token_decrypt}` : "",}
      },}
  });


  const error = onError(({networkError,graphQLErrors}) => {
        if (graphQLErrors) {            
          
          graphQLErrors.map(({
                  message,locations,path,extensions
              }) =>{

                console.log('error graph',localStorage.getItem('token'));
                
                if (extensions && localStorage.getItem('token')!=null) {
                  if (extensions.exception.status==401) {

                    Swal.fire({
                      icon: 'error',title: _coreTranslationService.instant('ErrorSub.Title'),text: _coreTranslationService.instant('ErrorSub.Message'),timer: 6000,timerProgressBar: true,showCancelButton: false,showConfirmButton: false,allowOutsideClick: false,allowEscapeKey: false
                    }); 
                    
          
                    setTimeout(() => {  
                      localStorage.clear();                 
                      window.location.href = "/pages/authentication/login-v2";                       
                    },7000);
                    
                    
                  }
                }
                
              }
              
          );
      }
      if (networkError) {
          console.log(`[Network error]: ${networkError}`);
      }
  })
  

  const _split = split(
    ({query}) => {
      const data = getMainDeFinition(query);
      return (
        data.kind === 'OperationDeFinition' && data.operation === 'subscription'
      );
    },ws,//http
    auth.concat(http)
  )

  
  
  

  const cleanTypeName = new ApolloLink((operation,forward) => {
    if (operation.variables) {
      const omitTypename = (key,value) => (key === '__typename' ? undefined : value);
      operation.variables = JSON.parse(JSON.stringify(operation.variables),omitTypename);
    }
    return forward(operation).map((data) => {
      return data;
    });
  });

  


  const link =ApolloLink.from([cleanTypeName,basic,error,_split]);
 
  
  
  return {
    link: link,cache: new InMemoryCache({
      addTypename: false,}),defaultOptions: {
      watchQuery: {
        fetchPolicy: 'network-only',//errorPolicy: 'all',},query: {
          fetchPolicy: 'network-only',mutate: {
        
      }
    },};
}

@NgModule({
  providers: [
    {
      provide: APOLLO_OPTIONS,useFactory: createApollo,deps: [HttpLink,CoreTranslationService],],})
export class GraphQLModule {
}

解决方法

我分享了我的解决方案,我不知道它是否是最好的选择,但它对我有用,在客户端:

graphql.module.ts

import { NgModule } from '@angular/core';
import { ApolloClientOptions,InMemoryCache,split,ApolloLink,Operation,makeVar} from '@apollo/client/core';
import { Apollo,APOLLO_OPTIONS } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import {WebSocketLink} from '@apollo/client/link/ws';
import {getMainDefinition} from '@apollo/client/utilities';
import { environment } from 'environments/environment';
import * as CryptoJS from 'crypto-js';  
import { setContext } from '@apollo/client/link/context';
import {onError} from '@apollo/client/link/error';
import Swal from 'sweetalert2';
import { CoreTranslationService } from '@core/services/translation.service';
import { locale as en } from 'app/main/pages/i18n/en';
import { locale as es } from 'app/main/pages/i18n/es';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import { HttpClientModule } from '@angular/common/http';
import {HttpLinkModule} from 'apollo-angular-link-http';


const uri = environment.apiUrl; 
const urisub = environment.apiSubs;



function operationFilter(operationName:string):boolean{  
  if(operationName!="checkToken") return true;  
  else return false;      


@NgModule({

  exports: [
    HttpClientModule,HttpLinkModule
  ]
})
export class GraphQLModule {
    public Clientws: any;
    public subscriptionClient: SubscriptionClient = null;

   constructor(apollo: Apollo,httpLink: HttpLink,_coreTranslationService: CoreTranslationService){

    _coreTranslationService.translate(en,es);

    const getIdToken = () => localStorage.getItem('token') || null;

    const auth = setContext((operation,context) => {      
        return {
          headers: {
            Authorization: `Bearer ${getIdToken()}`
          }
        };      
    });

    const http = httpLink.create({
        uri(operation){ 
          return operationFilter(operation.operationName)? uri : urisub;
        } 
    });


      
      const wsClient = new SubscriptionClient(`ws://localhost:3005/subscriptions`,{
        reconnect: true,connectionParams: async () => {                
          return {                 
              Authorization:`Bearer ${getIdToken()}`,}
        },})



      this.Clientws = wsClient

      const ws = new WebSocketLink(wsClient)

      

      this.subscriptionClient = (<any>ws).subscriptionClient;

        const error = onError(({networkError,graphQLErrors}) => {

                      
        if (graphQLErrors  && getIdToken()!=null && getIdToken()!='') {            
          
          graphQLErrors.map(({
                  message,locations,path,extensions
              }) =>{

                if (extensions) {
                  if (extensions.exception.status==401 && getIdToken()!=null && getIdToken()!='') {                      
                    

                    Swal.fire({
                      icon: 'error',title: _coreTranslationService.instant('ErrorSub.Title'),text: _coreTranslationService.instant('ErrorSub.Message'),timer: 6000,timerProgressBar: true,showCancelButton: false,showConfirmButton: false,allowOutsideClick: false,allowEscapeKey: false
                    }); 
                    
          
                    setTimeout(() => {  
                      localStorage.clear();                 
                      window.location.href = "/pages/authentication/login-v2";                       
                    },7000);
                    
                    
                  }
                }
                
              }
              
          );
      }
      if (networkError) {
          console.log(`[Network error]:`,networkError);
      }
  })

      const _split = split(
        ({query}) => {
          const data = getMainDefinition(query);
          return (
            data.kind === 'OperationDefinition' && data.operation === 'subscription'
          );
        },ws,auth.concat(http)
      )


        const cleanTypeName = new ApolloLink((operation,forward) => {
          if (operation.variables) {
            const omitTypename = (key,value) => (key === '__typename' ? undefined : value);
            operation.variables = JSON.parse(JSON.stringify(operation.variables),omitTypename);
          }
          return forward(operation).map((data) => {
            return data;
          });
        });

          const basic = setContext((operation,context) => ({
            headers: {
              Accept: 'charset=utf-8'
            }
          }));

  


  const link =ApolloLink.from([cleanTypeName,basic,error,_split]);

    apollo.create({
      link:link,cache: new InMemoryCache({
        addTypename: false,}),defaultOptions: {
        watchQuery: {
          fetchPolicy: 'network-only',//errorPolicy: 'all',},query: {
            fetchPolicy: 'network-only',mutate: {
          
        }
      },});
   }
}

login.component.ts

constructor(
    .....,private gqlModule: GraphQLModule,.....
  ){
     .......
  }

onSubmit(event) { //submit login form 
......

//if service login response true
this.gqlModule.Clientws.close(false,false); // this closes the websocket and automatically it reconnects with the new information in the header
//Note .. If you do not have the reconnection in true,you need to connect again manually    
......
}

在服务器端:

配置订阅选项

onConnect: (connectionParams,webSocket,context) => {   
         

          if (connectionParams['Authorization'] != null && connectionParams['Authorization'] != '') {

            if (connectionParams['Authorization'].split(" ")[1]!= null && connectionParams['Authorization'].split(" ")[1]!= '' && connectionParams['Authorization'].split(" ")[1]!= 'null') { 
             return { authorization: connectionParams['Authorization'] };
            }
            
          } 
          
        },

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。