2017-02-06 30 views
1

我想知道是否可以这样做,因为我不确定是否我错了或者不可能。基本上,我想要做的是创建一个本地fetch JavaScript函数的包装功能。这个包装函数将实现令牌验证过程,如果给定的过期已过期并请求再次请求所需的资源,则请求新的accessToken。这是我已经到了到现在为止:将javascript抓取以添加自定义功能

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.'); 
      } 
     }); 
} 

我会尽力解释好一点上面的代码:我想使对用户透明令牌验证,以便让他们只是担心要求他们想要的资源。当accessToken仍然有效时,此方法工作正常,因为return request指令正在向消费者提供fetch请求的承诺。

当然,当accessToken已过期,我们要求一个新的auth服务器,这是行不通的。令牌刷新并请求私人资源,但consumer.js没有看到它。

对于这最后一种情况,是否可以修改程序的流程,以便刷新accessToken并执行服务器调用以再次获取专用资源?消费者不应该意识到这个过程;在这两种情况下(accessToken有效并且accessToken已过期并且已被刷新)consumer.js应该在其then函数中获得私人请求的资源。

回答

1

那么,最后我已经达成了一个解决方案。我试着用Promise来解决它,它有效。下面是customFetch.js文件的方法:

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; 
} 

基本上,我创建了一个Promise和我打过电话里面给它调用服务器获取资源的功能。我已经修改了一些request(现在称为requestResource)和refreshAccessToken,以便使它们具有可参数化的功能。我已经向他们传递了resolve函数,以便在我收到新令牌后“解析”任何函数。

可能该解决方案可以改进和优化,但作为第一种方法,它按照我的预期工作,所以我认为这是一个有效的解决方案。

编辑:正如@丹尼斯提出的那样,我在最初的方法中犯了一个错误。我只需要在refreshAccessToken函数中返回诺言,它就可以正常工作。这是customFetch.js文件的外观(与我刚发布的代码更类似),实际上,我在函数内部添加了return指令,但删除开始和结束括号也是可以的):

// '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

噢,我想我太晚了:DI想建议在这里删除一组括号'const refreshAccesToken =()=> window.fetch(...);'这样''refreshAccesToken''应该返回一个承诺,而不是'undefined',一切都应该工作正常 –

+0

@丹尼斯你是对的!Y在这个功能上犯了一个错误,我忘了回复这个承诺。我已经尝试过,并且工作正常,所以我会编辑我的答案以便添加您的方法,这更接近我的初始解决方案。 – christiansr85

相关问题