| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- package com.ruoyi.web.service;
- import javax.annotation.Resource;
- import com.google.common.collect.Lists;
- import com.google.common.collect.Maps;
- import com.ruoyi.common.core.domain.AjaxResult;
- import com.ruoyi.common.core.domain.entity.SysUser;
- import com.ruoyi.common.enums.AccessFromType;
- import com.ruoyi.common.enums.ErrorCodes;
- import com.ruoyi.common.exception.ErrorException;
- import com.ruoyi.common.utils.PhoneUtils;
- import com.ruoyi.dz.domain.DzCards;
- import com.ruoyi.dz.service.IDzCardsService;
- import com.ruoyi.dz.service.impl.DzCardsServiceImpl;
- import com.ruoyi.framework.web.service.TokenService;
- import com.ruoyi.system.domain.ZuserToken;
- import com.ruoyi.system.service.IZuserTokenService;
- import com.ruoyi.system.service.ShortMessageService;
- import com.ruoyi.web.controller.dz.DzCardsController;
- import org.apache.commons.collections4.CollectionUtils;
- import org.apache.commons.lang3.tuple.Pair;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.authentication.BadCredentialsException;
- import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
- import org.springframework.security.core.Authentication;
- import org.springframework.stereotype.Component;
- import com.ruoyi.common.constant.CacheConstants;
- import com.ruoyi.common.constant.Constants;
- import com.ruoyi.common.constant.UserConstants;
- import com.ruoyi.common.core.domain.model.LoginUser;
- import com.ruoyi.common.core.redis.RedisCache;
- import com.ruoyi.common.exception.ServiceException;
- import com.ruoyi.common.exception.user.BlackListException;
- import com.ruoyi.common.exception.user.CaptchaException;
- import com.ruoyi.common.exception.user.CaptchaExpireException;
- import com.ruoyi.common.exception.user.UserNotExistsException;
- import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
- import com.ruoyi.common.utils.DateUtils;
- import com.ruoyi.common.utils.MessageUtils;
- import com.ruoyi.common.utils.StringUtils;
- import com.ruoyi.common.utils.ip.IpUtils;
- import com.ruoyi.framework.manager.AsyncManager;
- import com.ruoyi.framework.manager.factory.AsyncFactory;
- import com.ruoyi.framework.security.context.AuthenticationContextHolder;
- import com.ruoyi.system.service.ISysConfigService;
- import com.ruoyi.system.service.ISysUserService;
- import org.springframework.transaction.annotation.Transactional;
- import java.util.*;
- import java.util.stream.Collectors;
- /**
- * 登录校验方法
- *
- * @author ruoyi
- */
- @Component
- public class SysLoginService
- {
- @Autowired
- private TokenService tokenService;
- @Resource
- private AuthenticationManager authenticationManager;
- @Autowired
- private RedisCache redisCache;
- @Autowired
- private ISysUserService userService;
- @Autowired
- private ISysConfigService configService;
- @Autowired
- private ShortMessageService shortMessageService;
- @Autowired
- private IDzCardsService cardsService;
- @Autowired
- private IZuserTokenService zuserTokenService;
- /**
- * 登录验证
- * @param mobile
- * @param username
- * @param password
- * @param code
- * @param uuid
- * @return
- */
- public AjaxResult login(String mobile, String username, String password, String code, String uuid,String type) {
- if (StringUtils.isNotBlank(mobile)) {
- //手机验证码登录:{code: "9", uuid: "cc94320c3fce4db5898213b727ac1dc0", mobile: "18774924158", password: "1234"}
- if (!shortMessageService.checkCode(mobile, password)) {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(mobile, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
- throw new CaptchaException();
- }
- loginMobilePreCheck(mobile, password);
- username = mobile;
- password = UserConstants.LOGIN_SMS_PASS;
- } else {
- // 验证码校验
- // 卡密登录json:{code: "0", uuid: "a9eee0b6729f42c4862a57acef8a4bdd", username: "20000025", password: "123456"}
- validateCaptcha(username, code, uuid);
- // 登录前置校验
- loginPreCheck(username, password);
- if(AccessFromType.isFrontApp(type)||AccessFromType.isH5(type)||AccessFromType.isWechat(type)){
- //卡密登录时,要将卡的密码转换为用户的密码。手机号的密码与卡的密码分开
- DzCards card = cardsService.selectDzCardsByCardNo(username);
- if (null==card){
- if(username.length()>6&&username.length()<8){
- //老师账号,直接跳过
- }else if(username.length()>=8){
- //卡用户
- return AjaxResult.error("卡不存在");
- }
- }
- if(null!=card&&username.length()>=8){
- //卡用户
- if (!password.trim().equalsIgnoreCase(card.getPassword())){
- return AjaxResult.error("密码错误");
- }
- String dbPwd = userService.selectPasswordByCardId(card.getCardId());
- if (StringUtils.isNotEmpty(dbPwd)){
- password = dbPwd;
- }
- }
- }
- }
- // 用户验证
- Authentication authentication = null;
- try
- {
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
- AuthenticationContextHolder.setContext(authenticationToken);
- // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
- authentication = authenticationManager.authenticate(authenticationToken);
- }
- catch (Exception e)
- {
- if (e instanceof BadCredentialsException)
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
- throw new UserPasswordNotMatchException();
- }
- else
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
- if(e.getCause() instanceof ErrorException) {
- ErrorException errorException = (ErrorException) e.getCause();
- Map map = Maps.newHashMap();
- ErrorCodes errorCode = errorException.getErrorCode();
- map.put("code", errorCode.getCode());
- map.put("message", StringUtils.isNotEmpty(errorException.getMessage()) ? errorException.getMessage() : errorCode.getTitle());
- return AjaxResult.success(map);
- }
- throw new ServiceException(e.getMessage());
- }
- }
- finally
- {
- AuthenticationContextHolder.clearContext();
- }
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
- LoginUser loginUser = (LoginUser) authentication.getPrincipal();
- Long userId = loginUser.getUserId();
- recordLoginInfo(userId);
- // 生成token
- AjaxResult ajax = AjaxResult.success();
- //单点登录
- resetTokensByUserIds(Arrays.asList(userId),true);
- // 生成token
- String token = tokenService.createToken(loginUser);
- ZuserToken userToken = new ZuserToken();
- userToken.setUserName(StringUtils.isEmpty(type)?username:(type+":"+username));
- userToken.setUserId(userId);
- userToken.setToken(token);
- userToken.setUpdateTime(new Date());
- zuserTokenService.insertZuserToken(userToken);
- ajax.put(Constants.TOKEN, token);
- return ajax;
- }
- /**
- * 此处不使用resetTokens是为了与志愿保持一致
- * 重置指定用户的登陆信息,强制重新登陆系统
- * @param userIds
- * @param isSso:适配志愿中锁分时也要清空用户token信息,此时设置为false,直接清空用户的token,设置为true就表示走单点登录的逻辑
- * @param isSetSso:后台配置的单点登录配置,设置为true时启用单点登录,为false时不启用单点登录
- * @return
- */
- public AjaxResult resetTokensByUserIds(List<Long> userIds,Boolean isSso) {
- if (isSso){
- String isSetSso = configService.selectConfigByKey( "is.set.sso");
- if (StringUtils.isBlank(isSetSso)||!Boolean.valueOf(isSetSso)){
- //参数配置的单点登录为false,此时不设置单点登录(将userids设置为空就不走清空流程),不需要清空用户已登录的token
- userIds = new ArrayList<>();
- }
- Set<String> excludeUserIdSet=new HashSet<>();
- String ssoWhiteListUserName = configService.selectConfigByKey("sso.white.list");
- if(StringUtils.isNotBlank(ssoWhiteListUserName)){
- Arrays.asList(ssoWhiteListUserName.split(",")).forEach(username->{
- excludeUserIdSet.add(username);
- });
- }
- if (CollectionUtils.isNotEmpty(excludeUserIdSet)){
- List<Long> excludeUserIds = userService.selectUsers(null,null,excludeUserIdSet).stream().map(SysUser::getUserId).collect(Collectors.toList());
- //userIds减去excludeUserIds
- // 将排除的用户ID转化为Set,以便高效查找
- Set<Long> excludeSet = new HashSet<>(excludeUserIds);
- // 使用Stream API过滤掉排除的用户ID
- userIds = userIds.stream().filter(userId -> !excludeSet.contains(userId)).collect(Collectors.toList());
- }
- }
- return resetTokensByUserIds(userIds);
- }
- /**
- * 重置指定用户的登陆信息,强制重新登陆系统
- * @param startUserList
- * @return
- */
- public AjaxResult resetTokensByUserIds(List<Long> userIds) {
- if(CollectionUtils.isEmpty(userIds)){
- return AjaxResult.success();
- }
- ZuserToken zuserToken = new ZuserToken();
- zuserToken.setUserIds(userIds);
- List<ZuserToken> zuserTokenList = zuserTokenService.selectTopZuserTokenList(zuserToken);
- for (ZuserToken zt : zuserTokenList) {
- if(StringUtils.isNotNull(zt)){
- tokenService.removeUserToken(zt.getToken());
- // zuserTokenService.deleteZuserTokenById(zt.getId());
- }
- }
- List<Long> tokenIdList = zuserTokenList.stream().map(ZuserToken::getId).collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
- Long[] tokenIds = tokenIdList.toArray(new Long[tokenIdList.size()]);
- if(tokenIds.length>0){
- zuserTokenService.deleteZuserTokenByIds(tokenIds);
- }
- return AjaxResult.success();
- }
- public void updateBindStatus(Long userId, Integer bindStatus) {
- ZuserToken zuserToken = new ZuserToken();
- zuserToken.setUserIds(Lists.newArrayList(userId));
- List<ZuserToken> zuserTokenList = zuserTokenService.selectTopZuserTokenList(zuserToken);
- for (ZuserToken zt : zuserTokenList) {
- if(StringUtils.isNotNull(zt)){
- tokenService.updateBindStatus(zt.getToken(), bindStatus);
- }
- }
- }
- public AjaxResult resetTokens(List<SysUser> startUserList) {
- List<Long> userIds = startUserList.stream().map(SysUser::getUserId).collect(Collectors.toList());
- return resetTokensByUserIds(userIds);
- }
- /**
- * 重置指定用户的登陆信息,强制重新登陆系统
- * @param userName
- * @return
- */
- public AjaxResult resetTokens(String userName) {
- ZuserToken zuserToken = new ZuserToken();
- zuserToken.setUserName(userName);
- List<ZuserToken> zuserTokenList = zuserTokenService.selectTopZuserTokenList(zuserToken);
- for (ZuserToken zt : zuserTokenList) {
- if(StringUtils.isNotNull(zt)){
- tokenService.removeUserToken(zt.getToken());
- zuserTokenService.deleteZuserTokenById(zt.getId());
- }
- }
- return AjaxResult.success();
- }
- /**
- * 内部自动登陆,不记录日志
- * @param username
- * @param password
- * @return
- */
- public String loginInner(String username, String password) {
- // 用户验证
- Authentication authentication = null;
- try
- {
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
- AuthenticationContextHolder.setContext(authenticationToken);
- // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
- authentication = authenticationManager.authenticate(authenticationToken);
- }
- catch (Exception e)
- {
- if (e instanceof BadCredentialsException)
- {
- throw new UserPasswordNotMatchException();
- }
- else
- {
- throw new ServiceException(e.getMessage());
- }
- }
- finally
- {
- AuthenticationContextHolder.clearContext();
- }
- LoginUser loginUser = (LoginUser) authentication.getPrincipal();
- return tokenService.createToken(loginUser);
- }
- /**
- * 校验验证码
- *
- * @param username 用户名
- * @param code 验证码
- * @param uuid 唯一标识
- * @return 结果
- */
- public void validateCaptcha(String username, String code, String uuid)
- {
- boolean captchaEnabled = configService.selectCaptchaEnabled();
- if (captchaEnabled)
- {
- String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
- String captcha = redisCache.getCacheObject(verifyKey);
- if (captcha == null)
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
- throw new CaptchaExpireException();
- }
- redisCache.deleteObject(verifyKey);
- if (!code.equalsIgnoreCase(captcha))
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
- throw new CaptchaException();
- }
- }
- }
- /**
- * 登录前置校验
- * @param username 用户名
- * @param password 用户密码
- */
- public void loginPreCheck(String username, String password)
- {
- // 用户名或密码为空 错误
- if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
- throw new UserNotExistsException();
- }
- // 密码如果不在指定范围内 错误
- if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
- || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
- throw new UserPasswordNotMatchException();
- }
- // 用户名不在指定范围内 错误
- if (username.length() < UserConstants.USERNAME_MIN_LENGTH
- || username.length() > UserConstants.USERNAME_MAX_LENGTH)
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
- throw new UserPasswordNotMatchException();
- }
- // IP黑名单校验
- String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
- if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
- throw new BlackListException();
- }
- }
- public void loginMobilePreCheck(String phoneNumber, String code)
- {
- // 用户名或密码为空 错误
- if (StringUtils.isEmpty(phoneNumber) || StringUtils.isEmpty(code))
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
- throw new UserNotExistsException();
- }
- // 密码如果不在指定范围内 错误
- if (code.length() != 4)
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
- throw new UserPasswordNotMatchException();
- }
- // 用户名不在指定范围内 错误
- if (!PhoneUtils.isPhoneNumber(phoneNumber))
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
- throw new UserPasswordNotMatchException();
- }
- // IP黑名单校验
- String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
- if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
- {
- AsyncManager.me().execute(AsyncFactory.recordLogininfor(phoneNumber, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
- throw new BlackListException();
- }
- }
- /**
- * 记录登录信息
- *
- * @param userId 用户ID
- */
- public void recordLoginInfo(Long userId)
- {
- userService.updateLoginInfo(userId, IpUtils.getIpAddr(), DateUtils.getNowDate());
- }
- }
|