---------------------------------------------------------------------------------------------------------------------------------
1.RedisKeyUtil:
public class RedisKeyUtil {
private static final String SPLIT=":";
private static final String PREFIX_ENTITY_LIKE="like:entity";
private static final String PREFIX_USER_LIKE="like:user";
private static final String PREFIX_FOLLOWEE="followee";
private static final String PREFIX_FOLLOWER="follower";
private static final String PREFIX_KAPTCHA="kaptcha";
private static final String PREFIX_TICKET="ticket";
private static final String PREFIX_USER="user";
//某个实体的赞
//like:entity:entityType:entityId->set(userId)
public static String getEntityLikeKey(int entityType,int entityId){
return PREFIX_ENTITY_LIKE+SPLIT+entityType+SPLIT+entityId;
}
//某个用户的赞
//like:user:userId->int
public static String getUserLikeKey(int userId){
return PREFIX_USER_LIKE+SPLIT+userId;
}
//某个用户关注的实体
//followee:userId:entityType->zset(entityId,now)
public static String getFolloweeKey(int userId,int entityType){
return PREFIX_FOLLOWEE+SPLIT+userId+SPLIT+entityType;
}
//某个实体拥有的粉丝
//follower:entityType:entityId->zset(userId,now)
public static String getFollowerKey(int entityType,int entityId){
return PREFIX_FOLLOWER+SPLIT+entityType+SPLIT+entityId;
}
//登录验证码
public static String getKaptchaKey(String owner){
return PREFIX_KAPTCHA+SPLIT+owner;
}
//登录凭证
public static String getTicketKey(String ticket){
return PREFIX_TICKET+SPLIT+ticket;
}
//用户
public static String getUserKey(int userId){
return PREFIX_USER+SPLIT+userId;
}
}
2.LoginController:
import com.google.code.kaptcha.Producer;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.UserServiceImpl;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.CommunityUtil;
import com.nowcoder.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.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import javax.imageio.ImageIO;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
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 UserServiceImpl userService;
@Autowired
@Qualifier("kaptchaProducer")
private Producer kaptchaProducer;
@Autowired
@Qualifier("redisTemplates")
private RedisTemplate redisTemplate;
@Value("${server.servlet.context-path}")
private String contextPath;
@GetMapping("/register")
public String getRegisterPage(){
return "/site/register";
}
@GetMapping("/login")
public String getLoginPage(){
return "/site/login";
}
@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";
}
}
// http://localhost:8080/community/activation/101/code
@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";
}
@GetMapping("/kaptcha")
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.setPath(contextPath);
cookie.setMaxAge(60);
response.addCookie(cookie);
//将验证码存入Redis
String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
redisTemplate.opsForValue().set(kaptchaKey,text,60, TimeUnit.SECONDS);
//将图片输出给浏览器
response.setContentType("image/jpg");
try {
OutputStream stream = response.getOutputStream();
ImageIO.write(image,"jpg",stream);
} catch (IOException e) {
logger.error("响应验证码失败:"+e.getMessage());
}
}
@PostMapping("/login")
public String login(String username, String password, String code, boolean rememberme,
Model model,/*HttpSession session,*/HttpServletResponse response,@CookieValue("kaptchaOwner") String kaptchaOwner){
//String kaptcha = (String) session.getAttribute("kaptcha");
//检查验证码
String kaptcha=null;
if (!StringUtils.isBlank(kaptchaOwner)){
String kaptchaKey = RedisKeyUtil.getKaptchaKey(kaptchaOwner);
kaptcha= (String) redisTemplate.opsForValue().get(kaptchaKey);
}
if (StringUtils.isBlank(kaptcha)||StringUtils.isBlank(code)||!kaptcha.equalsIgnoreCase(code)){
model.addAttribute("codeMsg","验证码不正确!");
return "/site/login";
}
//检查账号、密码
int expiredSeconds=rememberme ? REMMERBER_EXPIRED_SECONDS : DEFAULT_EXPIRE_SECONDS;
Map<String, Object> map= userService.login(username, password, (long) 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";
}
}
@GetMapping("/logout")
public String logout(@CookieValue("ticket") String ticket){
userService.logout(ticket);
return "redirect:/login";
}
}
3.LoginTicketMapper:不再使用[@Deprecated]
import com.nowcoder.community.entity.LoginTicket;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
@Deprecated
public interface LoginTickerMapper {
LoginTicket selectByTicket(String ticket);
int insertLoginTicket(LoginTicket loginTicket);
int updateStatus(@Param("ticket") String ticket, @Param("status") int status);
}
4.UserService:
import com.nowcoder.community.entity.LoginTicket;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.mapper.UserMapper;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.CommunityUtil;
import com.nowcoder.community.util.MailClient;
import com.nowcoder.community.util.RedisKeyUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Service
public class UserServiceImpl implements UserService, CommunityConstant {
@Autowired
private UserMapper userMapper;
@Autowired
private MailClient mailClient;
@Autowired
private TemplateEngine templateEngine;
@Autowired
@Qualifier("redisTemplates")
private RedisTemplate redisTemplate;
/**
* @Autowired
* private LoginTickerMapper loginTickerMapper;
*/
@Value("${community.path.domain}")
private String domain;
@Value("${server.servlet.context-path}")
private String contextPath;
public User selectById(int id) {
//return userMapper.selectById(id);
User user = getCache(id);
if (user==null){
user = initCache(id);
}
return user;
}
@Override
public User selectByName(String username) {
return userMapper.selectByName(username);
}
@Override
public User selectByEmail(String email) {
return userMapper.selectByEmail(email);
}
@Override
public int insertUser(User user) {
return userMapper.insertUser(user);
}
@Override
public int updateStatus(int id, int status) {
return userMapper.updateStatus(id, status);
}
@Override
public int updateHeader(int id, String headerUrl) {
//return userMapper.updateHeader(id, headerUrl);
int rows = userMapper.updateHeader(id, headerUrl);
clearCache(id);
return rows;
}
@Override
public int updatePassword(int id, String password) {
//return userMapper.updatePassword(id, password);
int rows = userMapper.updatePassword(id, password);
clearCache(id);
return rows;
}
/**
* 注册账号
* @param user
* @return
*/
public Map<String, Object> register(User user) {
Map<String, Object> map = new HashMap<>();
// 空值处理
if (user == null) {
throw new IllegalArgumentException("参数不能为空!");
}
if (StringUtils.isBlank(user.getUsername())) {
map.put("usernameMsg", "账号不能为空!");
return map;
}
if (StringUtils.isBlank(user.getPassword())) {
map.put("passwordMsg", "密码不能为空!");
return map;
}
if (StringUtils.isBlank(user.getEmail())) {
map.put("emailMsg", "邮箱不能为空!");
return map;
}
// 验证账号
User u = userMapper.selectByName(user.getUsername());
if (u != null) {
map.put("usernameMsg", "该账号已存在!");
return map;
}
// 验证邮箱
u = userMapper.selectByEmail(user.getEmail());
if (u != null) {
map.put("emailMsg", "该邮箱已被注册!");
return map;
}
// 注册用户
user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
user.setPassword(CommunityUtil.md5(user.getPassword() + user.getSalt()));
user.setType(0);
user.setStatus(0);
user.setActivationCode(CommunityUtil.generateUUID());
user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
user.setCreateTime(new Date());
userMapper.insertUser(user);
// 激活邮件
Context context = new Context();
context.setVariable("email", user.getEmail());
// http://localhost:8080/community/activation/101/code
String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
context.setVariable("url", url);
String content = templateEngine.process("/mail/activation", context);
mailClient.sendMail(user.getEmail(), "激活账号", content);
return map;
}
/**
* 激活账号
* @param userId
* @param code
* @return
*/
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;
}
}
/**
* 登录功能
* @param username
* @param password
* @param expiredSeconds
* @return
*/
public Map<String,Object> login(String username,String password,Long 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*1000L));
//loginTickerMapper.insertLoginTicket(loginTicket);
String ticketKey = RedisKeyUtil.getTicketKey(loginTicket.getTicket());
redisTemplate.opsForValue().set(ticketKey,loginTicket);
map.put("ticket",loginTicket.getTicket());
return map;
}
/**
* 退出登录
* @param ticket
*/
public void logout(String ticket){
//loginTickerMapper.updateStatus(ticket,1);
String ticketKey = RedisKeyUtil.getTicketKey(ticket);
LoginTicket loginTicket = (LoginTicket) redisTemplate.opsForValue().get(ticketKey);
loginTicket.setStatus(1);
redisTemplate.opsForValue().set(ticketKey,loginTicket);
}
//1.优先从缓存中取值
private User getCache(int userId){
String userKey = RedisKeyUtil.getUserKey(userId);
User user = (User) redisTemplate.opsForValue().get(userKey);
return user;
}
//2.取不到时初始化缓存
private User initCache(int userId){
User user = userMapper.selectById(userId);
String userKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.opsForValue().set(userKey,user,3600, TimeUnit.SECONDS);
return user;
}
//3.数据变更时清除缓存
private void clearCache(int userId){
String userKey = RedisKeyUtil.getUserKey(userId);
redisTemplate.delete(userKey);
}
}