经过一些研究,JWT通常用于登录验证,因为它的紧凑性和易于解析。我已经决定使用JWT了。但是,我的问题是如何将其嵌入我的redux范例中。假设我们有一个注册表单,当用户填写他或她的凭据并单击提交按钮时,这将调用一个动作来创建一个动作来创建一个JWT。现在,此操作进入我的应用程序的后端,我的应用程序的后端调用JWT API?那么这个动作是异步/ rpc调用吗?此外,路由如何发生?我之前使用过react-router,但是使用了样板。我正在从头开始构建这个Web应用程序,所以我对在哪里处理路由有点困惑,我在哪里准确传递这个令牌,这是我第一次从服务器获得的?每次用户发出请求时都会使用令牌吗?客户端每次执行请求时如何知道此令牌,以便让用户进行身份验证?
答案 0 :(得分:3)
我个人使用Redux saga进行异步API调用,并且我将向您显示我用于JWT授权的流程:
LOG_IN
操作LOGGING_IN_PROGRESS
动作以显示e.x.喷丝LOG_IN_SUCCESS
或LOG_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状态代码的响应。然后,在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