package com.uas.sso.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.uas.sso.SSOConfig; import com.uas.sso.SSOHelper; import com.uas.sso.SSOToken; import com.uas.sso.common.util.HttpUtil; import com.uas.sso.core.Const; import com.uas.sso.dao.UserRecordDao; import com.uas.sso.entity.*; import com.uas.sso.entity.login.*; import com.uas.sso.exception.PasswordErrorException; import com.uas.sso.exception.VisibleError; import com.uas.sso.foreign.entity.ForeignInfo; import com.uas.sso.foreign.factory.ForeignFactory; import com.uas.sso.foreign.service.ForeignService; import com.uas.sso.service.*; import com.uas.sso.util.PasswordLevelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.ui.ModelMap; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; /** * @author wangmh * @create 2018-08-10 10:19 * @desc **/ @Service public class LoginServiceImpl implements LoginService { @Autowired private UserService userService; @Autowired private TokenService tokenService; @Autowired private AppService appService; @Autowired private UserAccountService userAccountService; @Autowired private PersonalAccountService personalAccountService; @Autowired protected HttpServletRequest request; @Autowired protected HttpServletResponse response; @Autowired protected UserRecordDao userRecordDao; /** * 密码输错3次 */ private static final int PWD_ERROR_FIVE_TIME = 5; private final static Logger LOGGER = LoggerFactory.getLogger(LoginServiceImpl.class); /** * 密码输错3次 */ private static final int PWD_ERROR_THREE_TIME = 3; @Override public ModelMap loginByPassword(PasswordLogin loginParam) throws PasswordErrorException { // 校验参数 if (loginParam == null) { throw new VisibleError("参数错误"); } if (loginParam.getUsername() == null) { throw new VisibleError("用户名不能为空"); } if (loginParam.getPassword() == null) { throw new VisibleError("密码不能为空"); } // 获取用户信息 User user = userService.findByUsername(loginParam.getUsername()); if (user == null) { LOGGER.warn("用户名未找到!账号:{}, 密码:{}", loginParam.getUsername(), loginParam.getPassword()); throw new VisibleError("用户名或密码错误"); } // 获取密码错误次数 Integer pwdErrorCount = getPwdErrorCount(user.getUserUU()); // 校验验证码 if (pwdErrorCount >= PWD_ERROR_FIVE_TIME) { throw new VisibleError( "密码错误次数已达上限,今日无法登陆"); } // 校验账号是否被锁定,5次输错密码 if (pwdErrorCount >= PWD_ERROR_THREE_TIME && StringUtils.isEmpty(loginParam.getSureCaptcha())) { throw new VisibleError("验证码不能为空"); } if (pwdErrorCount >= PWD_ERROR_THREE_TIME && !loginParam.getSureCaptcha().equalsIgnoreCase(loginParam.getCaptcha())) { throw new VisibleError("验证码错误"); } // 校验密码 String encryPassword = userService.getEncryPassword(Const.ENCRY_FORMAT, loginParam.getPassword(), user.getSalt()); if (!encryPassword.equals(user.getPassword())) { int count = addPwdCount(user.getUserUU()); throw new PasswordErrorException("您输入的账号或密码有误", count); } // 校验密码强度,如果和存储的不同,则保存 int strength = PasswordLevelUtils.checkPasswordLevel(loginParam.getPassword()).getValue(); if (strength != user.getPasswordLevel()) { LOGGER.info("用户密码等级修改:{} -> {}",user.getPasswordLevel(), strength); user.setPasswordLevel(strength); userService.save(user); } // 登录 return login(user.getUserUU(), loginParam.getSpaceUU(), loginParam); } @Override public ModelMap loginByToken(TokenLogin loginParam) { Token tk = tokenService.findOne(loginParam.getToken()); // token不存在则不登录返回 if (tk == null) { LOGGER.warn("token({})已过期", loginParam.getToken()); new ModelMap("returnUrl", HttpUtil.decodeURL(loginParam.getReturnUrl())); } JSONObject data = JSON.parseObject(JSON.toJSONString(tk.getBind())); Long userUU = data.getLong("userUU"); Long spaceUU = data.getLong("spaceUU"); if (spaceUU == null) { spaceUU = loginParam.getSpaceUU(); } return login(userUU, spaceUU, loginParam); } @Override public ModelMap loginBySms(SmsLogin loginParam) { String token = loginParam.getToken(); String code = loginParam.getCode(); String mobile = loginParam.getMobile(); // 校验token if (token == null) { throw new VisibleError("请先获取验证码"); } Token existToken = tokenService.findOne(token); if (existToken == null || existToken.isExpired()) { throw new VisibleError("验证码已过期,请重新获取"); } if (!StringUtils.isEmpty(existToken.getMobile()) && !existToken.getMobile().equals(mobile)) { throw new VisibleError("手机号被修改,请重新获取验证码"); } if (StringUtils.isEmpty(code) || !code.equals(existToken.getBind())) { throw new VisibleError("验证码错误"); } // 获取登录用户 User user = userService.findByMobile(mobile); if (user == null) { throw new VisibleError("该手机号未注册"); } return login(user.getUserUU(), loginParam.getSpaceUU(), loginParam); } @Override public ModelMap loginByForeign(ForeignLogin loginParam) { String code = loginParam.getCode(); String type = loginParam.getType(); Long spaceUU = loginParam.getSpaceUU(); // 获取用户信息 User user = null; Long userUU = (Long) request.getSession().getAttribute("userUU"); if (userUU != null) { user = new User(userUU); } else { ForeignService foreignService = ForeignFactory.getForeignService(type); ForeignInfo foreignInfo = foreignService.getForeignInfoByCode(code); Optional accessTokenOptional = Optional.ofNullable(foreignInfo).map(ForeignInfo::getForeignAccessToken); if (!accessTokenOptional.isPresent()) { throw new VisibleError("验证信息过期"); } user = userService.findByForeignId(foreignInfo); // user为空提示未注册,不为空则放入session绑定用户使用 if (user == null) { // 提示前端用户微信未绑定账号 ModelMap map = new ModelMap("data", foreignInfo); Token token = new Token(map, foreignInfo.getForeignExpiresIn()); tokenService.save(token); ModelMap data = new ModelMap("hasRegister", false); data.put("token", token.getId()); return data; } request.getSession().setAttribute("userUU", user.getUserUU()); } return login(user.getUserUU(), spaceUU, loginParam).addAttribute("hasRegister", true); } @Override public int getPwdErrorCount(String username) { User user = userService.findByUsername(username); if (user == null) { throw new VisibleError("用户名不存在"); } return getPwdErrorCount(user.getUserUU()); } private ModelMap login(Long userUU, Long spaceUU, BaseLogin loginParam) { App app = appService.findOne(loginParam.getAppId()); if (app == null) { throw new VerifyError("应用不存在"); } App controlApp = StringUtils.isEmpty(app.getUserControl()) ? app : appService.findOne(app.getUserControl()); boolean personalEnable = Const.YES == controlApp.getPersonalEnable(); UserAccount userAccount = null; if (spaceUU == null) { // 找到用户账号信息 List userAccounts = userAccountService.findByUserUU(controlApp.getUid(), userUU); // 没有记录 if (CollectionUtils.isEmpty(userAccounts)) { // 没有记录如果当前应用允许个人账号的话,查找个人账号 userAccount = personalAccountService.findOneByUserUU(controlApp.getUid(), userUU); if (!personalEnable) { // 不支持个人账号则跳转优软云 loginParam.setReturnUrl(Const.HOME_PAGE); } } else if (userAccounts.size() == 1) { // 应用允许个人账号,并且账号未绑定企业,或者只绑定了一个企业,直接登录 userAccount = userAccounts.get(0); } else { // 返回企业id和名称 return getSpaceSelect(userAccounts, personalEnable); } } else if (personalEnable && Long.valueOf(spaceUU).equals(Const.SPACEUU_PERSONAL)) { // 使用个人账号登录 userAccount = personalAccountService.findOneByUserUU(controlApp.getUid(), userUU); } else { // 带企业登录 userAccount = userAccountService.findOneByUserUU(controlApp.getUid(), userUU, Long.valueOf(spaceUU)); } // 可能用户不在该企业,可能企业未开通该应用 if (userAccount == null) { LOGGER.warn("用户({})不在该企业({}),或企业({})未开通应用({}),登录应用:{}", userUU, spaceUU, spaceUU, controlApp.getUid(), loginParam.getAppId()); throw new VisibleError("数据错误"); } // 设置用户上次登录时间 UserRecord userRecord = userRecordDao.findOne(userUU); if (userRecord == null) { userRecord = new UserRecord(userUU); } if (userRecord.getLastLoginTime() != null) { // 将上次登录时间写入cookie userAccount.setLastLoginTime(userRecord.getLastLoginTime()); } // 将本次登录时间写入数据库 userRecord.setLastLoginTime(System.currentTimeMillis()); userRecordDao.save(userRecord); // 设置cookie request.setAttribute(SSOConfig.SSO_COOKIE_MAXAGE, loginParam.getMaxage()); SSOToken st = new SSOToken(request, userAccount.getMobile()); st.setData(JSON.toJSONString(userAccount)); SSOHelper.setSSOCookie(request, response, st, true); LOGGER.info("用户({})登录成功,时间:{}", userUU, userRecord.getLastLoginTime()); return loginByUser(userAccount, loginParam); } private ModelMap getSpaceSelect(List userAccounts, boolean personalEnable) { List> spaces = new ArrayList>(); Map space = null; // 设置带企业账号 for (UserAccount userAccount : userAccounts) { space = new HashMap(2); space.put("id", userAccount.getSpaceUU()); space.put("name", userAccount.getSpaceName()); spaces.add(space); } // 设置个人账号 if (personalEnable) { space = new HashMap(2); space.put("id", Const.SPACEUU_PERSONAL); space.put("name", String.format("%s(个人)", userAccounts.get(0).getVipName())); spaces.add(space); } return new ModelMap("spaces", spaces); } private ModelMap loginByUser(UserAccount userAccount, BaseLogin loginParam) { resetPwdCount(userAccount.getUserUU()); // 设置返回值,通知各个应用用户已经登录 ModelMap data = new ModelMap(); data = addOtherAppRequestData(userAccount, data, loginParam); data.put("returnUrl", HttpUtil.decodeURL(loginParam.getReturnUrl())); return data; } private ModelMap addOtherAppRequestData(UserAccount userAccount, ModelMap data, BaseLogin loginParam) { List loginUrls = appService.findAllLoginUrl(); boolean loginAll = loginParam.isLoginAll(); String currentUrl = HttpUtil.decodeURL(loginParam.getBaseUrl()); // 添加baseUrl if (loginAll && !loginUrls.contains(currentUrl)) { loginUrls.add(currentUrl); } data.put("loginUrls", loginUrls); data.put("currentUrl", currentUrl); // 添加传递数据 JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(userAccount)); Integer maxage = (Integer) request.getAttribute(SSOConfig.SSO_COOKIE_MAXAGE); jsonObject.put("maxage", maxage); data.put("data", jsonObject); return data; } /** * 获取密码错误次数 * @param userUU 用户uu号 * @return */ private Integer getPwdErrorCount(Long userUU) { Token token = tokenService.findOne("login_count_" + userUU); return (Integer) Optional.ofNullable(token).map(Token::getBind).orElse(0); } /** * 添加密码错误次数 * @param userUU 用户uu号 * @return */ private int addPwdCount(Long userUU) { String tokenId = "login_count_" + userUU; Token token = tokenService.findOne(tokenId); token = token == null ? new Token(0, getSecondsNextEarlyMorning().intValue()) : token; int count = (Integer) Optional.ofNullable(token).map(Token::getBind).orElse(0) + 1; token.setId(tokenId); token.setBind(count); tokenService.save(token); return count; } private Long getSecondsNextEarlyMorning() { Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_YEAR, 1); // 改成这样就好了 cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.MILLISECOND, 0); Long seconds = (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000; return seconds.longValue(); } /** * 重置密码错误次数 * @param userUU 用户uu号 * @return */ private void resetPwdCount(Long userUU) { tokenService.delete("login_count_" + userUU); } }