2017-02-06 1 views
1

hinzufügen würde ich gerne wissen, ob es möglich ist, dies zu tun, weil ich nicht sicher bin, wenn ich falsch liege, oder wenn es nicht möglich ist. Im Grunde, was ich tun möchte, ist eine Wrap-Funktion für native fetch Javascript-Funktion zu erstellen. Diese Umbruchfunktion würde den Tokenvalidierungsprozess implementieren und eine neue accessToken anfordern, wenn die angegebene abgelaufen ist und die gewünschte Ressource erneut anfordert. Dies ist, was ich bis jetzt erreicht haben:Wrap javascript holen benutzerdefinierte Funktionen

customFetch.js

// 'url' and 'options' parameters are used strictely as you would use them in fetch. 'authOptions' are used to configure the call to refresh the access token 
window.customFetch = (url, options, authOptions) => { 

    const OPTIONS = { 
     url: '', 
     unauthorizedRedirect: '', 
     storage: window.sessionStorage, 
     tokenName: 'accessToken' 
    } 

    // Merge options passed by user with the default auth options 
    let opts = Object.assign({}, OPTIONS, authOptions); 

    // Try to update 'authorizarion's header in order to send always the proper one to the server 
    options.headers = options.headers || {}; 
    options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`; 

    // Actual server request that user wants to do. 
    const request = window.fetch(url, options) 
     .then((d) => { 
      if (d.status === 401) { 
       // Unauthorized 
       console.log('not authorized'); 
       return refreshAccesToken(); 
      } 
      else { 
       return d.json(); 
      } 
     }); 

    // Auxiliar server call to get refresh the access token if it is expired. Here also check if the 
    // cookie has expired and if it has expired, then we should redirect to other page to login again in 
    // the application. 
    const refreshAccesToken =() => { 
     window.fetch(opts.url, { 
      method: 'get', 
      credentials: 'include' 
     }).then((d) => { 
      // For this example, we can omit this, we can suppose we always receive the access token 
      if (d.status === 401) { 
       // Unauthorized and the cookie used to validate and refresh the access token has expired. So we want to login in to the app again 
       window.location.href = opts.unauthorizedRedirect; 
      } 

      return d.json(); 
     }).then((json) => { 
      const jwt = json.token; 
      if (jwt) { 
       // Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request 
       opts.storage.setItem(opts.tokenName, jwt); 
       console.log('new acces token: ' + jwt); 

       // Re-send the original request when we have received the refreshed access token. 
       return window.customFetch(url, options, authOptions); 
      } 
      else { 
       console.log('no token has been sent'); 
       return null; 
      } 
     }); 
    } 

    return request; 
} 

consumer.js

const getResourcePrivate =() => { 
     const url = MAIN_URL + '/resource'; 
     customFetch(url, { 
      method: 'get' 
     },{ 
      url: AUTH_SERVER_TOKEN, 
      unauthorizedRedirect: AUTH_URI, 
      tokenName: TOKEN_NAME 
     }).then((json) => { 
      const resource = json ? json.resource : null; 
      if (resource) { 
       console.log(resource); 
      } 
      else { 
       console.log('No resource has been provided.'); 
      } 
     }); 
} 

Ich werde versuchen, ein wenig besser den obigen Code zu erklären: Ich möchte für Anwender transparent machen die Token-Validierung, damit sie sich nur darum kümmern müssen, die gewünschte Ressource anzufordern. Dieser Ansatz funktioniert gut, wenn die accessToken immer noch gültig ist, weil die return request Anweisung dem Verbraucher das Versprechen der fetch Anfrage gibt.

Natürlich, wenn die accessToken abgelaufen ist und wir einen neuen zu auth Server anfordern, funktioniert dies nicht. Das Token wird aktualisiert und die private Ressource wird angefordert, aber consumer.js sieht es nicht.

Für dieses letzte Szenario, ist es möglich, den Fluss des Programms zu ändern, um die accessToken zu aktualisieren und den Serveraufruf durchzuführen, um die private Ressource wieder zu bekommen? Der Verbraucher sollte sich über diesen Prozess nicht bewusst sein; in beiden Fällen (accessToken gültig ist und accessToken ist abgelaufen und aktualisiert wurde), um die consumer.js sollte die private angeforderte Ressource in seiner then Funktion erhalten.

Antwort

1

Nun, endlich habe ich eine Lösung erreicht. Ich habe versucht, es mit einem Promise zu lösen, und es hat Arbeit. Hier ist der Ansatz für customFetch.js Datei:

window.customFetch = (url, options, authOptions) => { 

    const OPTIONS = { 
     url: '', 
     unauthorizedRedirect: '', 
     storage: window.sessionStorage, 
     tokenName: 'accessToken' 
    } 

    // Merge options passed by user with the default auth options 
    let opts = Object.assign({}, OPTIONS, authOptions); 

    const requestResource = (resolve) => { 
     // Try to update 'authorizarion's header in order to send always the proper one to the server 
     options.headers = options.headers || {}; 
     options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`; 

     window.fetch(url, options) 
      .then((d) => { 
       if (d.status === 401) { 
        // Unauthorized 
        console.log('not authorized'); 
        return refreshAccesToken(resolve); 
       } 
       else { 
        resolve(d.json()); 
       } 
      }); 
    } 

    // Auxiliar server call to get refresh the access token if it is expired. Here also check if the 
    // cookie has expired and if it has expired, then we should redirect to other page to login again in 
    // the application. 
    const refreshAccesToken = (resolve) => { 
     window.fetch(opts.url, { 
      method: 'get', 
      credentials: 'include' 
     }).then((d) => { 
      if (d.status === 401) { 
       // Unauthorized 
       window.location.href = opts.unauthorizedRedirect; 
      } 

      return d.json(); 
     }).then((json) => { 
      const jwt = json.token; 
      if (jwt) { 
       // Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request 
       opts.storage.setItem(opts.tokenName, jwt); 
       console.log('new acces token: ' + jwt); 

       // Re-send the original request when we have received the refreshed access token. 
       requestResource(resolve); 
      } 
      else { 
       console.log('no token has been sent'); 
       return null; 
      } 
     }); 
    } 

    let promise = new Promise((resolve, reject) => { 
     requestResource(resolve); 
    }); 

    return promise; 
} 

Im Grunde habe ich erstellt ein Promise und ich habe in ihm auf die Funktion aufgerufen, die mit dem Server ruft die Ressource zu erhalten. Ich habe ein wenig verändert die request (jetzt requestResource genannt) und refreshAccessToken um sie parametrierbaren Funktionen zu machen. Und ich habe ihnen die resolve Funktion übergeben, um irgendeine Funktion "aufzulösen", sobald ich das neue Token erhalten habe.

Wahrscheinlich kann die Lösung verbessert und optimiert werden, sondern als erster Ansatz, es funktioniert wie ich erwartet hatte, so dass ich denke, es ist eine gültige Lösung ist.

BEARBEITEN: Da @Dennis mich vorgeschlagen hat, machte ich einen Fehler in meinem ersten Ansatz. Ich musste nur das Versprechen innerhalb der refreshAccessToken Funktion zurückgeben, und es würde gut funktionieren. So sollte die Datei customFetch.js aussehen (die dem Code ähnelt, den ich zuerst gepostet habe. Tatsächlich habe ich gerade einen return Befehl innerhalb der Funktion hinzugefügt, obwohl das Entfernen der Anfangs- und Endklammer auch funktionieren würde):

// 'url' and 'options' parameters are used strictely as you would use them in fetch. 'authOptions' are used to configure the call to refresh the access token 
window.customFetch = (url, options, authOptions) => { 

    const OPTIONS = { 
     url: '', 
     unauthorizedRedirect: '', 
     storage: window.sessionStorage, 
     tokenName: 'accessToken' 
    } 

    // Merge options passed by user with the default auth options 
    let opts = Object.assign({}, OPTIONS, authOptions); 

    // Try to update 'authorizarion's header in order to send always the proper one to the server 
    options.headers = options.headers || {}; 
    options.headers['Authorization'] = `Bearer ${opts.storage.getItem(opts.tokenName)}`; 

    // Actual server request that user wants to do. 
    const request = window.fetch(url, options) 
     .then((d) => { 
      if (d.status === 401) { 
       // Unauthorized 
       console.log('not authorized'); 
       return refreshAccesToken(); 
      } 
      else { 
       return d.json(); 
      } 
     }); 

    // Auxiliar server call to get refresh the access token if it is expired. Here also check if the 
    // cookie has expired and if it has expired, then we should redirect to other page to login again in 
    // the application. 
    const refreshAccesToken =() => { 
     return window.fetch(opts.url, { 
      method: 'get', 
      credentials: 'include' 
     }).then((d) => { 
      // For this example, we can omit this, we can suppose we always receive the access token 
      if (d.status === 401) { 
       // Unauthorized and the cookie used to validate and refresh the access token has expired. So we want to login in to the app again 
       window.location.href = opts.unauthorizedRedirect; 
      } 

      return d.json(); 
     }).then((json) => { 
      const jwt = json.token; 
      if (jwt) { 
       // Store in the browser's storage (sessionStorage by default) the refreshed token, in order to use it on every request 
       opts.storage.setItem(opts.tokenName, jwt); 
       console.log('new acces token: ' + jwt); 

       // Re-send the original request when we have received the refreshed access token. 
       return window.customFetch(url, options, authOptions); 
      } 
      else { 
       console.log('no token has been sent'); 
       return null; 
      } 
     }); 
    } 

    return request; 
} 
+2

Oh, ich glaube, ich bin zu spät dann: DI Entfernen eine Reihe von Klammern vorschlagen wollte hier 'const refreshAccesToken =() => window.fetch (...);' 'auf diese Weise refreshAccesToken' sollte eine Rück Versprechen, statt 'undefined' und alles sollte gut funktionieren –

+0

@Dennis du bist richtig !!Y hat in dieser Funktion einen Fehler gemacht und ich habe vergessen, das Versprechen zurückzugeben. Ich habe versucht und es funktioniert gut, also werde ich meine Antwort bearbeiten, um auch Ihre Vorgehensweise hinzuzufügen, die meiner ursprünglichen Lösung noch näher kam. – christiansr85

Verwandte Themen