登陆注册
- 核心思路就是你登陆注册,我判断是否合法,然后创建Ticket,下发Ticket;
@Controller
public class LoginController {
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
@Autowired
UserService userService;
/**
* regLogin 注册和登陆的处理;默认会跳转到登录页面
* 该页面可以登陆也可以注册
*
* @param model 通过model传递到view,view再通过input传递next值
* @param next 指之前浏览的页面,登陆注册完就可以跳转过去
* @return
*/
@RequestMapping(path = {"/reglogin"}, method = {RequestMethod.GET})
public String regLogin(Model model,
@RequestParam(value = "next", defaultValue = "") String next) {
model.addAttribute("next", next);
return "login";
}
/**
* reg register的简写;
* 主要功能就是看看用户能不能进行注册;
* 通过map得到的ticket跳转响应的页面
*
* @param model model
* @param username 用户名
* @param password 密码
* @param next 下一个跳转的页面
* @param rememberme 是否记住
* @param response 响应
* @return
*/
@RequestMapping(path = {"/reg"}, method = RequestMethod.POST)
public String reg(Model model,
@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam(value = "next", defaultValue = "") String next,
@RequestParam(value = "rememberme", defaultValue = "false") boolean rememberme,
HttpServletResponse response) {
try {
Map<String, String> map = userService.register(username, password);
return checkTicket(model, next, rememberme, response, map);
} catch (Exception e) {
logger.error("注册异常" + e.getMessage());
return "login";
}
}
/**
* 登陆主要就是需要查看用户是不是合法的;
* 通过userService返回的map,来获取ticket,
* 然后做出相应跳转
*
* @param model
* @param username
* @param password
* @param rememberme 是否记住,决定于是否下发cookie
* @param next 登录后要跳转的页面
* @param response 通过响应来下发cookie
* @return
*/
@RequestMapping(path = {"/login"}, method = RequestMethod.POST)
public String login(Model model,
@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam(value = "rememberme", defaultValue = "false") boolean rememberme,
@RequestParam(value = "next", defaultValue = "") String next,
HttpServletResponse response) {
try {
Map<String, String> map = userService.login(username, password);
return checkTicket(model, next, rememberme, response, map);
} catch (Exception e) {
logger.error("注册异常" + e.getMessage());
return "login";
}
}
/**
* 将ticket的status置为0,也就是说不能用了,
* 这样用户就需要重新登陆了,因为本地的cookie值 已经失效;
* 因为服务器表示你这ticket不能用啊;
*
* @param ticket
* @return
*/
@RequestMapping(path = {"/logout"}, method = RequestMethod.GET)
public String logout(@CookieValue("ticket") String ticket) {
userService.logout(ticket);
return "redirect:/";
}
/**
* 检验ticket,通过map得到ticket,如果map没有ticket
* 说明这注册或者登陆不合法,返回map中的msg给view,显示错误信息;
* 拿到ticket就下发cookie,默认是session,如果点击记住我,时间就是
* 5天,然后进行跳转,如果有next就跳转next
* @param model
* @param next
* @param rememberme
* @param response
* @param map
* @return
*/
private String checkTicket(Model model, @RequestParam(value = "next", defaultValue = "") String next, @RequestParam(value = "rememberme", defaultValue = "false") boolean rememberme, HttpServletResponse response, Map<String, String> map) {
if (map.containsKey("ticket")) {
Cookie cookie = new Cookie("ticket", map.get("ticket"));
cookie.setPath("/");
if (rememberme) {
cookie.setMaxAge(3600 * 24 * 5);
}
response.addCookie(cookie);
if (StringUtils.isNotBlank(next)) {
return "redirect:" + next;
}
return "redirect:/";
} else {
model.addAttribute("msg", map.get("msg"));
return "login";
}
}
}
Ticket
把Ticket叫做T票也不是没有道理的;简单来说就是服务器会制造门票,然后下发给客户端,如果你丢了(cookie过期T票就没了),就不能访问了;服务器也可以主动的将门票设置为不合法,要求你重新登陆一遍
因此存放在服务器里的ticket有一个过期时间,本地cookie也有一个过期时间;
- 可以通过UUID来生成ticket;
/**
* 判断注册是否合法的函数
* 如果合法的话,就添加盐,盐+password通过md5算法增加保密性;
* 将用户加入数据库,添加ticket(服务器),然后下发(客户端)
* @param username
* @param password
* @return
*/
public Map<String, String> register(String username, String password) {
Map<String, String> map = new HashMap<>();
if (StringUtils.isBlank(username)) {
map.put("msg", "用户名不能为空");
}
if (StringUtils.isBlank(password)) {
map.put("msg", "密码不能为空");
}
User user = userDAO.selectByName(username);
if (user != null) {
map.put("msg", "该用户已经被注册");
return map;
}
user = new User();
user.setName(username);
user.setSalt(UUID.randomUUID().toString().substring(0, 5));
user.setHeadUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
//md5 需要重看
user.setPassword(WendaUtil.MD5(password + user.getSalt()));
userDAO.addUser(user);
String ticket = addLoginTicket(user.getId());
map.put("ticket", ticket);
return map;
}
/**
* 验证的登陆是否合法,添加ticket,下发ticket
* @param username
* @param password
* @return
*/
public Map<String, String> login(String username, String password) {
Map<String, String> map = new HashMap<>();
if (StringUtils.isBlank(username)) {
map.put("msg", "用户名不能为空");
}
if (StringUtils.isBlank(password)) {
map.put("msg", "密码不能为空");
}
User user = userDAO.selectByName(username);
if (user == null) {
map.put("msg", "用户名不存在");
return map;
}
if (!WendaUtil.MD5(password + user.getSalt()).equals(user.getPassword())) {
map.put("msg", "用户密码错误");
return map;
}
String ticket = addLoginTicket(user.getId());
map.put("ticket", ticket);
return map;
}
/**
* 增加ticket,注意日期
* @param userId
* @return
*/
public String addLoginTicket(int userId) {
LoginTicket loginTicket = new LoginTicket();
loginTicket.setUserId(userId);
Date now = new Date();
now.setTime(now.getTime() + 3600L * 24 * 1000 * 1000);
loginTicket.setExpired(now);
loginTicket.setStatus(0);
loginTicket.setTicket(UUID.randomUUID().toString().replace("-", ""));
loginTicketDao.addTicket(loginTicket);
return loginTicket.getTicket();
}
/**
* 将ticket的status置为1
*
* @param ticket
*/
public void logout(String ticket) {
loginTicketDao.updateStatus(ticket, 1);
}
Interceptor
- 拦截器需要注册
@Component
public class WendaWebConfiguration extends WebMvcConfigurerAdapter {
@Autowired
PassportInterceptor passportInterceptor;
@Autowired
LoginRequiredInterceptor loginRequiredInterceptor;
/**
* 添加拦截器,拦截器会按照顺序执行
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(passportInterceptor);
registry.addInterceptor(loginRequiredInterceptor).addPathPatterns("/user/*");
super.addInterceptors(registry);
}
}
- 拦截器针对request
@Component
public class PassportInterceptor implements HandlerInterceptor {
@Autowired
LoginTicketDao loginTicketDao;
@Autowired
UserDAO userDAO;
@Autowired
HostHolder hostHolder;
//拦截器是针对request的,有多少次request,就经历多少次拦截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ticket = null;
if (request.getCookies() != null) {
for (Cookie cookie : request.getCookies()) {
if (cookie.getName().equals("ticket")) {
ticket = cookie.getValue();
break;
}
}
}
if (ticket != null) {
LoginTicket loginTicket = loginTicketDao.selectByTicket(ticket);
if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 0) {
return true;
}
User user = userDAO.selectById(loginTicket.getUserId());
hostHolder.setUser(user);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
if (modelAndView != null) {
modelAndView.addObject("user", hostHolder.getUser());
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
hostHolder.clear();
}
}