login.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. const {
  2. findUser
  3. } = require('./account')
  4. const {
  5. userCollection,
  6. LOG_TYPE
  7. } = require('../../common/constants')
  8. const {
  9. ERROR
  10. } = require('../../common/error')
  11. const {
  12. logout
  13. } = require('./logout')
  14. const PasswordUtils = require('./password')
  15. async function realPreLogin (params = {}) {
  16. const {
  17. user
  18. } = params
  19. const appId = this.getUniversalClientInfo().appId
  20. const {
  21. total,
  22. userMatched
  23. } = await findUser({
  24. userQuery: user,
  25. authorizedApp: appId
  26. })
  27. if (userMatched.length === 0) {
  28. if (total > 0) {
  29. throw {
  30. errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP
  31. }
  32. }
  33. throw {
  34. errCode: ERROR.ACCOUNT_NOT_EXISTS
  35. }
  36. } else if (userMatched.length > 1) {
  37. throw {
  38. errCode: ERROR.ACCOUNT_CONFLICT
  39. }
  40. }
  41. const userRecord = userMatched[0]
  42. checkLoginUserRecord(userRecord)
  43. return userRecord
  44. }
  45. async function preLogin (params = {}) {
  46. const {
  47. user
  48. } = params
  49. try {
  50. const user = await realPreLogin.call(this, params)
  51. return user
  52. } catch (error) {
  53. await this.middleware.uniIdLog({
  54. success: false,
  55. data: user,
  56. type: LOG_TYPE.LOGIN
  57. })
  58. throw error
  59. }
  60. }
  61. async function preLoginWithPassword (params = {}) {
  62. const {
  63. user,
  64. password
  65. } = params
  66. try {
  67. const userRecord = await realPreLogin.call(this, params)
  68. const {
  69. passwordErrorLimit,
  70. passwordErrorRetryTime
  71. } = this.config
  72. const {
  73. clientIP
  74. } = this.getUniversalClientInfo()
  75. // 根据ip地址,密码错误次数过多,锁定登录
  76. let loginIPLimit = userRecord.login_ip_limit || []
  77. // 清理无用记录
  78. loginIPLimit = loginIPLimit.filter(item => item.last_error_time > Date.now() - passwordErrorRetryTime * 1000)
  79. let currentIPLimit = loginIPLimit.find(item => item.ip === clientIP)
  80. if (currentIPLimit && currentIPLimit.error_times >= passwordErrorLimit) {
  81. throw {
  82. errCode: ERROR.PASSWORD_ERROR_EXCEED_LIMIT
  83. }
  84. }
  85. const passwordUtils = new PasswordUtils({
  86. userRecord,
  87. clientInfo: this.getUniversalClientInfo(),
  88. passwordSecret: this.config.passwordSecret
  89. })
  90. const {
  91. success: checkPasswordSuccess,
  92. refreshPasswordInfo
  93. } = passwordUtils.checkUserPassword({
  94. password
  95. })
  96. if (!checkPasswordSuccess) {
  97. // 更新用户ip对应的密码错误记录
  98. if (!currentIPLimit) {
  99. currentIPLimit = {
  100. ip: clientIP,
  101. error_times: 1,
  102. last_error_time: Date.now()
  103. }
  104. loginIPLimit.push(currentIPLimit)
  105. } else {
  106. currentIPLimit.error_times++
  107. currentIPLimit.last_error_time = Date.now()
  108. }
  109. await userCollection.doc(userRecord._id).update({
  110. login_ip_limit: loginIPLimit
  111. })
  112. throw {
  113. errCode: ERROR.PASSWORD_ERROR
  114. }
  115. }
  116. const extraData = {}
  117. if (refreshPasswordInfo) {
  118. extraData.password = refreshPasswordInfo.passwordHash
  119. extraData.password_secret_version = refreshPasswordInfo.version
  120. }
  121. const currentIPLimitIndex = loginIPLimit.indexOf(currentIPLimit)
  122. if (currentIPLimitIndex > -1) {
  123. loginIPLimit.splice(currentIPLimitIndex, 1)
  124. }
  125. extraData.login_ip_limit = loginIPLimit
  126. return {
  127. user: userRecord,
  128. extraData
  129. }
  130. } catch (error) {
  131. await this.middleware.uniIdLog({
  132. success: false,
  133. data: user,
  134. type: LOG_TYPE.LOGIN
  135. })
  136. throw error
  137. }
  138. }
  139. function checkLoginUserRecord (user) {
  140. switch (user.status) {
  141. case undefined:
  142. case 0:
  143. break
  144. case 1:
  145. throw {
  146. errCode: ERROR.ACCOUNT_BANNED
  147. }
  148. case 2:
  149. throw {
  150. errCode: ERROR.ACCOUNT_AUDITING
  151. }
  152. case 3:
  153. throw {
  154. errCode: ERROR.ACCOUNT_AUDIT_FAILED
  155. }
  156. case 4:
  157. throw {
  158. errCode: ERROR.ACCOUNT_CLOSED
  159. }
  160. default:
  161. break
  162. }
  163. }
  164. async function thirdPartyLogin (params = {}) {
  165. const {
  166. user
  167. } = params
  168. return {
  169. mobileConfirmed: !!user.mobile_confirmed,
  170. emailConfirmed: !!user.email_confirmed
  171. }
  172. }
  173. async function postLogin (params = {}) {
  174. const {
  175. user,
  176. extraData,
  177. isThirdParty = false
  178. } = params
  179. const {
  180. clientIP
  181. } = this.getUniversalClientInfo()
  182. const uniIdToken = this.getUniversalUniIdToken()
  183. const uid = user._id
  184. const updateData = {
  185. last_login_date: Date.now(),
  186. last_login_ip: clientIP,
  187. ...extraData
  188. }
  189. const createTokenRes = await this.uniIdCommon.createToken({
  190. uid
  191. })
  192. const {
  193. errCode,
  194. token,
  195. tokenExpired
  196. } = createTokenRes
  197. if (errCode) {
  198. throw createTokenRes
  199. }
  200. if (uniIdToken) {
  201. try {
  202. await logout.call(this)
  203. } catch (error) {}
  204. }
  205. await userCollection.doc(uid).update(updateData)
  206. await this.middleware.uniIdLog({
  207. data: {
  208. user_id: uid
  209. },
  210. type: LOG_TYPE.LOGIN
  211. })
  212. return {
  213. errCode: 0,
  214. newToken: {
  215. token,
  216. tokenExpired
  217. },
  218. uid,
  219. ...(
  220. isThirdParty
  221. ? thirdPartyLogin({
  222. user
  223. })
  224. : {}
  225. ),
  226. passwordConfirmed: !!user.password
  227. }
  228. }
  229. module.exports = {
  230. preLogin,
  231. postLogin,
  232. checkLoginUserRecord,
  233. preLoginWithPassword
  234. }