Echo/src/main/java/com/greate/community/controller/LoginController.java
2021-07-20 00:11:08 +08:00

324 lines
12 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.greate.community.controller;
import com.google.code.kaptcha.Producer;
import com.greate.community.entity.User;
import com.greate.community.service.UserService;
import com.greate.community.util.CommunityConstant;
import com.greate.community.util.CommunityUtil;
import com.greate.community.util.RedisKeyUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 登录、登出、注册
*/
@Controller
public class LoginController implements CommunityConstant {
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
@Autowired
private UserService userService;
@Autowired
private Producer kaptchaProducer;
@Autowired
private RedisTemplate redisTemplate;
@Value("${server.servlet.context-path}")
private String contextPath;
/**
* 进入注册界面
* @return
*/
@GetMapping("/register")
public String getRegisterPage() {
return "site/register";
}
/**
* 进入登录界面
* @return
*/
@GetMapping("/login")
public String getLoginPage() {
return "site/login";
}
/**
* 进入重置密码界面
*/
@GetMapping("/resetPwd")
public String getResetPwdPage() {
return "site/reset-pwd";
}
/**
* 注册用户
* @param model
* @param user
* @return
*/
@PostMapping("/register")
public String register(Model model, User user) {
Map<String, Object> map = userService.register(user);
if (map == null || map.isEmpty()) {
model.addAttribute("msg", "注册成功, 我们已经向您的邮箱发送了一封激活邮件,请尽快激活!");
model.addAttribute("target", "/index");
return "/site/operate-result";
} else {
model.addAttribute("usernameMsg", map.get("usernameMsg"));
model.addAttribute("passwordMsg", map.get("passwordMsg"));
model.addAttribute("emailMsg", map.get("emailMsg"));
return "/site/register";
}
}
/**
* 激活用户
* @param model
* @param userId
* @param code 激活码
* @return
* http://localhost:8080/echo/activation/用户id/激活码
*/
@GetMapping("/activation/{userId}/{code}")
public String activation(Model model, @PathVariable("userId") int userId,
@PathVariable("code") String code) {
int result = userService.activation(userId, code);
if (result == ACTIVATION_SUCCESS) {
model.addAttribute("msg", "激活成功, 您的账号已经可以正常使用!");
model.addAttribute("target", "/login");
}
else if (result == ACTIVATION_REPEAT) {
model.addAttribute("msg", "无效的操作, 您的账号已被激活过!");
model.addAttribute("target", "/index");
}
else {
model.addAttribute("msg", "激活失败, 您提供的激活码不正确!");
model.addAttribute("target", "/index");
}
return "/site/operate-result";
}
/**
* 生成验证码, 并存入 Redis
* @param response
*/
@GetMapping("/kaptcha")
public void getKaptcha(HttpServletResponse response) {
// 生成验证码
String text = kaptchaProducer.createText(); // 生成随机字符
System.out.println("验证码:" + text);
BufferedImage image = kaptchaProducer.createImage(text); // 生成图片
// 验证码的归属者
String kaptchaOwner = CommunityUtil.generateUUID();
Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
cookie.setMaxAge(60);
cookie.setPath(contextPath);
response.addCookie(cookie);
// 将验证码存入 redis
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);
// 将图片输出给浏览器
response.setContentType("image/png");
try {
ServletOutputStream os = response.getOutputStream();
ImageIO.write(image, "png", os);
} catch (IOException e) {
logger.error("响应验证码失败", e.getMessage());
}
}
/**
* 验证用户输入的图片验证码是否和redis中存入的是否相等
*
* @param kaptchaOwner 从 cookie 中取出的 kaptchaOwner
* @param checkCode 用户输入的图片验证码
* @return 失败则返回原因, 验证成功返回 "",
*/
private String checkKaptchaCode(String kaptchaOwner, String checkCode) {
if (StringUtils.isBlank(checkCode)) {
return "未发现输入的图片验证码";
}
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
String kaptchaValue = (String) redisTemplate.opsForValue().get(redisKey);
if (StringUtils.isBlank(kaptchaValue)) {
return "图片验证码过期";
} else if (!kaptchaValue.equalsIgnoreCase(checkCode)) {
return "图片验证码错误";
}
return "";
}
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @param code 验证码
* @param rememberMe 是否记住我(点击记住我后,凭证的有效期延长)
* @param model
* @param kaptchaOwner 从 cookie 中取出的 kaptchaOwner
* @param response
* @return
*/
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("code") String code,
@RequestParam(value = "rememberMe", required = false) boolean rememberMe,
Model model, HttpServletResponse response,
@CookieValue("kaptchaOwner") String kaptchaOwner) {
// 检查验证码
String kaptcha = null;
if (StringUtils.isNotBlank(kaptchaOwner)) {
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
kaptcha = (String) redisTemplate.opsForValue().get(redisKey);
}
if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
model.addAttribute("codeMsg", "验证码错误");
return "/site/login";
}
// 凭证过期时间(是否记住我)
int expiredSeconds = rememberMe ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
// 验证用户名和密码
Map<String, Object> map = userService.login(username, password, expiredSeconds);
if (map.containsKey("ticket")) {
// 账号和密码均正确,则服务端会生成 ticket浏览器通过 cookie 存储 ticket
Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
cookie.setPath(contextPath); // cookie 有效范围
cookie.setMaxAge(expiredSeconds);
response.addCookie(cookie);
return "redirect:/index";
}
else {
model.addAttribute("usernameMsg", map.get("usernameMsg"));
model.addAttribute("passwordMsg", map.get("passwordMsg"));
return "/site/login";
}
}
/**
* 用户登出
* @param ticket 设置凭证状态为无效
* @return
*/
@GetMapping("/logout")
public String logout(@CookieValue("ticket") String ticket) {
userService.logout(ticket);
SecurityContextHolder.clearContext();
return "redirect:/login";
}
/**
* 重置密码
*/
@PostMapping("/resetPwd")
@ResponseBody
public Map<String, Object> resetPwd(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("emailVerifyCode") String emailVerifyCode,
@RequestParam("kaptchaCode") String kaptcha,
Model model,
@CookieValue("kaptchaOwner") String kaptchaOwner) {
Map<String, Object> map = new HashMap<>(4);
// 检查图片验证码
String kaptchaCheckRst = checkKaptchaCode(kaptchaOwner, kaptcha);
if (StringUtils.isNotBlank(kaptchaCheckRst)) {
map.put("status", "1");
map.put("errMsg", kaptchaCheckRst);
}
// 检查邮件验证码
String emailVerifyCodeCheckRst = checkRedisResetPwdEmailCode(username, emailVerifyCode);
if (StringUtils.isNotBlank(emailVerifyCodeCheckRst)) {
map.put("status", "1");
map.put("errMsg", emailVerifyCodeCheckRst);
}
// 执行重置密码操作
Map<String, Object> stringObjectMap = userService.doResetPwd(username, password);
String usernameMsg = (String) stringObjectMap.get("errMsg");
if (StringUtils.isBlank(usernameMsg)) {
map.put("status", "0");
map.put("msg", "重置密码成功!");
map.put("target", "/login");
}
return map;
}
/**
* 发送邮件验证码(用于重置密码)
*
* @param kaptchaOwner 从 cookie 中取出的 kaptchaOwner
* @param kaptcha 用户输入的图片验证码
* @param username 用户输入的需要找回的账号
*/
@PostMapping("/sendEmailCodeForResetPwd")
@ResponseBody
public Map<String, Object> sendEmailCodeForResetPwd(Model model, @CookieValue("kaptchaOwner") String kaptchaOwner,
@RequestParam("kaptcha") String kaptcha,
@RequestParam("username") String username) {
Map<String, Object> map = new HashMap<>(3);
// 检查图片验证码
String kaptchaCheckRst = checkKaptchaCode(kaptchaOwner, kaptcha);
if (StringUtils.isNotBlank(kaptchaCheckRst)) {
map.put("status", "1");
map.put("errMsg", kaptchaCheckRst);
}
Map<String, Object> stringObjectMap = userService.doSendEmailCode4ResetPwd(username);
String usernameMsg = (String) stringObjectMap.get("errMsg");
if (StringUtils.isBlank(usernameMsg)) {
map.put("status", "0");
map.put("msg", "已经往您的邮箱发送了一封验证码邮件, 请查收!");
}
return map;
}
/**
* 检查 邮件 验证码
*
* @param username 用户名
* @param checkCode 用户输入的图片验证码
* @return 验证成功 返回"", 失败则返回原因
*/
private String checkRedisResetPwdEmailCode(String username, String checkCode) {
if (StringUtils.isBlank(checkCode)) {
return "未发现输入的邮件验证码";
}
final String redisKey = "EmailCode4ResetPwd:" + username;
String emailVerifyCodeInRedis = (String) redisTemplate.opsForValue().get(redisKey);
if (StringUtils.isBlank(emailVerifyCodeInRedis)) {
return "邮件验证码已过期";
} else if (!emailVerifyCodeInRedis.equalsIgnoreCase(checkCode)) {
return "邮件验证码错误";
}
return "";
}
}