主要优化以下三个方面:
一、redis存储验证码
1.打开RedisKeyUtil
private static final String PREFIX_KAPTCHA = "kaptcha";//定义前缀,验证码叫 kaptcha
// 获取登录验证码
public static String getKaptchaKey(String owner) {//以字符串临时标识用户,很快会过期
return PREFIX_KAPTCHA + SPLIT + owner;//要识别出验证码属于哪个用户,不能靠userid,因为验证码 是辅助用户登录的,显示验证码的时候还没有登录
}
2.验证码是在登录功能里用到的,在logincontroller中使用
@RequestMapping(path = "/kaptcha", method = RequestMethod.GET)
public void getKaptcha(HttpServletResponse response/*, HttpSession session*/) {//原来是把验证码传到session中,现在将其注释掉
// 生成验证码
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);
// 将验证码存入session
// session.setAttribute("kaptcha", text);
// 验证码的归属(为了实现将验证码存入Redis,需要构造key,而key需要验证码的归属)
String kaptchaOwner = CommunityUtil.generateUUID();//创建随机字符串并存入cookie
Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
cookie.setMaxAge(60);//设置cookie的失效时间 60s
cookie.setPath(contextPath);//有效路径,整个项目下都有效
response.addCookie(cookie);//添加到客户端
// 将验证码存入Redis
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
redisTemplate.opsForValue().set(redisKey, text, 60, TimeUnit.SECONDS);//text是验证码的文本,60后面的TimeUnit.SECONDS 是其单位秒。存入了redis
// 将突图片输出给浏览器
response.setContentType("image/png");
try {
OutputStream os = response.getOutputStream();
ImageIO.write(image, "png", os);
} catch (IOException e) {
logger.error("响应验证码失败:" + e.getMessage());
}
}
@RequestMapping(path = "/login", method = RequestMethod.POST)
public String login(String username, String password, String code, boolean rememberme,
Model model, /*HttpSession session, */HttpServletResponse response,//因为从redis中取,所以要注释掉/*HttpSession session, */
@CookieValue("kaptchaOwner") String kaptchaOwner) {//声明变量String kaptchaOwner,用来接收值 kaptchaOwner
// 检查验证码
// String kaptcha = (String) session.getAttribute("kaptcha");
String kaptcha = null;//默认kaptcha为null
if (StringUtils.isNotBlank(kaptchaOwner)) {//判断key不为空,即数据没有失效
String redisKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);//redis 的key 需要kaptchaOwner,kaptchaOwner要从Cookie中取
kaptcha = (String) redisTemplate.opsForValue().get(redisKey);//根据redisKey,从redis中取值,将object强制转型为String
}
if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
model.addAttribute("codeMsg", "验证码不正确!");
return "/site/login";
}
3.测试:
在首页的登录页面
可以登录成功,说明优化后正确
二、使用redis存储登录凭证
1.在RedisKeyUtil中
private static final String PREFIX_TICKET = "ticket";
private static final String PREFIX_USER = "user";
// 登录的凭证
public static String getTicketKey(String ticket) {
return PREFIX_TICKET + SPLIT + ticket;
}
2.通过废除掉LoginTicketMapper
3.重构
在userservice中,
// 生成登录凭证
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());
redisTemplate.opsForValue().set(redisKey, loginTicket);//每个redisKey,对应一个loginTicket,这样redis 可以把对象序列化一个字符串
map.put("ticket", loginTicket.getTicket());
return map;
}
public LoginTicket findLoginTicket(String ticket) {
// return loginTicketMapper.selectByTicket(ticket);
String redisKey = RedisKeyUtil.getTicketKey(ticket);
return (LoginTicket) redisTemplate.opsForValue().get(redisKey);
}
测试:
可以登录成功,且可以正常
登录凭证是不会删的,只是会修改状态
三、使用redis缓存用户信息
缓存 表示过一会儿要把数据给删掉。
因为user表还在,所以这里只是临时 存一下,
该方法访问非常频繁。
每个user都缓存到redis里面
在userservice中,更改:
public User findUserById(int id) {
// return userMapper.selectById(id);
User user = getCache(id);
if (user == null) {
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;
}
}
其中,getCache为
// 1.优先从缓存中取值
private User getCache(int userId) {//从缓存中取到User
String redisKey = RedisKeyUtil.getUserKey(userId);
return (User) redisTemplate.opsForValue().get(redisKey);
}
// 2.取不到时初始化缓存数据,取到则直接返回
private User initCache(int userId) {
User user = userMapper.selectById(userId);//从mysql中查到数据
String redisKey = RedisKeyUtil.getUserKey(userId);//得到user的key
redisTemplate.opsForValue().set(redisKey, user, 3600, TimeUnit.SECONDS);//过期时间3600s
return user;
}
// 3.数据变更时清除缓存数据
private void clearCache(int userId) {
String redisKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.delete(redisKey);
}
}
在RedisKeyUtil中添加
// 用户
public static String getUserKey(int userId) {
return PREFIX_USER + SPLIT + userId;
}
在user services中
public int updateHeader(int userId, String headerUrl) {
// return userMapper.updateHeader(userId, headerUrl);
int rows = userMapper.updateHeader(userId, headerUrl);//更新并返回 更新的行数
clearCache(userId);//更新以后,清除缓存
return rows;
}
测试:
将来会使用 一个小工具来测试性能到底优化了多少。