登陆注册与忘记密码前后端与jwt配置

Change-Id: Ide4ca3ea34609fdb33ea027e28169852fa41784a
diff --git a/rhj/frontend/src/pages/LoginPage/LoginPage.js b/rhj/frontend/src/pages/LoginPage/LoginPage.js
new file mode 100644
index 0000000..c315b7d
--- /dev/null
+++ b/rhj/frontend/src/pages/LoginPage/LoginPage.js
@@ -0,0 +1,380 @@
+import React, { useState, useEffect } from 'react';
+import { Link } from 'react-router-dom';
+import { Input, Checkbox, Modal, Alert } from 'antd';
+import { MailOutlined, LockOutlined, ExclamationCircleOutlined, CheckCircleOutlined } from '@ant-design/icons';
+import { 
+  getRememberedLoginInfo, 
+  saveRememberedLoginInfo, 
+  saveAuthInfo, 
+  isLoggedIn 
+} from '../../utils/auth';
+import { hashPassword } from '../../utils/crypto';
+import './LoginPage.css';
+
+const baseURL = 'http://10.126.59.25:8082';
+
+const LoginPage = () => {
+  const [formData, setFormData] = useState({
+    email: '',
+    password: ''
+  });
+
+  const [rememberMe, setRememberMe] = useState(false);
+  const [isLoading, setIsLoading] = useState(false);
+  const [errors, setErrors] = useState({
+    email: '',
+    password: ''
+  });
+  const [errorModal, setErrorModal] = useState({
+    visible: false,
+    title: '',
+    content: ''
+  });
+  const [successAlert, setSuccessAlert] = useState({
+    visible: false,
+    message: ''
+  });
+
+  // 显示错误弹窗
+  const showErrorModal = (title, content) => {
+    setErrorModal({
+      visible: true,
+      title: title,
+      content: content
+    });
+  };
+
+  // 关闭错误弹窗
+  const closeErrorModal = () => {
+    setErrorModal({
+      visible: false,
+      title: '',
+      content: ''
+    });
+  };
+
+  // 显示成功提示
+  const showSuccessAlert = (message) => {
+    setSuccessAlert({
+      visible: true,
+      message: message
+    });
+    
+    // 3秒后自动隐藏
+    setTimeout(() => {
+      setSuccessAlert({
+        visible: false,
+        message: ''
+      });
+    }, 3000);
+  };
+
+  // 页面加载时检查是否有记住的登录信息
+  useEffect(() => {
+    // 检查是否已经登录
+    if (isLoggedIn()) {
+      // 如果已经有token,可以选择直接跳转到主页面
+      // window.location.href = '/test-dashboard';
+      console.log('用户已登录');
+    }
+
+    // 获取记住的登录信息
+    const rememberedInfo = getRememberedLoginInfo();
+    if (rememberedInfo.rememberMe && rememberedInfo.email) {
+      setFormData({
+        email: rememberedInfo.email,
+        password: rememberedInfo.password
+      });
+      setRememberMe(true);
+    }
+  }, []);
+
+  const handleEmailChange = (e) => {
+    const value = e.target.value;
+    setFormData(prev => ({
+      ...prev,
+      email: value
+    }));
+    
+    // 清除邮箱错误提示
+    if (errors.email) {
+      setErrors(prev => ({
+        ...prev,
+        email: ''
+      }));
+    }
+  };
+
+  const handlePasswordChange = (e) => {
+    const value = e.target.value;
+    setFormData(prev => ({
+      ...prev,
+      password: value
+    }));
+    
+    // 清除密码错误提示
+    if (errors.password) {
+      setErrors(prev => ({
+        ...prev,
+        password: ''
+      }));
+    }
+  };
+
+  const handleRememberMeChange = (e) => {
+    const checked = e.target.checked;
+    setRememberMe(checked);
+    
+    // 如果取消记住我,清除已保存的登录信息
+    if (!checked) {
+      saveRememberedLoginInfo('', '', false);
+    }
+  };
+
+  const validateForm = () => {
+    const newErrors = {
+      email: '',
+      password: ''
+    };
+    
+    let hasError = false;
+    
+    // 验证邮箱
+    if (!formData.email || typeof formData.email !== 'string' || !formData.email.trim()) {
+      newErrors.email = '请输入邮箱地址';
+      hasError = true;
+    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
+      newErrors.email = '请输入有效的邮箱地址';
+      hasError = true;
+    }
+    
+    // 验证密码
+    if (!formData.password || typeof formData.password !== 'string' || !formData.password.trim()) {
+      newErrors.password = '请输入密码';
+      hasError = true;
+    } else if (formData.password.length < 6) {
+      newErrors.password = '密码长度至少6位';
+      hasError = true;
+    }
+    
+    setErrors(newErrors);
+    return !hasError;
+  };
+
+  const handleSubmit = async (e) => {
+    e.preventDefault();
+    
+    // 验证表单
+    if (!validateForm()) {
+      return;
+    }
+    
+    setIsLoading(true);
+    
+    try {
+      // 发送登录请求到后端
+      const response = await fetch(baseURL + '/login', {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          email: formData.email, // 后端支持邮箱登录
+          password: hashPassword(formData.password) // 前端加密密码
+        })
+      });
+      
+      const result = await response.json();
+      
+      if (result.success) {
+        // 显示成功提示
+        showSuccessAlert('登录成功!正在跳转...');
+        
+        // 保存认证信息
+        saveAuthInfo(result.token, result.user, rememberMe);
+        
+        // 保存或清除记住的登录信息
+        saveRememberedLoginInfo(formData.email, formData.password, rememberMe);
+        
+        // 延迟跳转,让用户看到成功提示
+        setTimeout(() => {
+          window.location.href = '/test-dashboard';
+        }, 1500);
+      } else {
+        // 登录失败,显示错误信息
+        let errorTitle = '登录失败';
+        let errorContent = result.message || '登录失败,请检查您的邮箱和密码';
+        
+        // 根据错误类型提供更详细的信息
+        if (result.message) {
+          if (result.message.includes('邮箱') || result.message.includes('email')) {
+            errorTitle = '邮箱验证失败';
+            errorContent = '您输入的邮箱地址不存在或格式不正确,请检查后重试。';
+          } else if (result.message.includes('密码') || result.message.includes('password')) {
+            errorTitle = '密码验证失败';
+            errorContent = '您输入的密码不正确,请检查后重试。如果忘记密码,请点击"忘记密码"进行重置。';
+          } else if (result.message.includes('用户不存在')) {
+            errorTitle = '用户不存在';
+            errorContent = '该邮箱尚未注册,请先注册账户或检查邮箱地址是否正确。';
+          } else if (result.message.includes('账户被锁定') || result.message.includes('locked')) {
+            errorTitle = '账户被锁定';
+            errorContent = '您的账户因安全原因被暂时锁定,请联系客服或稍后重试。';
+          }
+        }
+        
+        showErrorModal(errorTitle, errorContent);
+      }
+    } catch (error) {
+      console.error('登录请求失败:', error);
+      
+      // 根据错误类型显示不同的错误信息
+      if (error.name === 'TypeError' && error.message.includes('fetch')) {
+        showErrorModal('网络连接失败', '无法连接到服务器,请检查您的网络连接后重试。如果问题持续存在,请联系客服。');
+      } else if (error.name === 'AbortError') {
+        showErrorModal('请求超时', '请求超时,请检查网络连接后重试。');
+      } else {
+        showErrorModal('登录失败', '网络连接失败,请检查网络或稍后重试。如果问题持续存在,请联系客服。');
+      }
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  return (
+    <div className="login-container">
+      <div className="login-background"></div>
+      
+      {isLoading && (
+        <div className="loading-overlay">
+          <div className="loading-content">
+            <div className="loading-spinner-large"></div>
+            <p className="loading-text">正在登录...</p>
+          </div>
+        </div>
+      )}
+      
+      <div className="login-content">
+        <div className="login-card">
+          {/* 成功提示 */}
+          {successAlert.visible && (
+            <div style={{ marginBottom: '16px' }}>
+              <Alert
+                message={successAlert.message}
+                type="success"
+                icon={<CheckCircleOutlined />}
+                showIcon
+                closable
+                onClose={() => setSuccessAlert({ visible: false, message: '' })}
+                style={{
+                  borderRadius: '8px',
+                  border: '1px solid #b7eb8f',
+                  backgroundColor: '#f6ffed'
+                }}
+              />
+            </div>
+          )}
+          
+          <div className="login-header">
+            <h1 className="login-title">欢迎来到小红书</h1>
+            <p className="login-subtitle">标记我的生活</p>
+          </div>
+
+          <form className="login-form" onSubmit={handleSubmit}>
+            <div className="form-group">
+              <Input
+                type="email"
+                id="email"
+                name="email"
+                className={`form-input ${errors.email ? 'input-error' : ''}`}
+                placeholder="请输入您的邮箱"
+                value={formData.email}
+                onChange={handleEmailChange}
+                prefix={<MailOutlined />}
+                size="large"
+                title=""
+                status={errors.email ? 'error' : ''}
+              />
+              {errors.email && (
+                <div className="error-message">
+                  {errors.email}
+                </div>
+              )}
+            </div>
+
+            <div className="form-group">
+              <Input.Password
+                id="password"
+                name="password"
+                className={`form-input ${errors.password ? 'input-error' : ''}`}
+                placeholder="请输入您的密码"
+                value={formData.password}
+                onChange={handlePasswordChange}
+                prefix={<LockOutlined />}
+                size="large"
+                title=""
+                status={errors.password ? 'error' : ''}
+              />
+              {errors.password && (
+                <div className="error-message">
+                  {errors.password}
+                </div>
+              )}
+            </div>
+
+            <div className="form-options">
+              <Checkbox 
+                checked={rememberMe}
+                onChange={handleRememberMeChange}
+              >
+                记住我
+              </Checkbox>
+              <Link to="/forgot-password" className="forgot-password">忘记密码?</Link>
+            </div>
+
+            <button
+              type="submit"
+              className={`login-button ${isLoading ? 'loading' : ''}`}
+              disabled={isLoading}
+            >
+              {isLoading ? (
+                <>
+                  <div className="loading-spinner"></div>
+                  登录中...
+                </>
+              ) : (
+                '登录'
+              )}
+            </button>
+          </form>
+
+          <div className="signup-link">
+            <p>还没有账户? <Link to="/register">立即注册</Link></p>
+          </div>
+        </div>
+      </div>
+
+      {/* 错误弹窗 */}
+      <Modal
+        title={
+          <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
+            <ExclamationCircleOutlined style={{ color: '#ff4d4f', fontSize: '18px' }} />
+            {errorModal.title}
+          </div>
+        }
+        open={errorModal.visible}
+        onOk={closeErrorModal}
+        onCancel={closeErrorModal}
+        okText="我知道了"
+        cancelButtonProps={{ style: { display: 'none' } }}
+        centered
+        className="error-modal"
+      >
+        <div style={{ padding: '16px 0', fontSize: '14px', lineHeight: '1.6' }}>
+          {errorModal.content}
+        </div>
+      </Modal>
+    </div>
+  );
+};
+
+export default LoginPage;