You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于React、Redux和Django自定义模型构建认证系统(不使用默认认证)

可行方案:基于React、Redux和Django自定义模型的认证系统

完全可以实现这个需求,下面分Django后端和React+Redux前端两部分给出具体实现步骤:


一、Django后端实现

1. 完善自定义用户模型

你的基础模型已经具备核心字段,补充密码加密和验证的方法,复用Django内置的安全哈希逻辑:

from django.db import models
from django.contrib.auth.hashers import check_password, make_password

class CustomUser(models.Model):
    username = models.CharField(max_length=150, unique=True)
    email = models.EmailField(unique=True)
    encrypted_password = models.CharField(max_length=255)

    # 设置加密密码
    def set_password(self, raw_password):
        self.encrypted_password = make_password(raw_password)
        self.save()

    # 验证密码
    def check_password(self, raw_password):
        return check_password(raw_password, self.encrypted_password)

2. 编写认证API视图

用Django REST Framework(DRF)实现注册、登录接口,结合JWT生成认证令牌(推荐用djangorestframework-simplejwt库):

from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import CustomUser
from rest_framework_simplejwt.tokens import RefreshToken

# 用户注册接口
class RegisterView(APIView):
    def post(self, request):
        username = request.data.get('username')
        email = request.data.get('email')
        password = request.data.get('password')
        
        if CustomUser.objects.filter(email=email).exists():
            return Response({'error': '邮箱已被注册'}, status=status.HTTP_400_BAD_REQUEST)
        
        user = CustomUser.objects.create(username=username, email=email)
        user.set_password(password)
        return Response({'message': '注册成功'}, status=status.HTTP_201_CREATED)

# 用户登录接口
class LoginView(APIView):
    def post(self, request):
        email = request.data.get('email')
        password = request.data.get('password')
        
        try:
            user = CustomUser.objects.get(email=email)
        except CustomUser.DoesNotExist:
            return Response({'error': '账号或密码错误'}, status=status.HTTP_401_UNAUTHORIZED)
        
        if not user.check_password(password):
            return Response({'error': '账号或密码错误'}, status=status.HTTP_401_UNAUTHORIZED)
        
        # 生成JWT令牌
        refresh = RefreshToken.for_user(user)
        return Response({
            'refresh': str(refresh),
            'access': str(refresh.access_token),
            'user': {
                'username': user.username,
                'email': user.email
            }
        }, status=status.HTTP_200_OK)

3. 配置路由与权限

在urls.py中注册API路由,并配置JWT认证保护需要权限的接口:

from django.urls import path
from .views import RegisterView, LoginView
from rest_framework_simplejwt.views import (
    TokenRefreshView,
)

urlpatterns = [
    path('api/register/', RegisterView.as_view(), name='register'),
    path('api/login/', LoginView.as_view(), name='login'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 其他需要认证的接口配置JWT认证类
]

二、React+Redux前端实现

1. 配置Redux认证状态管理

用Redux Toolkit创建认证slice,处理登录、注册、登出的异步逻辑和状态:

// src/features/auth/authSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from '../../utils/axios';

// 登录异步Action
export const login = createAsyncThunk(
  'auth/login',
  async (credentials, { rejectWithValue }) => {
    try {
      const res = await axios.post('/api/login/', credentials);
      // 持久化存储令牌和用户信息
      localStorage.setItem('accessToken', res.data.access);
      localStorage.setItem('userInfo', JSON.stringify(res.data.user));
      return res.data;
    } catch (err) {
      return rejectWithValue(err.response.data);
    }
  }
);

// 注册异步Action
export const register = createAsyncThunk(
  'auth/register',
  async (userData, { rejectWithValue }) => {
    try {
      await axios.post('/api/register/', userData);
      return { message: '注册成功' };
    } catch (err) {
      return rejectWithValue(err.response.data);
    }
  }
);

// 初始化状态:从本地存储读取
const initialState = {
  user: JSON.parse(localStorage.getItem('userInfo')) || null,
  token: localStorage.getItem('accessToken') || null,
  loading: false,
  error: null
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    logout: (state) => {
      state.user = null;
      state.token = null;
      localStorage.removeItem('accessToken');
      localStorage.removeItem('userInfo');
    }
  },
  extraReducers: (builder) => {
    // 登录状态处理
    builder
      .addCase(login.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(login.fulfilled, (state, action) => {
        state.loading = false;
        state.user = action.payload.user;
        state.token = action.payload.access;
      })
      .addCase(login.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload.error;
      })
      // 注册状态处理
      .addCase(register.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(register.fulfilled, (state) => {
        state.loading = false;
      })
      .addCase(register.rejected, (state, action) => {
        state.loading = false;
        state.error = action.payload.error;
      });
  }
});

export const { logout } = authSlice.actions;
export default authSlice.reducer;

2. 配置Redux Store

将认证slice加入全局store:

// src/app/store.js
import { configureStore } from '@reduxjs/toolkit';
import authReducer from '../features/auth/authSlice';

export const store = configureStore({
  reducer: {
    auth: authReducer
  }
});

3. 编写登录/注册组件

使用Redux的useDispatch和useSelector处理状态交互:

// src/components/Login.js
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { login } from '../features/auth/authSlice';

const Login = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const dispatch = useDispatch();
  const { loading, error } = useSelector(state => state.auth);

  const handleSubmit = (e) => {
    e.preventDefault();
    dispatch(login({ email, password }));
  };

  return (
    <form onSubmit={handleSubmit} className="login-form">
      <div className="form-group">
        <label>邮箱</label>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
      </div>
      <div className="form-group">
        <label>密码</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
      </div>
      {error && <p className="error-message">{error}</p>}
      <button type="submit" disabled={loading}>
        {loading ? '登录中...' : '登录'}
      </button>
    </form>
  );
};

export default Login;

4. 配置请求拦截器

自动为所有请求携带JWT令牌:

// src/utils/axios.js
import axios from 'axios';

const axiosInstance = axios.create({
  baseURL: 'http://localhost:8000' // 替换为你的Django API地址
});

// 请求拦截:添加Authorization头
axiosInstance.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('accessToken');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

export default axiosInstance;

5. 路由保护

创建高阶组件,拦截未登录用户访问受保护路由:

// src/components/ProtectedRoute.js
import { Navigate } from 'react-router-dom';
import { useSelector } from 'react-redux';

const ProtectedRoute = ({ children }) => {
  const { user } = useSelector(state => state.auth);
  
  if (!user) {
    return <Navigate to="/login" replace />;
  }
  
  return children;
};

export default ProtectedRoute;

关键注意事项

  • Django端务必使用内置的密码哈希方法,不要自行实现加密逻辑,避免安全漏洞
  • JWT令牌建议存储在localStorage,但要注意XSS风险;若追求更高安全性,可改用HttpOnly Cookie(需后端配合配置)
  • Redux异步逻辑统一用createAsyncThunk处理,避免回调嵌套
  • 所有需要权限的Django API视图,需配置permission_classes = [IsAuthenticated]和authentication_classes = [JWTAuthentication]

内容的提问来源于stack exchange,提问作者Mohammad Touhidul Islam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 22:45:52