From d43b77b3c88fe4c002cff2fe3bd2af3f7f4f4249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=85?= <1669139609@qq.com> Date: Mon, 24 Jan 2022 16:15:08 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=99=BB=E5=BD=95=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=20=E6=96=B0=E5=A2=9EDate=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/controller/LoginController.java | 421 +-------------- .../modules/system/service/ILoginService.java | 112 ++++ .../system/service/impl/LoginServiceImpl.java | 489 ++++++++++++++++++ .../jero/modules/system/util/DateUtils.java | 57 ++ 4 files changed, 676 insertions(+), 403 deletions(-) create mode 100644 jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/ILoginService.java create mode 100644 jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/impl/LoginServiceImpl.java create mode 100644 jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/util/DateUtils.java diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/controller/LoginController.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/controller/LoginController.java index fb9f22be..75661187 100644 --- a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/controller/LoginController.java +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/controller/LoginController.java @@ -5,6 +5,7 @@ import cn.hutool.crypto.asymmetric.RSA; import com.alibaba.fastjson.JSONObject; import com.aliyuncs.exceptions.ClientException; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.jero.modules.system.service.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -21,10 +22,6 @@ import com.jero.common.util.encryption.EncryptedString; import com.jero.modules.system.entity.SysDepart; import com.jero.modules.system.entity.SysUser; import com.jero.modules.system.model.SysLoginModel; -import com.jero.modules.system.service.ISysDepartService; -import com.jero.modules.system.service.ISysDictService; -import com.jero.modules.system.service.ISysLogService; -import com.jero.modules.system.service.ISysUserService; import com.jero.modules.system.util.RandImageUtil; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -48,104 +45,12 @@ import java.util.*; @Slf4j public class LoginController { @Autowired - private ISysUserService sysUserService; - @Autowired - private ISysBaseAPI sysBaseAPI; - @Autowired - private ISysLogService logService; - @Autowired - private RedisUtil redisUtil; - @Autowired - private ISysDepartService sysDepartService; - @Autowired - private ISysDictService sysDictService; - @Resource - private BaseCommonService baseCommonService; + private ILoginService loginService; - private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890"; - //密码登录错误的次数前缀 - public static final String RETRY_LOGIN_PREFIX = "login:retryLoginCount_"; - //密码登录错误的最大限制次数 - public static final int RETRY_LOGIN_MAX_COUNT = 5; @ApiOperation("登录接口") @RequestMapping(value = "/login", method = RequestMethod.POST) public Result login(@RequestBody SysLoginModel sysLoginModel){ - Result result = new Result(); - String username = sysLoginModel.getUsername(); - String password = sysLoginModel.getPassword(); - String rsaPublicKey = sysLoginModel.getRsaPublicKey(); - String rsaPrivateKey = String.valueOf(redisUtil.get(rsaPublicKey)); - //update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题 - //前端密码加密,后端进行密码解密 - //password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密 - //update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题 - //update-begin-author:taoyan date:20190828 for:校验验证码 - String captcha = sysLoginModel.getCaptcha(); - if(captcha==null){ - result.error500("验证码无效"); - return result; - } - String lowerCaseCaptcha = captcha.toLowerCase(); - String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+sysLoginModel.getCheckKey(), "utf-8"); - Object checkCode = redisUtil.get(realKey); - //当进入登录页时,有一定几率出现验证码错误 #1714 - if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) { - result.error500("验证码错误"); - return result; - } - try { - //解密获取密码和用户名 - password = CommonUtils.decryptBtRsaPriKey(password, rsaPrivateKey); - username = CommonUtils.decryptBtRsaPriKey(username, rsaPrivateKey); - } catch (Exception e) { - e.printStackTrace(); - } - //1. 校验用户是否有效 - //update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(SysUser::getUsername,username); - SysUser sysUser = sysUserService.getOne(queryWrapper); - //update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false - result = sysUserService.checkUserIsEffective(sysUser); - if(!result.isSuccess()) { - return result; - } - - // 若用户名有效,则查询该账号的登陆失败次数是否符合等保要求 - int retryCount = 0 ; - if ( redisUtil.get(RETRY_LOGIN_PREFIX + username) != null){ - retryCount = (int) redisUtil.get(RETRY_LOGIN_PREFIX + username); - } - if (retryCount >= RETRY_LOGIN_MAX_COUNT){ - result.error500("密码错误次数过多,请稍后重试"); - return result; - } - //2. 校验用户名或密码是否正确 - String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt()); - String syspassword = sysUser.getPassword(); - if (!syspassword.equals(userpassword)) { - // 重试登录次数加一 - retryCount++; - if( retryCount == 1){ - redisUtil.set(RETRY_LOGIN_PREFIX + username,retryCount,60 * 30); - }else { - redisUtil.set(RETRY_LOGIN_PREFIX + username,retryCount,redisUtil.getExpire(RETRY_LOGIN_PREFIX + username)); - } - String msg = retryCount == RETRY_LOGIN_MAX_COUNT ? "密码错误次数过多,请稍后重试":"用户名或密码错误,剩余可登录次数:"+(RETRY_LOGIN_MAX_COUNT - retryCount); - result.error500(msg); - return result; - } - //登录成功,清除错误登录次数 - redisUtil.del(RETRY_LOGIN_PREFIX + username); - - //用户登录信息 - userInfo(sysUser, result); - //update-begin--Author:wangshuai Date:20200714 for:登录日志没有记录人员 - LoginUser loginUser = new LoginUser(); - BeanUtils.copyProperties(sysUser, loginUser); - baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser); - //update-end--Author:wangshuai Date:20200714 for:登录日志没有记录人员 - return result; + return loginService.login(sysLoginModel); } /** @@ -156,30 +61,7 @@ public class LoginController { */ @RequestMapping(value = "/logout") public Result logout(HttpServletRequest request,HttpServletResponse response) { - //用户退出逻辑 - String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); - if(oConvertUtils.isEmpty(token)) { - return Result.error("退出登录失败!"); - } - String username = JwtUtil.getUsername(token); - LoginUser sysUser = sysBaseAPI.getUserByName(username); - if(sysUser!=null) { - //update-begin--Author:wangshuai Date:20200714 for:登出日志没有记录人员 - baseCommonService.addLog("用户名: "+sysUser.getRealname()+",退出成功!", CommonConstant.LOG_TYPE_1, null,sysUser); - //update-end--Author:wangshuai Date:20200714 for:登出日志没有记录人员 - log.info(" 用户名: "+sysUser.getRealname()+",退出成功! "); - //清空用户登录Token缓存 - redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token); - //清空用户登录Shiro权限缓存 - redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId()); - //清空用户的缓存信息(包括部门信息),例如sys:cache:user:: - redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername())); - //调用shiro的logout - SecurityUtils.getSubject().logout(); - return Result.OK("退出登录成功!"); - }else { - return Result.error("Token无效!"); - } + return loginService.logout(request,response); } /** @@ -188,29 +70,7 @@ public class LoginController { */ @GetMapping("loginfo") public Result loginfo() { - Result result = new Result(); - JSONObject obj = new JSONObject(); - //update-begin--Author:zhangweijian Date:20190428 for:传入开始时间,结束时间参数 - // 获取一天的开始和结束时间 - Calendar calendar = new GregorianCalendar(); - calendar.set(Calendar.HOUR_OF_DAY, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - Date dayStart = calendar.getTime(); - calendar.add(Calendar.DATE, 1); - Date dayEnd = calendar.getTime(); - // 获取系统访问记录 - Long totalVisitCount = logService.findTotalVisitCount(); - obj.put("totalVisitCount", totalVisitCount); - Long todayVisitCount = logService.findTodayVisitCount(dayStart,dayEnd); - obj.put("todayVisitCount", todayVisitCount); - Long todayIp = logService.findTodayIp(dayStart,dayEnd); - //update-end--Author:zhangweijian Date:20190428 for:传入开始时间,结束时间参数 - obj.put("todayIp", todayIp); - result.setResult(obj); - result.success("登录成功"); - return result; + return loginService.loginfo(); } /** @@ -219,22 +79,9 @@ public class LoginController { */ @GetMapping("visitInfo") public Result>> visitInfo() { - Result>> result = new Result>>(); - Calendar calendar = new GregorianCalendar(); - calendar.set(Calendar.HOUR_OF_DAY,0); - calendar.set(Calendar.MINUTE,0); - calendar.set(Calendar.SECOND,0); - calendar.set(Calendar.MILLISECOND,0); - calendar.add(Calendar.DAY_OF_MONTH, 1); - Date dayEnd = calendar.getTime(); - calendar.add(Calendar.DAY_OF_MONTH, -7); - Date dayStart = calendar.getTime(); - List> list = logService.findVisitCount(dayStart, dayEnd); - result.setResult(oConvertUtils.toLowerCasePageList(list)); - return result; + return loginService.visitInfo(); } - - + /** * 登陆成功选择用户当前部门 * @param user @@ -242,19 +89,7 @@ public class LoginController { */ @RequestMapping(value = "/selectDepart", method = RequestMethod.PUT) public Result selectDepart(@RequestBody SysUser user) { - Result result = new Result(); - String username = user.getUsername(); - if(oConvertUtils.isEmpty(username)) { - LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal(); - username = sysUser.getUsername(); - } - String orgCode= user.getOrgCode(); - this.sysUserService.updateUserDepart(username, orgCode); - SysUser sysUser = sysUserService.getUserByName(username); - JSONObject obj = new JSONObject(); - obj.put("userInfo", sysUser); - result.setResult(obj); - return result; + return loginService.selectDepart(user); } /** @@ -265,83 +100,9 @@ public class LoginController { */ @PostMapping(value = "/sms") public Result sms(@RequestBody JSONObject jsonObject) { - Result result = new Result(); - String mobile = jsonObject.get("mobile").toString(); - //手机号模式 登录模式: "2" 注册模式: "1" - String smsmode=jsonObject.get("smsmode").toString(); - log.info(mobile); - if(oConvertUtils.isEmpty(mobile)){ - result.setMessage("手机号不允许为空!"); - result.setSuccess(false); - return result; - } - Object object = redisUtil.get(mobile); - if (object != null) { - result.setMessage("验证码10分钟内,仍然有效!"); - result.setSuccess(false); - return result; - } - - //随机数 - String captcha = RandomUtil.randomNumbers(6); - JSONObject obj = new JSONObject(); - obj.put("code", captcha); - try { - boolean b = false; - //注册模板 - if (CommonConstant.SMS_TPL_TYPE_1.equals(smsmode)) { - SysUser sysUser = sysUserService.getUserByPhone(mobile); - if(sysUser!=null) { - result.error500(" 手机号已经注册,请直接登录!"); - baseCommonService.addLog("手机号已经注册,请直接登录!", CommonConstant.LOG_TYPE_1, null); - return result; - } - b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.REGISTER_TEMPLATE_CODE); - }else { - //登录模式,校验用户有效性 - SysUser sysUser = sysUserService.getUserByPhone(mobile); - result = sysUserService.checkUserIsEffective(sysUser); - if(!result.isSuccess()) { - String message = result.getMessage(); - if("该用户不存在,请注册".equals(message)){ - result.error500("该用户不存在或未绑定手机号"); - } - return result; - } - - /** - * smsmode 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板 - */ - if (CommonConstant.SMS_TPL_TYPE_0.equals(smsmode)) { - //登录模板 - b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_TEMPLATE_CODE); - } else if(CommonConstant.SMS_TPL_TYPE_2.equals(smsmode)) { - //忘记密码模板 - b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE); - } - } - - if (b == false) { - result.setMessage("短信验证码发送失败,请稍后重试"); - result.setSuccess(false); - return result; - } - //验证码10分钟内有效 - redisUtil.set(mobile, captcha, 600); - //update-begin--Author:scott Date:20190812 for:issues#391 - //result.setResult(captcha); - //update-end--Author:scott Date:20190812 for:issues#391 - result.setSuccess(true); - - } catch (ClientException e) { - e.printStackTrace(); - result.error500(" 短信接口未配置,请联系管理员!"); - return result; - } - return result; + return loginService.sms(jsonObject); } - /** * 手机号登录接口 * @@ -351,72 +112,7 @@ public class LoginController { @ApiOperation("手机号登录接口") @PostMapping("/phoneLogin") public Result phoneLogin(@RequestBody JSONObject jsonObject) { - Result result = new Result(); - String phone = jsonObject.getString("mobile"); - - //校验用户有效性 - SysUser sysUser = sysUserService.getUserByPhone(phone); - result = sysUserService.checkUserIsEffective(sysUser); - if(!result.isSuccess()) { - return result; - } - - String smscode = jsonObject.getString("captcha"); - Object code = redisUtil.get(phone); - if (!smscode.equals(code)) { - result.setMessage("手机验证码错误"); - return result; - } - //用户信息 - userInfo(sysUser, result); - //添加日志 - baseCommonService.addLog("用户名: " + sysUser.getUsername() + ",登录成功!", CommonConstant.LOG_TYPE_1, null); - - return result; - } - - - /** - * 用户信息 - * - * @param sysUser - * @param result - * @return - */ - private Result userInfo(SysUser sysUser, Result result) { - String syspassword = sysUser.getPassword(); - String username = sysUser.getUsername(); - // 生成token - String token = JwtUtil.sign(username, syspassword); - // 设置token缓存有效时间 - redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); - redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000); - - // 获取用户部门信息 - JSONObject obj = new JSONObject(); - List departs = sysDepartService.queryUserDeparts(sysUser.getId()); - obj.put("departs", departs); - if (departs == null || departs.size() == 0) { - obj.put("multi_depart", 0); - } else if (departs.size() == 1) { - sysUserService.updateUserDepart(username, departs.get(0).getOrgCode()); - obj.put("multi_depart", 1); - } else { - //查询当前是否有登录部门 - // update-begin--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去 - SysUser sysUserById = sysUserService.getById(sysUser.getId()); - if(oConvertUtils.isEmpty(sysUserById.getOrgCode())){ - sysUserService.updateUserDepart(username, departs.get(0).getOrgCode()); - } - // update-end--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去 - obj.put("multi_depart", 2); - } - obj.put("token", token); - obj.put("userInfo", sysUser); - obj.put("sysAllDictItems", sysDictService.queryAllDictItems()); - result.setResult(obj); - result.success("登录成功"); - return result; + return loginService.phoneLogin(jsonObject); } /** @@ -425,12 +121,8 @@ public class LoginController { */ @GetMapping(value = "/getEncryptedString") public Result> getEncryptedString(){ - Result> result = new Result>(); - Map map = new HashMap(); - map.put("key", EncryptedString.key); - map.put("iv",EncryptedString.iv); - result.setResult(map); - return result; + return loginService.getEncryptedString(); + } /** @@ -441,20 +133,7 @@ public class LoginController { @ApiOperation("获取验证码") @GetMapping(value = "/randomImage/{key}") public Result randomImage(HttpServletResponse response,@PathVariable String key){ - Result res = new Result(); - try { - String code = RandomUtil.randomString(BASE_CHECK_CODES,4); - String lowerCaseCode = code.toLowerCase(); - String realKey = MD5Util.MD5Encode(lowerCaseCode+key, "utf-8"); - redisUtil.set(realKey, lowerCaseCode, 60); - String base64 = RandImageUtil.generate(code); - res.setSuccess(true); - res.setResult(base64); - } catch (Exception e) { - res.error500("获取验证码出错"+e.getMessage()); - e.printStackTrace(); - } - return res; + return randomImage(response,key); } /** @@ -464,55 +143,8 @@ public class LoginController { * @throws Exception */ @RequestMapping(value = "/mLogin", method = RequestMethod.POST) - public Result mLogin(@RequestBody SysLoginModel sysLoginModel) throws Exception { - Result result = new Result(); - String username = sysLoginModel.getUsername(); - String password = sysLoginModel.getPassword(); - - //1. 校验用户是否有效 - SysUser sysUser = sysUserService.getUserByName(username); - result = sysUserService.checkUserIsEffective(sysUser); - if(!result.isSuccess()) { - return result; - } - - //2. 校验用户名或密码是否正确 - String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt()); - String syspassword = sysUser.getPassword(); - if (!syspassword.equals(userpassword)) { - result.error500("用户名或密码错误"); - return result; - } - - String orgCode = sysUser.getOrgCode(); - if(oConvertUtils.isEmpty(orgCode)) { - //如果当前用户无选择部门 查看部门关联信息 - List departs = sysDepartService.queryUserDeparts(sysUser.getId()); - if (departs == null || departs.size() == 0) { - result.error500("用户暂未归属部门,不可登录!"); - return result; - } - orgCode = departs.get(0).getOrgCode(); - sysUser.setOrgCode(orgCode); - this.sysUserService.updateUserDepart(username, orgCode); - } - JSONObject obj = new JSONObject(); - //用户登录信息 - obj.put("userInfo", sysUser); - - // 生成token - String token = JwtUtil.sign(username, syspassword); - // 设置超时时间 - redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); - redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000); - - //token 信息 - obj.put("token", token); - result.setResult(obj); - result.setSuccess(true); - result.setCode(200); - baseCommonService.addLog("用户名: " + username + ",登录成功[移动端]!", CommonConstant.LOG_TYPE_1, null); - return result; + public Result mLogin(@RequestBody SysLoginModel sysLoginModel) { + return loginService.mLogin(sysLoginModel); } /** @@ -522,19 +154,9 @@ public class LoginController { */ @RequestMapping(value = "/checkCaptcha", method = RequestMethod.POST) public Result checkCaptcha(@RequestBody SysLoginModel sysLoginModel){ - String captcha = sysLoginModel.getCaptcha(); - String checkKey = sysLoginModel.getCheckKey(); - if(captcha==null){ - return Result.error("验证码无效"); - } - String lowerCaseCaptcha = captcha.toLowerCase(); - String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+checkKey, "utf-8"); - Object checkCode = redisUtil.get(realKey); - if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) { - return Result.error("验证码错误"); - } - return Result.OK(); + return loginService.checkCaptcha(sysLoginModel); } + /** * 返回一个RSA公钥 * @author 马志朝 @@ -545,13 +167,6 @@ public class LoginController { @ApiOperation("获取RSA公钥") @GetMapping("/getRSAPublicKey") public Result getRSAPublicKey(){ - RSA rsa = new RSA(); - String privateKeyBase64 = rsa.getPrivateKeyBase64(); - String publicKeyBase64 = rsa.getPublicKeyBase64(); - //存到redis key为公钥 value为私钥 - redisUtil.set(publicKeyBase64, privateKeyBase64, 60L); - Result result = new Result<>(); - result.setResult(publicKeyBase64); - return result; + return loginService.getRSAPublicKey(); } } \ No newline at end of file diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/ILoginService.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/ILoginService.java new file mode 100644 index 00000000..0eaf5eb2 --- /dev/null +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/ILoginService.java @@ -0,0 +1,112 @@ +package com.jero.modules.system.service; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.IService; +import com.jero.common.api.vo.Result; +import com.jero.common.exception.JeroBootException; +import com.jero.modules.system.entity.SysCategory; +import com.jero.modules.system.entity.SysUser; +import com.jero.modules.system.model.SysLoginModel; +import com.jero.modules.system.model.TreeSelectModel; +import io.swagger.annotations.ApiOperation; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + +/** + * @Description: 登录 + * @Author: jero-boot + * @Date: 2022-01-24 + * @Version: V1.0 + */ +public interface ILoginService{ + + /** + * 登录 + * @param sysLoginModel + * @return Result + */ + public Result login(SysLoginModel sysLoginModel); + + /** + * 退出登录 + * @param request response + * @return Result + */ + public Result logout(HttpServletRequest request, HttpServletResponse response); + + /** + * 获取访问量 + * @return Result + */ + public Result loginfo(); + + /** + * 获取访问量 + * @return Result + */ + public Result>> visitInfo(); + + /** + * 登陆成功选择用户当前部门 + * @param user + * @return + */ + public Result selectDepart(SysUser user); + + /** + * 短信登录接口 + * + * @param jsonObject + * @return + */ + public Result sms(JSONObject jsonObject); + + /** + * 手机号登录接口 + * + * @param jsonObject + * @return + */ + public Result phoneLogin(JSONObject jsonObject); + + /** + * 获取加密字符串 + * @return + */ + public Result> getEncryptedString(); + + /** + * 后台生成图形验证码 :有效 + * @param response + * @param key + */ + public Result randomImage(HttpServletResponse response,String key); + + /** + * app登录 + * @param sysLoginModel + * @return + * @throws Exception + */ + public Result mLogin(SysLoginModel sysLoginModel); + + /** + * 图形验证码 + * @param sysLoginModel + * @return + */ + public Result checkCaptcha(@RequestBody SysLoginModel sysLoginModel); + + /** + * 返回一个RSA公钥 + * @author 马志朝 + * @date 2021/4/15 15:01 + * @param + * @return com.jero.common.api.vo.Result + */ + public Result getRSAPublicKey(); +} diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/impl/LoginServiceImpl.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/impl/LoginServiceImpl.java new file mode 100644 index 00000000..b521534a --- /dev/null +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/service/impl/LoginServiceImpl.java @@ -0,0 +1,489 @@ +package com.jero.modules.system.service.impl; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.asymmetric.RSA; +import com.alibaba.fastjson.JSONObject; +import com.aliyuncs.exceptions.ClientException; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.jero.common.api.vo.Result; +import com.jero.common.constant.CacheConstant; +import com.jero.common.constant.CommonConstant; +import com.jero.common.exception.JeroBootException; +import com.jero.common.system.api.ISysBaseAPI; +import com.jero.common.system.util.JwtUtil; +import com.jero.common.system.vo.LoginUser; +import com.jero.common.util.*; +import com.jero.common.util.encryption.EncryptedString; +import com.jero.modules.base.service.BaseCommonService; +import com.jero.modules.system.entity.SysDepart; +import com.jero.modules.system.entity.SysUser; +import com.jero.modules.system.model.SysLoginModel; +import com.jero.modules.system.model.TreeSelectModel; +import com.jero.modules.system.service.*; +import com.jero.modules.system.util.RandImageUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.SecurityUtils; +import org.jeecgframework.poi.excel.imports.base.ImportFileServiceI; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.*; + +/** + * 登录 实现类 + */ +@Slf4j +@Service +public class LoginServiceImpl implements ILoginService { + + @Autowired + private ISysUserService sysUserService; + @Autowired + private ISysBaseAPI sysBaseAPI; + @Autowired + private ISysLogService logService; + @Autowired + private RedisUtil redisUtil; + @Autowired + private ISysDepartService sysDepartService; + @Autowired + private ISysDictService sysDictService; + @Resource + private BaseCommonService baseCommonService; + + private static final String BASE_CHECK_CODES = "qwertyuiplkjhgfdsazxcvbnmQWERTYUPLKJHGFDSAZXCVBNM1234567890"; + //密码登录错误的次数前缀 + public static final String RETRY_LOGIN_PREFIX = "login:retryLoginCount_"; + //密码登录错误的最大限制次数 + public static final int RETRY_LOGIN_MAX_COUNT = 5; + + @Override + public Result login(SysLoginModel sysLoginModel) { + Result result = new Result(); + String username = sysLoginModel.getUsername(); + String password = sysLoginModel.getPassword(); + String rsaPublicKey = sysLoginModel.getRsaPublicKey(); + String rsaPrivateKey = String.valueOf(redisUtil.get(rsaPublicKey)); + //update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题 + //前端密码加密,后端进行密码解密 + //password = AesEncryptUtil.desEncrypt(sysLoginModel.getPassword().replaceAll("%2B", "\\+")).trim();//密码解密 + //update-begin--Author:scott Date:20190805 for:暂时注释掉密码加密逻辑,有点问题 + //update-begin-author:taoyan date:20190828 for:校验验证码 + String captcha = sysLoginModel.getCaptcha(); + if(captcha==null){ + result.error500("验证码无效"); + return result; + } + String lowerCaseCaptcha = captcha.toLowerCase(); + String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+sysLoginModel.getCheckKey(), "utf-8"); + Object checkCode = redisUtil.get(realKey); + //当进入登录页时,有一定几率出现验证码错误 #1714 + if(checkCode==null || !checkCode.toString().equals(lowerCaseCaptcha)) { + result.error500("验证码错误"); + return result; + } + try { + //解密获取密码和用户名 + password = CommonUtils.decryptBtRsaPriKey(password, rsaPrivateKey); + username = CommonUtils.decryptBtRsaPriKey(username, rsaPrivateKey); + } catch (Exception e) { + e.printStackTrace(); + } + //1. 校验用户是否有效 + //update-begin-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(SysUser::getUsername,username); + SysUser sysUser = sysUserService.getOne(queryWrapper); + //update-end-author:wangshuai date:20200601 for: 登录代码验证用户是否注销bug,if条件永远为false + result = sysUserService.checkUserIsEffective(sysUser); + if(!result.isSuccess()) { + return result; + } + + // 若用户名有效,则查询该账号的登陆失败次数是否符合等保要求 + int retryCount = 0 ; + if ( redisUtil.get(RETRY_LOGIN_PREFIX + username) != null){ + retryCount = (int) redisUtil.get(RETRY_LOGIN_PREFIX + username); + } + if (retryCount >= RETRY_LOGIN_MAX_COUNT){ + result.error500("密码错误次数过多,请稍后重试"); + return result; + } + //2. 校验用户名或密码是否正确 + String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt()); + String syspassword = sysUser.getPassword(); + if (!syspassword.equals(userpassword)) { + // 重试登录次数加一 + retryCount++; + if( retryCount == 1){ + redisUtil.set(RETRY_LOGIN_PREFIX + username,retryCount,60 * 30); + }else { + redisUtil.set(RETRY_LOGIN_PREFIX + username,retryCount,redisUtil.getExpire(RETRY_LOGIN_PREFIX + username)); + } + String msg = retryCount == RETRY_LOGIN_MAX_COUNT ? "密码错误次数过多,请稍后重试":"用户名或密码错误,剩余可登录次数:"+(RETRY_LOGIN_MAX_COUNT - retryCount); + result.error500(msg); + return result; + } + //登录成功,清除错误登录次数 + redisUtil.del(RETRY_LOGIN_PREFIX + username); + + //用户登录信息 + userInfo(sysUser, result); + //update-begin--Author:wangshuai Date:20200714 for:登录日志没有记录人员 + LoginUser loginUser = new LoginUser(); + BeanUtils.copyProperties(sysUser, loginUser); + baseCommonService.addLog("用户名: " + username + ",登录成功!", CommonConstant.LOG_TYPE_1, null,loginUser); + //update-end--Author:wangshuai Date:20200714 for:登录日志没有记录人员 + return result; + + } + + @Override + public Result logout(HttpServletRequest request, HttpServletResponse response) { + //用户退出逻辑 + String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); + if(oConvertUtils.isEmpty(token)) { + return Result.error("退出登录失败!"); + } + String username = JwtUtil.getUsername(token); + LoginUser sysUser = sysBaseAPI.getUserByName(username); + if(sysUser!=null) { + //update-begin--Author:wangshuai Date:20200714 for:登出日志没有记录人员 + baseCommonService.addLog("用户名: "+sysUser.getRealname()+",退出成功!", CommonConstant.LOG_TYPE_1, null,sysUser); + //update-end--Author:wangshuai Date:20200714 for:登出日志没有记录人员 + log.info(" 用户名: "+sysUser.getRealname()+",退出成功! "); + //清空用户登录Token缓存 + redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token); + //清空用户登录Shiro权限缓存 + redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId()); + //清空用户的缓存信息(包括部门信息),例如sys:cache:user:: + redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername())); + //调用shiro的logout + SecurityUtils.getSubject().logout(); + return Result.OK("退出登录成功!"); + }else { + return Result.error("Token无效!"); + } + } + + @Override + public Result loginfo() { + Result result = new Result(); + JSONObject obj = new JSONObject(); + //update-begin--Author:zhangweijian Date:20190428 for:传入开始时间,结束时间参数 + // 获取一天的开始和结束时间 + Calendar calendar = new GregorianCalendar(); + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + Date dayStart = calendar.getTime(); + calendar.add(Calendar.DATE, 1); + Date dayEnd = calendar.getTime(); + // 获取系统访问记录 + Long totalVisitCount = logService.findTotalVisitCount(); + obj.put("totalVisitCount", totalVisitCount); + Long todayVisitCount = logService.findTodayVisitCount(dayStart,dayEnd); + obj.put("todayVisitCount", todayVisitCount); + Long todayIp = logService.findTodayIp(dayStart,dayEnd); + //update-end--Author:zhangweijian Date:20190428 for:传入开始时间,结束时间参数 + obj.put("todayIp", todayIp); + result.setResult(obj); + result.success("登录成功"); + return result; + } + + @Override + public Result>> visitInfo() { + Result>> result = new Result>>(); + Calendar calendar = new GregorianCalendar(); + calendar.set(Calendar.HOUR_OF_DAY,0); + calendar.set(Calendar.MINUTE,0); + calendar.set(Calendar.SECOND,0); + calendar.set(Calendar.MILLISECOND,0); + calendar.add(Calendar.DAY_OF_MONTH, 1); + Date dayEnd = calendar.getTime(); + calendar.add(Calendar.DAY_OF_MONTH, -7); + Date dayStart = calendar.getTime(); + List> list = logService.findVisitCount(dayStart, dayEnd); + result.setResult(oConvertUtils.toLowerCasePageList(list)); + return result; + } + + @Override + public Result selectDepart(SysUser user) { + Result result = new Result(); + String username = user.getUsername(); + if(oConvertUtils.isEmpty(username)) { + LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal(); + username = sysUser.getUsername(); + } + String orgCode= user.getOrgCode(); + this.sysUserService.updateUserDepart(username, orgCode); + SysUser sysUser = sysUserService.getUserByName(username); + JSONObject obj = new JSONObject(); + obj.put("userInfo", sysUser); + result.setResult(obj); + return result; + } + + @Override + public Result sms(JSONObject jsonObject) { + Result result = new Result(); + String mobile = jsonObject.get("mobile").toString(); + //手机号模式 登录模式: "2" 注册模式: "1" + String smsmode=jsonObject.get("smsmode").toString(); + log.info(mobile); + if(oConvertUtils.isEmpty(mobile)){ + result.setMessage("手机号不允许为空!"); + result.setSuccess(false); + return result; + } + Object object = redisUtil.get(mobile); + if (object != null) { + result.setMessage("验证码10分钟内,仍然有效!"); + result.setSuccess(false); + return result; + } + + //随机数 + String captcha = RandomUtil.randomNumbers(6); + JSONObject obj = new JSONObject(); + obj.put("code", captcha); + try { + boolean b = false; + //注册模板 + if (CommonConstant.SMS_TPL_TYPE_1.equals(smsmode)) { + SysUser sysUser = sysUserService.getUserByPhone(mobile); + if(sysUser!=null) { + result.error500(" 手机号已经注册,请直接登录!"); + baseCommonService.addLog("手机号已经注册,请直接登录!", CommonConstant.LOG_TYPE_1, null); + return result; + } + b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.REGISTER_TEMPLATE_CODE); + }else { + //登录模式,校验用户有效性 + SysUser sysUser = sysUserService.getUserByPhone(mobile); + result = sysUserService.checkUserIsEffective(sysUser); + if(!result.isSuccess()) { + String message = result.getMessage(); + if("该用户不存在,请注册".equals(message)){ + result.error500("该用户不存在或未绑定手机号"); + } + return result; + } + + /** + * smsmode 短信模板方式 0 .登录模板、1.注册模板、2.忘记密码模板 + */ + if (CommonConstant.SMS_TPL_TYPE_0.equals(smsmode)) { + //登录模板 + b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.LOGIN_TEMPLATE_CODE); + } else if(CommonConstant.SMS_TPL_TYPE_2.equals(smsmode)) { + //忘记密码模板 + b = DySmsHelper.sendSms(mobile, obj, DySmsEnum.FORGET_PASSWORD_TEMPLATE_CODE); + } + } + + if (b == false) { + result.setMessage("短信验证码发送失败,请稍后重试"); + result.setSuccess(false); + return result; + } + //验证码10分钟内有效 + redisUtil.set(mobile, captcha, 600); + //update-begin--Author:scott Date:20190812 for:issues#391 + //result.setResult(captcha); + //update-end--Author:scott Date:20190812 for:issues#391 + result.setSuccess(true); + + } catch (ClientException e) { + e.printStackTrace(); + result.error500(" 短信接口未配置,请联系管理员!"); + return result; + } + return result; + } + + @Override + public Result phoneLogin(JSONObject jsonObject) { + Result result = new Result(); + String phone = jsonObject.getString("mobile"); + + //校验用户有效性 + SysUser sysUser = sysUserService.getUserByPhone(phone); + result = sysUserService.checkUserIsEffective(sysUser); + if(!result.isSuccess()) { + return result; + } + + String smscode = jsonObject.getString("captcha"); + Object code = redisUtil.get(phone); + if (!smscode.equals(code)) { + result.setMessage("手机验证码错误"); + return result; + } + //用户信息 + userInfo(sysUser, result); + //添加日志 + baseCommonService.addLog("用户名: " + sysUser.getUsername() + ",登录成功!", CommonConstant.LOG_TYPE_1, null); + + return result; + } + + @Override + public Result> getEncryptedString() { + Result> result = new Result>(); + Map map = new HashMap(); + map.put("key", EncryptedString.key); + map.put("iv",EncryptedString.iv); + result.setResult(map); + return result; + } + + @Override + public Result randomImage(HttpServletResponse response, String key) { + Result res = new Result(); + try { + String code = RandomUtil.randomString(BASE_CHECK_CODES,4); + String lowerCaseCode = code.toLowerCase(); + String realKey = MD5Util.MD5Encode(lowerCaseCode+key, "utf-8"); + redisUtil.set(realKey, lowerCaseCode, 60); + String base64 = RandImageUtil.generate(code); + res.setSuccess(true); + res.setResult(base64); + } catch (Exception e) { + res.error500("获取验证码出错"+e.getMessage()); + e.printStackTrace(); + } + return res; + } + + @Override + public Result mLogin(SysLoginModel sysLoginModel) { + Result result = new Result(); + String username = sysLoginModel.getUsername(); + String password = sysLoginModel.getPassword(); + + //1. 校验用户是否有效 + SysUser sysUser = sysUserService.getUserByName(username); + result = sysUserService.checkUserIsEffective(sysUser); + if(!result.isSuccess()) { + return result; + } + + //2. 校验用户名或密码是否正确 + String userpassword = PasswordUtil.encrypt(username, password, sysUser.getSalt()); + String syspassword = sysUser.getPassword(); + if (!syspassword.equals(userpassword)) { + result.error500("用户名或密码错误"); + return result; + } + + String orgCode = sysUser.getOrgCode(); + if(oConvertUtils.isEmpty(orgCode)) { + //如果当前用户无选择部门 查看部门关联信息 + List departs = sysDepartService.queryUserDeparts(sysUser.getId()); + if (departs == null || departs.size() == 0) { + result.error500("用户暂未归属部门,不可登录!"); + return result; + } + orgCode = departs.get(0).getOrgCode(); + sysUser.setOrgCode(orgCode); + this.sysUserService.updateUserDepart(username, orgCode); + } + JSONObject obj = new JSONObject(); + //用户登录信息 + obj.put("userInfo", sysUser); + + // 生成token + String token = JwtUtil.sign(username, syspassword); + // 设置超时时间 + redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); + redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000); + + //token 信息 + obj.put("token", token); + result.setResult(obj); + result.setSuccess(true); + result.setCode(200); + baseCommonService.addLog("用户名: " + username + ",登录成功[移动端]!", CommonConstant.LOG_TYPE_1, null); + return result; + } + + @Override + public Result checkCaptcha(SysLoginModel sysLoginModel) { + String captcha = sysLoginModel.getCaptcha(); + String checkKey = sysLoginModel.getCheckKey(); + if(captcha==null){ + return Result.error("验证码无效"); + } + String lowerCaseCaptcha = captcha.toLowerCase(); + String realKey = MD5Util.MD5Encode(lowerCaseCaptcha+checkKey, "utf-8"); + Object checkCode = redisUtil.get(realKey); + if(checkCode==null || !checkCode.equals(lowerCaseCaptcha)) { + return Result.error("验证码错误"); + } + return Result.OK(); + } + + @Override + public Result getRSAPublicKey() { + RSA rsa = new RSA(); + String privateKeyBase64 = rsa.getPrivateKeyBase64(); + String publicKeyBase64 = rsa.getPublicKeyBase64(); + //存到redis key为公钥 value为私钥 + redisUtil.set(publicKeyBase64, privateKeyBase64, 60L); + Result result = new Result<>(); + result.setResult(publicKeyBase64); + return result; + } + + /** + * 用户信息 + * + * @param sysUser + * @param result + * @return + */ + private Result userInfo(SysUser sysUser, Result result) { + String syspassword = sysUser.getPassword(); + String username = sysUser.getUsername(); + // 生成token + String token = JwtUtil.sign(username, syspassword); + // 设置token缓存有效时间 + redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); + redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000); + + // 获取用户部门信息 + JSONObject obj = new JSONObject(); + List departs = sysDepartService.queryUserDeparts(sysUser.getId()); + obj.put("departs", departs); + if (departs == null || departs.size() == 0) { + obj.put("multi_depart", 0); + } else if (departs.size() == 1) { + sysUserService.updateUserDepart(username, departs.get(0).getOrgCode()); + obj.put("multi_depart", 1); + } else { + //查询当前是否有登录部门 + // update-begin--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去 + SysUser sysUserById = sysUserService.getById(sysUser.getId()); + if(oConvertUtils.isEmpty(sysUserById.getOrgCode())){ + sysUserService.updateUserDepart(username, departs.get(0).getOrgCode()); + } + // update-end--Author:wangshuai Date:20200805 for:如果用戶为选择部门,数据库为存在上一次登录部门,则取一条存进去 + obj.put("multi_depart", 2); + } + obj.put("token", token); + obj.put("userInfo", sysUser); + obj.put("sysAllDictItems", sysDictService.queryAllDictItems()); + result.setResult(obj); + result.success("登录成功"); + return result; + } +} diff --git a/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/util/DateUtils.java b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/util/DateUtils.java new file mode 100644 index 00000000..87f267c6 --- /dev/null +++ b/jero-boot/jero-boot-module-system/src/main/java/com/jero/modules/system/util/DateUtils.java @@ -0,0 +1,57 @@ +package com.jero.modules.system.util; + +import cn.hutool.core.date.DateUtil; + +import java.time.*; +import java.util.Date; + +public class DateUtils extends DateUtil { + + /** + * LocalDate转Date + * @param localDate + * @return Date + */ + public static Date localDate2Date(LocalDate localDate) { + if (null == localDate) { + return null; + } + ZonedDateTime zonedDateTime = localDate.atStartOfDay(ZoneId.systemDefault()); + return Date.from(zonedDateTime.toInstant()); + } + + /** + * Date转LocalDate + * @param date + */ + public static LocalDate date2LocalDate(Date date) { + if(null == date) { + return null; + } + return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); + } + + /** + * LocalDateTime转Date + * @param localDateTime + * @return Date + */ + public static Date localDateTime2Date(LocalDateTime localDateTime){ + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId); + Instant instant = zonedDateTime.toInstant(); + return Date.from(instant); + } + + /** + * Date转LocalDateTime + * @param date + * @return LocalDateTime + */ + public static LocalDateTime date2LocalDateTime(Date date){ + Instant instant = date.toInstant(); + ZoneId zoneId = ZoneId.systemDefault(); + ZonedDateTime zonedDateTime = instant.atZone(zoneId); + return zonedDateTime.toLocalDateTime(); + } +} \ No newline at end of file