SysLoginService.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package com.ruoyi.web.service;
  2. import javax.annotation.Resource;
  3. import com.google.common.collect.Maps;
  4. import com.ruoyi.common.core.domain.AjaxResult;
  5. import com.ruoyi.common.core.domain.entity.SysUser;
  6. import com.ruoyi.common.enums.AccessFromType;
  7. import com.ruoyi.common.enums.ErrorCodes;
  8. import com.ruoyi.common.exception.ErrorException;
  9. import com.ruoyi.common.utils.PhoneUtils;
  10. import com.ruoyi.dz.domain.DzCards;
  11. import com.ruoyi.dz.service.IDzCardsService;
  12. import com.ruoyi.dz.service.impl.DzCardsServiceImpl;
  13. import com.ruoyi.framework.web.service.TokenService;
  14. import com.ruoyi.system.service.ShortMessageService;
  15. import com.ruoyi.web.controller.dz.DzCardsController;
  16. import org.apache.commons.collections4.CollectionUtils;
  17. import org.apache.commons.lang3.tuple.Pair;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import org.springframework.security.authentication.AuthenticationManager;
  20. import org.springframework.security.authentication.BadCredentialsException;
  21. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  22. import org.springframework.security.core.Authentication;
  23. import org.springframework.stereotype.Component;
  24. import com.ruoyi.common.constant.CacheConstants;
  25. import com.ruoyi.common.constant.Constants;
  26. import com.ruoyi.common.constant.UserConstants;
  27. import com.ruoyi.common.core.domain.model.LoginUser;
  28. import com.ruoyi.common.core.redis.RedisCache;
  29. import com.ruoyi.common.exception.ServiceException;
  30. import com.ruoyi.common.exception.user.BlackListException;
  31. import com.ruoyi.common.exception.user.CaptchaException;
  32. import com.ruoyi.common.exception.user.CaptchaExpireException;
  33. import com.ruoyi.common.exception.user.UserNotExistsException;
  34. import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
  35. import com.ruoyi.common.utils.DateUtils;
  36. import com.ruoyi.common.utils.MessageUtils;
  37. import com.ruoyi.common.utils.StringUtils;
  38. import com.ruoyi.common.utils.ip.IpUtils;
  39. import com.ruoyi.framework.manager.AsyncManager;
  40. import com.ruoyi.framework.manager.factory.AsyncFactory;
  41. import com.ruoyi.framework.security.context.AuthenticationContextHolder;
  42. import com.ruoyi.system.service.ISysConfigService;
  43. import com.ruoyi.system.service.ISysUserService;
  44. import org.springframework.transaction.annotation.Transactional;
  45. import java.util.Arrays;
  46. import java.util.List;
  47. import java.util.Map;
  48. /**
  49. * 登录校验方法
  50. *
  51. * @author ruoyi
  52. */
  53. @Component
  54. public class SysLoginService
  55. {
  56. @Autowired
  57. private TokenService tokenService;
  58. @Resource
  59. private AuthenticationManager authenticationManager;
  60. @Autowired
  61. private RedisCache redisCache;
  62. @Autowired
  63. private ISysUserService userService;
  64. @Autowired
  65. private ISysConfigService configService;
  66. @Autowired
  67. private ShortMessageService shortMessageService;
  68. @Autowired
  69. private IDzCardsService cardsService;
  70. /**
  71. * 登录验证
  72. * @param mobile
  73. * @param username
  74. * @param password
  75. * @param code
  76. * @param uuid
  77. * @return
  78. */
  79. public AjaxResult login(String mobile, String username, String password, String code, String uuid,String type) {
  80. if (StringUtils.isNotBlank(mobile)) {
  81. //手机验证码登录:{code: "9", uuid: "cc94320c3fce4db5898213b727ac1dc0", mobile: "18774924158", password: "1234"}
  82. if (!shortMessageService.checkCode(mobile, password)) {
  83. AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobile, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
  84. throw new CaptchaException();
  85. }
  86. loginMobilePreCheck(mobile, password);
  87. username = mobile;
  88. password = UserConstants.LOGIN_SMS_PASS;
  89. } else {
  90. // 验证码校验
  91. // 卡密登录json:{code: "0", uuid: "a9eee0b6729f42c4862a57acef8a4bdd", username: "20000025", password: "123456"}
  92. validateCaptcha(username, code, uuid);
  93. // 登录前置校验
  94. loginPreCheck(username, password);
  95. if(AccessFromType.isFrontApp(type)||AccessFromType.isH5(type)||AccessFromType.isWechat(type)){
  96. //卡密登录时,要将卡的密码转换为用户的密码。手机号的密码与卡的密码分开
  97. DzCards card = cardsService.selectDzCardsByCardNo(username);
  98. if (null==card){
  99. return AjaxResult.error("卡不存在");
  100. }
  101. if (!password.trim().equalsIgnoreCase(card.getPassword())){
  102. return AjaxResult.error("密码错误");
  103. }
  104. String dbPwd = userService.selectPasswordByCardId(card.getCardId());
  105. if (StringUtils.isNotEmpty(dbPwd)){
  106. password = dbPwd;
  107. }
  108. }
  109. }
  110. // 用户验证
  111. Authentication authentication = null;
  112. try
  113. {
  114. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
  115. AuthenticationContextHolder.setContext(authenticationToken);
  116. // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
  117. authentication = authenticationManager.authenticate(authenticationToken);
  118. }
  119. catch (Exception e)
  120. {
  121. if (e instanceof BadCredentialsException)
  122. {
  123. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
  124. throw new UserPasswordNotMatchException();
  125. }
  126. else
  127. {
  128. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
  129. if(e.getCause() instanceof ErrorException) {
  130. ErrorException errorException = (ErrorException) e.getCause();
  131. Map map = Maps.newHashMap();
  132. ErrorCodes errorCode = errorException.getErrorCode();
  133. map.put("code", errorCode.getCode());
  134. map.put("message", StringUtils.isNotEmpty(errorException.getMessage()) ? errorException.getMessage() : errorCode.getTitle());
  135. return AjaxResult.success(map);
  136. }
  137. throw new ServiceException(e.getMessage());
  138. }
  139. }
  140. finally
  141. {
  142. AuthenticationContextHolder.clearContext();
  143. }
  144. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
  145. LoginUser loginUser = (LoginUser) authentication.getPrincipal();
  146. recordLoginInfo(loginUser.getUserId());
  147. // 生成token
  148. AjaxResult ajax = AjaxResult.success();
  149. ajax.put(Constants.TOKEN, tokenService.createToken(loginUser));
  150. return ajax;
  151. }
  152. /**
  153. * 内部自动登陆,不记录日志
  154. * @param username
  155. * @param password
  156. * @return
  157. */
  158. public String loginInner(String username, String password) {
  159. // 用户验证
  160. Authentication authentication = null;
  161. try
  162. {
  163. UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
  164. AuthenticationContextHolder.setContext(authenticationToken);
  165. // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
  166. authentication = authenticationManager.authenticate(authenticationToken);
  167. }
  168. catch (Exception e)
  169. {
  170. if (e instanceof BadCredentialsException)
  171. {
  172. throw new UserPasswordNotMatchException();
  173. }
  174. else
  175. {
  176. throw new ServiceException(e.getMessage());
  177. }
  178. }
  179. finally
  180. {
  181. AuthenticationContextHolder.clearContext();
  182. }
  183. LoginUser loginUser = (LoginUser) authentication.getPrincipal();
  184. return tokenService.createToken(loginUser);
  185. }
  186. /**
  187. * 校验验证码
  188. *
  189. * @param username 用户名
  190. * @param code 验证码
  191. * @param uuid 唯一标识
  192. * @return 结果
  193. */
  194. public void validateCaptcha(String username, String code, String uuid)
  195. {
  196. boolean captchaEnabled = configService.selectCaptchaEnabled();
  197. if (captchaEnabled)
  198. {
  199. String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
  200. String captcha = redisCache.getCacheObject(verifyKey);
  201. if (captcha == null)
  202. {
  203. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
  204. throw new CaptchaExpireException();
  205. }
  206. redisCache.deleteObject(verifyKey);
  207. if (!code.equalsIgnoreCase(captcha))
  208. {
  209. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
  210. throw new CaptchaException();
  211. }
  212. }
  213. }
  214. /**
  215. * 登录前置校验
  216. * @param username 用户名
  217. * @param password 用户密码
  218. */
  219. public void loginPreCheck(String username, String password)
  220. {
  221. // 用户名或密码为空 错误
  222. if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
  223. {
  224. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
  225. throw new UserNotExistsException();
  226. }
  227. // 密码如果不在指定范围内 错误
  228. if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
  229. || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
  230. {
  231. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
  232. throw new UserPasswordNotMatchException();
  233. }
  234. // 用户名不在指定范围内 错误
  235. if (username.length() < UserConstants.USERNAME_MIN_LENGTH
  236. || username.length() > UserConstants.USERNAME_MAX_LENGTH)
  237. {
  238. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
  239. throw new UserPasswordNotMatchException();
  240. }
  241. // IP黑名单校验
  242. String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
  243. if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
  244. {
  245. AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
  246. throw new BlackListException();
  247. }
  248. }
  249. public void loginMobilePreCheck(String phoneNumber, String code)
  250. {
  251. // 用户名或密码为空 错误
  252. if (StringUtils.isEmpty(phoneNumber) || StringUtils.isEmpty(code))
  253. {
  254. AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
  255. throw new UserNotExistsException();
  256. }
  257. // 密码如果不在指定范围内 错误
  258. if (code.length() != 4)
  259. {
  260. AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
  261. throw new UserPasswordNotMatchException();
  262. }
  263. // 用户名不在指定范围内 错误
  264. if (!PhoneUtils.isPhoneNumber(phoneNumber))
  265. {
  266. AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
  267. throw new UserPasswordNotMatchException();
  268. }
  269. // IP黑名单校验
  270. String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
  271. if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
  272. {
  273. AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
  274. throw new BlackListException();
  275. }
  276. }
  277. /**
  278. * 记录登录信息
  279. *
  280. * @param userId 用户ID
  281. */
  282. public void recordLoginInfo(Long userId)
  283. {
  284. userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate());
  285. }
  286. }