javascript - 用于 Django 和 React SPA 的安全 JWT 身份验证
问题描述
我为我的 Django-React SPA 设置了基本的不安全 JWT 身份验证。我们仍处于开发阶段,现在想开始研究身份验证安全性。
我在初始实现中使用了这两个参考:110% Complete JWT Authentication with Django & React - 2020 以及如何正确设置 Axios 拦截器和 React Context?.
这是我的相关代码。目前我正在使用 rest_framework_simpleJWT 库并在必要时打开以使用不同的库。
Django 设置.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
), #
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=10),
'REFRESH_TOKEN_LIFETIME': timedelta(minutes=60),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUTH_HEADER_TYPES': ('JWT',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
}
网址.py
urlpatterns = [
path('', index ),
.
.
path('api/token/obtain/', jwt_views.TokenObtainPairView.as_view(), \
name='token_create'), # override sjwt stock token
path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), \
name='token_refresh'),
url(r'^.*/$', index),
]
axiosApi.js
// axiosApi.js
import axios from 'axios';
import axiosRefreshInstance from './axiosRefreshApi.js';
const csrftoken = getCookie('csrftoken');
const axiosInstance = axios.create({
baseURL: 'http://127.0.0.1:8000/',
timeout: 5000,
headers: {
'Authorization': "JWT " + localStorage.getItem('access_token'),
'Content-Type': 'application/json',
'accept': 'application/json',
'X-CSRFToken': csrftoken
}
});
axiosInstance.interceptors.response.use(
response => response,
error => {
console.log("Axios Interceptor response error happened: ");
console.log("error.config/originalRequest:");
console.log(error.toJSON());
const originalRequest = error.config;
if (error.response.status === 401 &&
error.response.statusText === "Unauthorized") {
if (localStorage.getItem('refresh_token')) {
const refresh_token = localStorage.getItem('refresh_token');
return axiosRefreshInstance
.post('api/token/refresh/', {refresh: refresh_token})
.then((response) => {
localStorage.setItem('access_token', response.data.access);
localStorage.setItem('refresh_token', response.data.refresh);
console.log("new refresh token: ");
console.log(localStorage.getItem('refresh_token'));
axiosInstance.defaults.headers['Authorization'] = "JWT " +
response.data.access;
originalRequest.headers['Authorization'] = "JWT " +
response.data.access;
return axiosInstance(originalRequest);
})
.catch(err => {
// 2. access:invalid and refresh:invalid => redirect to login page
console.log("Refresh token is expired.");
console.log(err.config);
});
} else {
console.log("Refresh token is MISSING");
}
}
return Promise.reject(error);
}
);
export default axiosInstance;
登录.js
import React, { Component } from "react";
import axiosInstance from "../axiosApi.js";
class Login extends Component {
constructor(props) {
super(props);
this.state = {username: "", password: ""};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
//TODO:Don't store login and password in state.
//TODO:Implement the spec for onChange
this.setState({[event.target.name]: event.target.value});
}
handleSubmit(event) {
event.preventDefault();
const url = "api/token/obtain/";
const csrftoken = getCookie('csrftoken');
var i, requestBody={};
for (i=0; i < event.target.length; i++) {
requestBody[event.target.elements[i].name] = event.target.elements[i].value;
}
const response = axiosInstance.post('api/token/obtain/', requestBody)
.then(response => {
axiosInstance.defaults.headers['Authorization'] = "JWT " + response.data.access;
localStorage.setItem('access_token', response.data.access);
localStorage.setItem('refresh_token', response.data.refresh);
})
.catch (error => {
throw error;
})
}
render() {
return (
<div>Login
<form onSubmit={this.handleSubmit}>
<label>
Username:
<input name="username" type="text"
value={this.state.username} onChange={this.handleChange}/>
</label>
<label>
Password:
<input name="password" type="password"
value={this.state.password} onChange={this.handleChange}/>
</label>
<input type="submit" value="Submit"/>
</form>
</div>
)
}
}
export default Login;
上述实现使用本地存储来保存 JWT 令牌,这会将应用程序暴露于 XSS 攻击。一种建议的解决方案是将令牌存储在“httponly”cookie 中。然而,javascript 和 React 无法访问 httponly cookie。那么如何使用它呢?我也不知道如何设置 rest_framework_simpleJWT 将这些 cookie 发送回浏览器。任何帮助将不胜感激。
解决方案
推荐阅读
- excel - 在多列上复制转置循环
- php - PHP 使用 2 个键按对象数组分组
- react-native - 在我的 android 设备上 react native 的开发者菜单中没有开发设置选项
- angular - 如何从firebase检索贷款信息(贷款文件字段)和特定贷款中的项目(贷款文件的项目集合)?
- gulp - 为什么 Gulp 运行没有错误但没有编译 SCSS 脚本?
- amazon-web-services - community.aws.elb_application_lb:如何在侦听器中为新添加的规则分配下一个优先级
- testing - 在最后一次测试移动应用程序之前我应该使用哪个服务器。生产?还是分期?或测试?
- android - 如何在 Retrofit 中使用加密和解密
- python - 如何在 MS Teams 消息扩展中支持基于参数的搜索?
- vb.net - 如何在 VB.Net 中使用字符串键和命名元组作为值声明字典?