如何使用react-redux实现登录身份验证?

时间:2016-12-25 17:27:55

标签: reactjs react-router jwt

经过一些研究,JWT通常用于登录验证,因为它的紧凑性和易于解析。我已经决定使用JWT了。但是,我的问题是如何将其嵌入我的redux范例中。假设我们有一个注册表单,当用户填写他或她的凭据并单击提交按钮时,这将调用一个动作来创建一个动作来创建一个JWT。现在,此操作进入我的应用程序的后端,我的应用程序的后端调用JWT API?那么这个动作是异步/ rpc调用吗?此外,路由如何发生?我之前使用过react-router,但是使用了样板。我正在从头开始构建这个Web应用程序,所以我对在哪里处理路由有点困惑,我在哪里准确传递这个令牌,这是我第一次从服务器获得的?每次用户发出请求时都会使用令牌吗?客户端每次执行请求时如何知道此令牌,以便让用户进行身份验证?

3 个答案:

答案 0 :(得分:3)

我个人使用Redux saga进行异步API调用,并且我将向您显示我用于JWT授权的流程:

  1. 使用用户名和密码发送LOG_IN操作
  2. 在您的传奇中您发送LOGGING_IN_PROGRESS动作以显示e.x.喷丝
  3. 进行API调用
  4. 检索到的令牌保存e.x.在localstorage
  5. 发送LOG_IN_SUCCESSLOG_IN_FAILED以告知应用您的回复
  6. 现在,我总是使用一个单独的函数来处理我的所有请求,如下所示:

    import request from 'axios';
    import {get} from './persist'; // function to get something from localstorage
    
    export const GET = 'GET';
    export const POST = 'POST';
    export const PUT = 'PUT';
    export const DELETE = 'DELETE';
    
    const service = (requestType, url, data = {}, config = {}) => {
        request.defaults.headers.common.Authorization = get('token') ? `Token ${get('token')}` : '';
        switch (requestType) {
            case GET: {
                return request.get(url, data, config);
            }
            case POST: {
                return request.post(url, data, config);
            }
            case PUT: {
                return request.put(url, data, config);
            }
            case DELETE: {
                return request.delete(url, data, config);
            }
            default: {
                throw new TypeError('No valid request type provided');
            }
        }
    };
    export default service;
    

    感谢这项服务,我可以轻松地为我的应用程序设置每个API调用的请求数据(也可以设置语言环境)。

    最有趣的部分应该是这一行:

    request.defaults.headers.common.Authorization = get('token') ? `Token ${get('token')}` : '';`
    

    它在每个请求上设置JWT令牌或将该字段留空。

    如果令牌已过期或无效,您的后端API应在任何API调用上返回带有401状态代码的响应。然后,在saga catch块中,您可以以任何方式处理此错误。

答案 1 :(得分:3)

当用户提交其凭据(电子邮件/密码)时,您的后端首次对此进行身份验证,并且只有这一次后端才会使用这些凭据。在身份验证时,您的后端将创建一个包含一些用户信息的JWT,通常只是用户ID。有很多JWT Libraries甚至jwt-decode for javascript来执行此操作。后端将响应此JWT,前端将为每个后续请求保存它(即localStorage.setItem('authToken', jwt))。

用户将在Authorization键下的请求标头中发送带有JWT的请求。类似的东西:

function buildHeaders() {
    const token = localStorage.getItem('authToken')

    return {
      "Accept": "application/json",
      "Content-Type": "application/json"
      "Authorization": `${token}`
    }
}

您的后端现在将解码并验证JWT。如果它是有效的JWT,则请求继续,否则它被拒绝。

现在使用React-Router,您可以使用onEnter功能保护经过身份验证的路由。您提供的功能执行任何必要的检查(检查JWT的localStorage以及当前用户)。通常我已经这样做了:

const _ensureAuthenticated = (nextState, replace) => {
    const { dispatch } = store
    const { session } = store.getState()
    const { currentUser } = session
    const token = localStorage.getItem("phoenixAuthToken")
    if (!currentUser && token) {     // if no user but token exist, still verify
      dispatch(Actions.currentUser())
    } else if (!token) {             // if no token at all redirect to sign-in
      replace({
        pathname: "/sign-in",
        state: { nextPathname: nextState.location.pathname}
      })
    }
  }

您可以在任何路线中使用此功能:

<Route path="/secret-path" onEnter={_ensureAuthenticated} />

有关JWT和jwt.io的更多信息,请查看react-router auth-flow example,了解有关使用react-router进行身份验证的更多信息。

答案 2 :(得分:0)

我最近不得不使用React&amp; amp; Redux也是如此。

以下是一些实现登录功能和设置http auth标头的主要代码段。

这是我的登录异步操作创建者功能:

function login(username, password) {
    return dispatch => {
        dispatch(request({ username }));

        userService.login(username, password)
            .then(
                user => { 
                    dispatch(success(user));
                    history.push('/');
                },
                error => {
                    dispatch(failure(error));
                    dispatch(alertActions.error(error));
                }
            );
    };

    function request(user) { return { type: userConstants.LOGIN_REQUEST, user } }
    function success(user) { return { type: userConstants.LOGIN_SUCCESS, user } }
    function failure(error) { return { type: userConstants.LOGIN_FAILURE, error } }
}

这是处理api呼叫的用户服务的登录功能

function login(username, password) {
    const requestOptions = {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
    };

    return fetch('/users/authenticate', requestOptions)
        .then(response => {
            if (!response.ok) { 
                return Promise.reject(response.statusText);
            }

            return response.json();
        })
        .then(user => {
            // login successful if there's a jwt token in the response
            if (user && user.token) {
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('user', JSON.stringify(user));
            }

            return user;
        });
}

这是一个帮助函数,用于设置http请求的授权标题

export function authHeader() {
    // return authorization header with jwt token
    let user = JSON.parse(localStorage.getItem('user'));

    if (user && user.token) {
        return { 'Authorization': 'Bearer ' + user.token };
    } else {
        return {};
    }
}

有关完整示例和工作演示,您可以转到this blog post