user.service.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /**
  2. * 用户相关云函数
  3. * 采用 Parse 标准安全实践:
  4. * Parse Server 默认使用 bcrypt 对密码进行安全哈希存储。
  5. * 客户端/服务端传输应使用 HTTPS 保证安全。
  6. */
  7. import crypto from 'crypto';
  8. import fs from 'fs';
  9. console.log('Loading user.service.js...');
  10. function getLoginPrivateKey() {
  11. const keyPath = process.env.LOGIN_RSA_PRIVATE_KEY_FILE;
  12. if (!keyPath) throw new Error('LOGIN_RSA_PRIVATE_KEY_FILE is not configured');
  13. return fs.readFileSync(keyPath, 'utf8');
  14. }
  15. /**
  16. * 解密 RSA 加密的数据
  17. * @param {string} encryptedData Base64 编码的加密字符串
  18. * @returns {string} 解密后的明文字符串
  19. */
  20. function decryptRSA(encryptedData) {
  21. try {
  22. const buffer = Buffer.from(encryptedData, 'base64');
  23. const decrypted = crypto.privateDecrypt(
  24. {
  25. key: getLoginPrivateKey(),
  26. // 根据前端使用的 padding 方式进行调整,常见的是 crypto.constants.RSA_PKCS1_PADDING 或 RSA_PKCS1_OAEP_PADDING
  27. padding: crypto.constants.RSA_PKCS1_PADDING,
  28. },
  29. buffer
  30. );
  31. return decrypted.toString('utf8');
  32. } catch (error) {
  33. console.error('RSA Decryption failed:', error.message);
  34. throw new Error('解密失败');
  35. }
  36. }
  37. /**
  38. * 用户登录云函数
  39. * 调用方式: Parse.Cloud.run('user_login', { username, password })
  40. * 此时传入的 username 和 password 为 RSA 加密并经过 Base64 编码的字符串
  41. */
  42. Parse.Cloud.define('user_login', async request => {
  43. console.log('Cloud Function called: user_login');
  44. const { username: encryptedUsername, password: encryptedPassword } = request.params;
  45. // 参数校验
  46. if (!encryptedUsername || !encryptedPassword) {
  47. throw new Parse.Error(Parse.Error.VALIDATION_ERROR, '用户名和密码不能为空');
  48. }
  49. let username, password;
  50. try {
  51. // 解密账号和密码
  52. username = decryptRSA(encryptedUsername);
  53. password = decryptRSA(encryptedPassword);
  54. } catch (error) {
  55. throw new Parse.Error(Parse.Error.VALIDATION_ERROR, '账号或密码解密失败,请检查加密格式');
  56. }
  57. try {
  58. // 使用 Parse 原生登录,自动验证密码哈希
  59. // logIn 返回的用户对象已包含 sessionToken
  60. const user = await Parse.User.logIn(username, password);
  61. return {
  62. success: true,
  63. user: user.toJSON(),
  64. sessionToken: user.getSessionToken()
  65. };
  66. } catch (error) {
  67. console.error('Login error:', error);
  68. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '用户名或密码错误');
  69. }
  70. });
  71. /**
  72. * 用户注册云函数
  73. * 调用方式: Parse.Cloud.run('user_signup', { username, password, email, ...attributes })
  74. */
  75. Parse.Cloud.define('user_signup', async request => {
  76. if (!request.master) {
  77. throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, '公开注册已禁用');
  78. }
  79. console.log('Cloud Function called: user_signup');
  80. const { username, password, email, ...attributes } = request.params;
  81. // 参数校验
  82. if (!username || !password) {
  83. throw new Parse.Error(Parse.Error.VALIDATION_ERROR, '用户名和密码不能为空');
  84. }
  85. const user = new Parse.User();
  86. user.set('username', username);
  87. user.set('password', password); // Parse 会自动进行 bcrypt 加密存储
  88. if (email) {
  89. user.set('email', email);
  90. }
  91. // 动态设置其他属性
  92. Object.keys(attributes).forEach(key => {
  93. user.set(key, attributes[key]);
  94. });
  95. try {
  96. await user.signUp();
  97. return {
  98. success: true,
  99. userId: user.id,
  100. sessionToken: user.getSessionToken(),
  101. user: user.toJSON()
  102. };
  103. } catch (error) {
  104. console.error('Signup error:', error);
  105. // 转换错误信息(如用户名已存在)
  106. throw new Parse.Error(Parse.Error.USERNAME_TAKEN, error.message);
  107. }
  108. });
  109. export default {};