package com.ruoyi.web.controller.system; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.framework.manager.AsyncManager; import com.ruoyi.framework.manager.factory.AsyncFactory; import com.ruoyi.framework.shiro.service.SysLoginService; import com.ruoyi.framework.shiro.util.SMSUtils; import com.ruoyi.system.service.ISysLogininforService; import com.ruoyi.system.service.ISysUserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.text.Convert; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.web.service.ConfigService; import java.io.IOException; import java.security.NoSuchAlgorithmException; /** * 登录验证 * * @author ruoyi */ @Controller public class SysLoginController extends BaseController { /** * 是否开启记住我功能 */ @Value("${shiro.rememberMe.enabled: false}") private boolean rememberMe; @Autowired private ConfigService configService; @Autowired private ISysUserService userService; @Autowired private SysLoginService loginService; @Autowired private SMSUtils smsUtils; @GetMapping("/login") public String login(HttpServletRequest request, HttpServletResponse response, ModelMap mmap) { // 如果是Ajax请求,返回Json字符串。 if (ServletUtils.isAjaxRequest(request)) { return ServletUtils.renderString(response, "{\"code\":\"1\",\"msg\":\"未登录或登录超时。请重新登录\"}"); } // 是否开启记住我 mmap.put("isRemembered", rememberMe); // 是否开启用户注册 mmap.put("isAllowRegister", Convert.toBool(configService.getKey("sys.account.registerUser"), false)); // 是否开启短信验证码登录 mmap.put("smsLoginEnabled", Convert.toBool(configService.getKey("sys.account.smsLoginEnabled"), false)); // 是否开启短信验证码前的图形验证码 mmap.put("smsCaptchaEnabled", Convert.toBool(configService.getKey("sys.account.smsCaptchaEnabled"), true)); return "login"; } /* @PostMapping("/login") @ResponseBody public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe) { UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); Subject subject = SecurityUtils.getSubject(); try { subject.login(token); return success(); } catch (AuthenticationException e) { String msg = "用户或密码错误"; if (StringUtils.isNotEmpty(e.getMessage())) { msg = e.getMessage(); } return error(msg); } }*/ /** * 发送短信验证码 */ @PostMapping("/sendSmsCode") @ResponseBody public AjaxResult sendSmsCode(String phone, String captchaCode) { try { // 获取图形验证码开关配置 boolean smsCaptchaEnabled = Convert.toBool(configService.getKey("sys.account.smsCaptchaEnabled"), true); // 如果开启了图形验证码,验证图形验证码 if (smsCaptchaEnabled) { if (StringUtils.isEmpty(captchaCode)) { return error("请输入图形验证码"); } // 验证图形验证码是否正确 if (!validateCaptcha(captchaCode)) { return error("图形验证码错误"); } } // 手机号格式验证 if (StringUtils.isEmpty(phone)) { return error("请输入手机号码"); } if (!phone.matches("^1[3-9]\\d{9}$")) { return error("请输入正确的手机号码"); } // 检查手机号是否已注册 SysUser user = userService.selectUserByPhoneNumber(phone); if (user == null) { return error("该手机号未注册"); } // 检查发送频率(防止频繁发送)- 60秒内只能发送一次 HttpServletRequest request = ServletUtils.getRequest(); Long lastSendTime = (Long) request.getSession().getAttribute("sms_last_send_time_" + phone); if (lastSendTime != null && System.currentTimeMillis() - lastSendTime < 60000) { long waitTime = 60 - (System.currentTimeMillis() - lastSendTime) / 1000; return error("请求过于频繁,请" + waitTime + "秒后再试"); } // 生成验证码 String smsCode = generateSmsCode(); // 存储验证码到Session(有效期5分钟) request.getSession().setAttribute("sms_code_" + phone, smsCode); request.getSession().setAttribute("sms_code_time_" + phone, System.currentTimeMillis()); request.getSession().setAttribute("sms_last_send_time_" + phone, System.currentTimeMillis()); // 调用短信服务发送验证码 boolean sendResult = sendSms(phone, smsCode); // boolean sendResult = true; if (sendResult) { System.out.println(smsCode); // 记录发送日志,不需要记录日志 //AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_SUCCESS, "短信验证码发送成功")); return success("验证码发送成功"); } else { return error("验证码发送失败,请稍后重试"); } } catch (Exception e) { return error("验证码发送失败,请稍后重试"); } } /** * 短信验证码登录 */ @PostMapping("/smsLogin") @ResponseBody public AjaxResult smsLogin(String phone, String smsCode, Boolean rememberMe) { try { // 验证短信验证码并获取用户 SysUser user = loginService.smsLogin(phone, smsCode); // 使用固定密码和用户名进行Shiro登录 UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginName(), "SMS_LOGIN_FIXED_PASSWORD_2025", rememberMe); Subject subject = SecurityUtils.getSubject(); subject.login(token); return success(); } catch (Exception e) { // 直接返回具体的错误信息 return error(e.getMessage()); } } private String generateSmsCode() { // 生成6位随机数字 return String.valueOf((int) ((Math.random() * 9 + 1) * 100000)); } private boolean sendSms(String phone, String smsCode) { // 实现短信发送逻辑 // 这里需要集成实际的短信服务商 boolean b = false; try { b = smsUtils.sendMsg(phone, smsCode); } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return b; } /** * 验证图形验证码 */ private boolean validateCaptcha(String captchaCode) { try { HttpServletRequest request = ServletUtils.getRequest(); // 调试:打印Session中所有与验证码相关的属性 java.util.Enumeration attributeNames = request.getSession().getAttributeNames(); while (attributeNames.hasMoreElements()) { String name = attributeNames.nextElement(); if (name.toLowerCase().contains("captcha") || name.toLowerCase().contains("kaptcha")) { Object value = request.getSession().getAttribute(name); } } // 尝试常见的验证码Session key String[] possibleKeys = { "KAPTCHA_SESSION_KEY", "kaptchaCode", com.ruoyi.common.constant.ShiroConstants.CURRENT_CAPTCHA, "captcha", "verifyCode" }; String sessionCaptcha = null; String usedKey = null; for (String key : possibleKeys) { Object value = request.getSession().getAttribute(key); if (value != null) { sessionCaptcha = value.toString(); usedKey = key; break; } } if (StringUtils.isEmpty(sessionCaptcha)) { return false; } // 验证码比较(忽略大小写) boolean isValid = sessionCaptcha.equalsIgnoreCase(captchaCode); // 验证后清除Session中的验证码,防止重复使用 if (isValid && usedKey != null) { request.getSession().removeAttribute(usedKey); } return isValid; } catch (Exception e) { return false; } } @GetMapping("/unauth") public String unauth() { return "error/unauth"; } }