经过一些研究,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授权中使用的流程:
- 调度
LOG_IN
用户名和密码的操作 - 在您的传奇中,您要派遣
LOGGING_IN_PROGRESS
行动显示E.X.旋转器 - 做API调用
- 检索令牌保存E.X.在localstorage
- 调度
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状态代码的响应。然后,在传奇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 {};
}
}
对于完整的示例和工作演示,您可以转到此博客文章