diff --git a/backend/apps/authentication/serializers.py b/backend/apps/authentication/serializers.py index c06b563..4880a58 100644 --- a/backend/apps/authentication/serializers.py +++ b/backend/apps/authentication/serializers.py @@ -71,3 +71,17 @@ class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): data['user'] = UserSerializer(self.user).data return data + + +class ChangePasswordSerializer(serializers.Serializer): + """ + 密码修改序列化器 + """ + old_password = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'}) + new_password = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'}) + new_password_confirm = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'}) + + def validate(self, attrs): + if attrs['new_password'] != attrs['new_password_confirm']: + raise serializers.ValidationError({"new_password": "新密码字段不匹配。"}) + return attrs diff --git a/backend/apps/authentication/urls.py b/backend/apps/authentication/urls.py index ab2a46c..b728008 100644 --- a/backend/apps/authentication/urls.py +++ b/backend/apps/authentication/urls.py @@ -1,11 +1,13 @@ from django.urls import path from rest_framework_simplejwt.views import TokenRefreshView -from .views import CustomTokenObtainPairView, UserListView, UserDetailView, current_user +from .views import CustomTokenObtainPairView, UserListView, UserDetailView, current_user, change_password, reset_password urlpatterns = [ path('login/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'), path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('users/', UserListView.as_view(), name='user-list'), path('users//', UserDetailView.as_view(), name='user-detail'), + path('users//reset-password/', reset_password, name='user-reset-password'), path('user/', current_user, name='current-user'), + path('user/change-password/', change_password, name='change-password'), ] diff --git a/backend/apps/authentication/views.py b/backend/apps/authentication/views.py index 795629d..94ecc06 100644 --- a/backend/apps/authentication/views.py +++ b/backend/apps/authentication/views.py @@ -4,8 +4,11 @@ from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from rest_framework_simplejwt.views import TokenObtainPairView from rest_framework.exceptions import PermissionDenied +from django.shortcuts import get_object_or_404 from .models import User -from .serializers import UserSerializer, UserCreateSerializer, CustomTokenObtainPairSerializer +from .serializers import UserSerializer, UserCreateSerializer, CustomTokenObtainPairSerializer, ChangePasswordSerializer + +RESET_PASSWORD = "abc!0000" class CustomTokenObtainPairView(TokenObtainPairView): @@ -76,3 +79,35 @@ def current_user(request): """ serializer = UserSerializer(request.user) return Response(serializer.data) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def change_password(request): + """ + 修改当前用户密码 + """ + serializer = ChangePasswordSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + if not request.user.check_password(serializer.validated_data['old_password']): + return Response({"detail": "原密码不正确"}, status=400) + + request.user.set_password(serializer.validated_data['new_password']) + request.user.save() + return Response({"status": "密码已更新"}) + + +@api_view(['POST']) +@permission_classes([IsAuthenticated]) +def reset_password(request, pk): + """ + 管理员重置用户密码 + """ + if request.user.role != 'admin': + raise PermissionDenied("只有管理员可以重置密码") + + user = get_object_or_404(User, pk=pk) + user.set_password(RESET_PASSWORD) + user.save() + return Response({"status": "密码已重置", "reset_password": RESET_PASSWORD}) diff --git a/frontend/src/api/auth.js b/frontend/src/api/auth.js index 11d22bd..277075c 100644 --- a/frontend/src/api/auth.js +++ b/frontend/src/api/auth.js @@ -29,3 +29,13 @@ export const deleteUser = async (id) => { const { data } = await api.delete(`/auth/users/${id}/`) return data } + +export const changePassword = async (payload) => { + const { data } = await api.post('/auth/user/change-password/', payload) + return data +} + +export const resetUserPassword = async (id) => { + const { data } = await api.post(`/auth/users/${id}/reset-password/`) + return data +} diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue index 16a1c1b..e557bac 100644 --- a/frontend/src/layouts/MainLayout.vue +++ b/frontend/src/layouts/MainLayout.vue @@ -25,6 +25,7 @@
{{ user?.username || '用户' }}
{{ isAdmin ? '管理员' : '普通账号' }}
+ 修改密码 退出 @@ -33,12 +34,32 @@ + + + + + + + + + + + + + + +