React - 如何在发送帖子请求之前检查JWT是否有效?

Sam*_*nti 6 javascript login jwt reactjs redux

另一个菜鸟问题.我正在使用JWT授权将我的用户登录到系统,获取令牌并将其保存localstorage,然后发送保存数据的发布请求(基本上是一个很大的形式).问题是,服务器在给定时间(大约20分钟)之后使令牌无效,因此,我的一些帖子请求正在返回401 status.如何在发送帖子请求之前验证(如果需要,显示登录提示)?我正在使用redux-form我的表格.

PS:我知道我应该使用动作创作者等,但我仍然是新手,所以不太擅长这些东西.

这是我的身份验证:

export function loginUser(creds) {

const data = querystring.stringify({_username: creds.username, _password: creds.password});

let config = {
    method: 'POST',
    headers: { 'Content-Type':'application/x-www-form-urlencoded' },
    body: data
};

return dispatch => {
    // We dispatch requestLogin to kickoff the call to the API
    dispatch(requestLogin(creds));

    return fetch(BASE_URL+'/login_check', config)
        .then(response =>
            response.json().then(user => ({ user, response }))
        ).then(({ user, response }) =>  {
            if (!response.ok) {
                // If there was a problem, we want to
                // dispatch the error condition
                dispatch(loginError(user.message));
                return Promise.reject(user)
            } else {
                // If login was successful, set the token in local storage
                localStorage.setItem('id_token', user.token);
                let token = localStorage.getItem('id_token')
                console.log(token);
                // Dispatch the success action
                dispatch(receiveLogin(user));
            }
        }).catch(err => console.log("Error: ", err))
    }
}
Run Code Online (Sandbox Code Playgroud)

这是POST请求(我values从中获取对象redux-form)

const token = localStorage.getItem('id_token');
const AuthStr = 'Bearer '.concat(token);

let headers ={
headers: { 'Content-Type':'application/json','Authorization' : AuthStr }
};

export default (async function showResults(values, dispatch) {
axios.post(BASE_URL + '/new', values, headers)
    .then(function (response) {
        console.log(values);
        console.log(response);
    })
    .catch(function (error) {
        console.log(token);
        console.log(values)
        console.log(error.response);
    });
});
Run Code Online (Sandbox Code Playgroud)

PPS:如果有人有任何改进我的代码的建议,请随时发表评论.

jaf*_*mlp 15

这是通过将 JWT 令牌中的 exp 属性与当前时间进行比较来使用jwt-decode库的解决方案。(JWT 令牌只是一个 Base64 编码的字符串)

  • 安装 jwt-decode ( npm install jwt-decode --save)

     let token = localStorage.getItem(TOKEN);
      let decodedToken = jwt_decode(token);
      console.log("Decoded Token", decodedToken);
      let currentDate = new Date();
    
      // JWT exp is in seconds
      if (decodedToken.exp * 1000 < currentDate.getTime()) {
        console.log("Token expired.");
      } else {
        console.log("Valid token");   
        result = true;
      }
    
    Run Code Online (Sandbox Code Playgroud)

重要提示: jwt-decode 不验证令牌,任何格式良好的 JWT 都可以解码。您应该使用 express-jwt、koa-jwt、Owin Bearer JWT 等来验证服务器端逻辑中的令牌。

  • 我认为这比公认的答案更有效。“jwt-decode”是由“jsonwebtoken”的同一作者制作的,在接受的答案中提到了该作者,如果您只想从浏览器(客户端)“解码”(并且必须)“令牌” ),这个库就是为此目的而创建的。 (2认同)

Suv*_*tha 10

可以通过两种方式检查JWT到期时间.首先,您必须安装jsonwebtoken包并在文件顶部需要它.此后,您可以按照以下方式检查JWT到期,然后再发送任何休息请求.

选项1

var isExpired = false;
const token = localStorage.getItem('id_token');
var decodedToken=jwt.decode(token, {complete: true});
var dateNow = new Date();

if(decodedToken.exp < dateNow.getTime())
    isExpired = true;
Run Code Online (Sandbox Code Playgroud)

选项2

const token = localStorage.getItem('id_token');
jwt.verify(token, 'shhhhh', function(err, decoded) {
  if (err) {
    /*
      err = {
        name: 'TokenExpiredError',
        message: 'jwt expired',
        expiredAt: 1408621000
      }
    */
  }
});
Run Code Online (Sandbox Code Playgroud)

检查该方法的错误.如果是TokenExpiredError则表示令牌已过期.

  • 您确定解决方案 2 吗?这意味着前端将拥有用于检查令牌的密钥。然后查看源代码,可以找到并使用它生成valide令牌 (13认同)
  • @SamiaRuponti 只是为了向您澄清有关 JWT 令牌的信息。JWT 令牌只是采用 Base64 编码,因此任何人都可以“解码”令牌以查看令牌中存在哪些声明。https://jwt.io/ 该网站是探索这一点的绝佳资源。JWT 的第三部分是签名,仅使用服务器上存储的密钥进行签名和验证。密钥应该始终保持安全,但理论上您可以简单地检查 JWT 中的 exp(过期)标头,而无需与服务器通信。 (3认同)
  • 我实际上没有遵循任何一个选项。正如 Pi Home Server 所说,这不是很安全,所以最终做了一些服务器端验证。不过还是谢谢你。 (2认同)

dir*_*ory 5

您还可以使用中间件来检查令牌是否已过期。如果令牌即将到期,您甚至可以更新它。例如,您可以执行如下所示的操作;

 export function jwtMiddleware({ dispatch, getState }) {
  return (next) => (action) => {
    switch (action.type) {
      case 'CHECK_AUTH_TOKEN' :
        if (getState().auth && getState().auth.token) {
          var tokenExpiration = jwtDecode(getState().auth.token).exp;
          var tokenExpirationTimeInSeconds = (tokenExpiration - moment(Math.floor(Date.now() / 1000)));
          if (tokenExpiration && tokenExpirationTimeInSeconds < 20) {
            history.push(i18next.t('translation:routes.auth.logout'));
          }
        }
      break;
      case 'UPDATE_AUTH_TOKEN' :
        if (getState().auth && getState().auth.token) {
          var tokenExpiration = jwtDecode(getState().auth.token).exp;
          var tokenExpirationTimeInSeconds = (tokenExpiration - moment(Math.floor(Date.now() / 1000)));
          if (tokenExpiration && tokenExpirationTimeInSeconds < 100 && tokenExpirationTimeInSeconds > 20) {
            if (!getState().auth.fetching) {
              return dispatch(refreshAuthToken(getState().auth));
            }
          }
        }
      break;
      case 'REFRESH_AUTH_TOKEN_FAIL' :
        if (getState().auth && getState().auth.token) {
          return dispatch(removeAuthToken(getState().auth)).then(response => {
            history.push(i18next.t('translation:routes.auth.logout'));
          });
        }
      break;
      }
    return next(action);
  }
}
Run Code Online (Sandbox Code Playgroud)