如何使用React-Redux实现登录身份验证



经过一些研究,JWT通常用于登录身份验证,因为其紧凑的性质和轻松解析。我已经解决了使用JWT。但是,我的问题是如何将其嵌入我的redux范式中。假设我们有一个注册表格,当用户填写其凭据并单击提交按钮时,这将调用一个操作以创建一个措施创建JWT。现在,此操作转到了我的应用程序的后端,而我的应用程序的后端称为JWT API?因此,此操作是异步/RPC调用吗?另外,路由如何准确地发生?我以前曾使用过React-Router,但是使用了样板。我正在从头开始构建此Web应用程序,因此我对在哪里处理路由感到有些困惑,并且我第一次从服务器中获得的代币将在哪里传递?每次用户执行请求时都使用令牌吗?客户每次执行请求时如何知道这个令牌,以使用户保持身份验证?

用户提交凭据(电子邮件/密码)时,您的后端是第一次验证,只有这次,后端才能使用这些凭据。关于身份验证,您的后端将创建一个带有一些用户信息的JWT,通常只是用户ID。JavaScript有很多JWT库,甚至还有JWT库。后端将使用此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的局部设备以及当前用户)。通常我已经这样做了:

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.IO以获取有关JWT和React-Router auth-Flow示例的更多信息,以获取有关使用React-Router的身份验证的更多信息。

我亲自使用redux传奇呼叫,我将向您展示我在JWT授权中使用的流程:

  1. 调度LOG_IN用户名和密码的操作
  2. 在您的传奇中,您要派遣LOGGING_IN_PROGRESS行动显示E.X.旋转器
  3. 做API调用
  4. 检索令牌保存E.X.在localstorage
  5. 调度LOG_IN_SUCCESSLOG_IN_FAILED通知应用程序您得到了什么响应

现在,我始终使用一个单独的功能来处理我的所有请求,看起来像这样:

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状态代码的响应。然后,在传奇catch块中,您可以以任何方式处理此错误。

我最近必须实现注册并使用React&amp;Redux也是如此。

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

这是我的登录Async Action Creator 函数:

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

对于完整的示例和工作演示,您可以转到此博客文章

最新更新