在util包
下的RedisKeyUtil类
下为其生成Key。
public class RedisKeyUtil {
private static final String PREFIX_KAPTCHA = "kaptcha"; //验证码
//登录验证码
//oener验证码的拥有者,临时凭证
public static String getKaptchaKey(String owner) {
return PREFIX_KAPTCHA + SPLIT + owner;
}
}
在controller包
下的LoginController类
下的getKaptcha()方法
对其进行优化:不再将验证码存入session
中,而将其存入Redis
中。需要服务端临时给客户端颁发一个凭证来判断验证码的归属,因此生成一个随机字符串发送给客户端并用cookie
保存起来。
@RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
//生成验证码的方法
public void getKaptcha(HttpServletResponse response/*, HttpSession session*/) {
//生成验证码
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);
// 将验证码存入session
//session.setAttribute("kaptcha", text);
//验证码的归属
//临时给客户端颁发一个凭证,生成一个随机字符串
String kaptchaOwner = CommunityUtil.generateUUID();
Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
cookie.setMaxAge(60); //cookie的生存时间
cookie.setPath(contextPath); //cookie的有效路径
response.addCookie(cookie); //发送给客户端
//将验证码存入redis
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
//验证码存的是一个字符串,用redis的string类型
redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);
// 将图片输出给浏览器
response.setContentType("image/png");
try {
OutputStream os = response.getOutputStream();
ImageIO.write(image, "png", os);
} catch (IOException e) {
logger.error("响应验证码失败:", e.getMessage());
}
}
在controller包
下的LoginController类
下的login()方法
对其进行优化:不再从session
中获取验证码,转而从cookie
中获取临时颁发的凭证,然后拼接获得Redis
的Key
,从Redis
中获取验证码。
@RequestMapping(path = "/login", method = RequestMethod.POST)
public String login(String username, String password, String code, boolean rememberme,
Model model, /*HttpSession session,*/ HttpServletResponse response
, @CookieValue("kaptchaOwner") String kaptchaOwner) {
//从session中获取验证码,转为String类型
//String kaptcha = (String) session.getAttribute("kaptcha");
//1.检查验证码
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";
}
//2.检查账号,密码
int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
Map<String, Object> map = userService.login(username, password, expiredSeconds);
if (map.containsKey("ticket")) {
//登录成功,并重定向到首页
Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
cookie.setPath(contextPath);
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";
}
}
在util包
下的RedisKeyUtil类
下为其生成Key。
public class RedisKeyUtil {
private static final String PREFIX_TICKET = "ticket"; //登录凭证
//登录凭证
public static String getTicketKey(String ticket) {
return PREFIX_TICKET + SPLIT + ticket;
}
}
在dao包
下的LoginTicketMapper类
中添加注解@Deprecated
用来标识此类不建议使用。
//登录实现
public Map<String, Object> login(String username, String password, int expiredSeconds) {
Map<String, Object> map = new HashMap<>();
//空值处理
if (StringUtils.isBlank(username)) {
map.put("usernameMsg", "账号不能为空!");
return map;
}
if (StringUtils.isBlank(password)) {
map.put("passwordMsg", "密码不能为空!");
return map;
}
//验证账号
User user = userMapper.selectByName(username);
if (user == null) {
map.put("usernameMsg", "该账号不存在!");
return map;
}
//验证状态
if (user.getStatus() == 0) {
map.put("usernameMsg", "该账号未激活!");
return map;
}
//验证密码
//先把传入的明文密码,按照一定的规则加密,再与数据库中的加密密码作比较
password = CommunityUtil.md5(password + user.getSalt());
if (!user.getPassword().equals(password)) {
map.put("passwordMsg", "密码不正确!");
return map;
}
//生成登录凭证
LoginTicket loginTicket = new LoginTicket();
loginTicket.setUserId(user.getId());
loginTicket.setTicket(CommunityUtil.generateUUID());
loginTicket.setStatus(0);
loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));
//loginTicketMapper.insertLoginTicket(loginTicket);
String redisKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
//redis会将loginTicket对象序列成为JSON格式的字符串进行保存
redisTemplate.opsForValue().set(redisKey, loginTicket);
map.put("ticket", loginTicket.getTicket());
return map;
}
//退出登录实现
public void logout(String ticket) {
//1表示无效
//loginTicketMapper.updateStatus(ticket, 1);
String redisKey = RedisKeyUtil.getTicketKey(ticket);
LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(redisKey);
loginTicket.setStatus(1);
redisTemplate.opsForValue().set(redisKey, loginTicket);
}
//查找凭证
public LoginTicket findLoginTicket(String ticket) {
//return loginTicketMapper.selectByTicket(ticket);
String redisKey = RedisKeyUtil.getTicketKey(ticket);
return (LoginTicket) redisTemplate.opsForValue().get(redisKey);
}
在util包
下的RedisKeyUtil类
下为其生成Key。
public class RedisKeyUtil {
private static final String PREFIX_USER = "user";
//用户
public static String getUserKey(int userId) {
return PREFIX_USER + SPLIT + userId;
}
}
在service包
下的UserService类
下实现getCache()方法
优先从缓存中取值、initCache()方法
如果取不到时就初始化缓存数据、clearCache()方法
当数据变更时清除缓存数据。
// 1.优先从缓存中取值
private User getCache(int userId) {
String redisKey = RedisKeyUtil.getUserKey(userId);
return (User) redisTemplate.opsForValue().get(redisKey);
}
// 2.如果取不到时就初始化缓存数据
private User initCache(int userId) {
User user = userMapper.selectById(userId);
String redisKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);
return user;
}
// 3.当数据变更时清除缓存数据
private void clearCache(int userId) {
String redisKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.delete(redisKey);
}
//根据userId查询用户的具体信息
public User findUserById(int id) {
//return userMapper.selectById(id);
User user = getCache(id);
if (user == null) {
//如果user为空,说明此时从缓存中取不到值,因此进行初始化
user = initCache(id);
}
return user;
}
//激活用户
public int activation(int userId, String code) {
User user = userMapper.selectById(userId);
if (user.getStatus() == 1) { //重复激活
return ACTIVATION_REPEAT;
} else if (user.getActivationCode().equals(code)) { //激活成功
userMapper.updateStatus(userId, 1);
clearCache(userId);
return ACTIVATION_SUCCESS;
} else {
return ACTIVATION_FAILURE; //激活失败
}
}
//更新用户的头像
public int updateHeader(int userId, String headerUrl) {
//return userMapper.updateHeader(userId, headerUrl);
int rows = userMapper.updateHeader(userId, headerUrl);
clearCache(userId);
return rows;
}