SpringBoot(7)在SpringBoot实现基于Token的身份验证

匿名 (未验证) 提交于 2019-12-03 00:21:02

基于Token的身份验证用来替代传统的cookie+session身份验证方法中的session。

基于Token的身份验证流程如下。那在SpringBoot中怎么去实现呢?

  • 客户端使用用户名跟密码请求登录
  • 服务端收到请求,去验证用户名与密码
  • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端
  • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里
  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
  • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

首先前三步是一起的,DAO层就不写了,就是设计一个相关的表用于存储,那在service层和Controller层对应的实现如下,主体就是标记红色的那一块:

package com.springboot.springboot.service;  import com.springboot.springboot.dao.loginTicketsDAO; import com.springboot.springboot.dao.userDAO; import com.springboot.springboot.model.User; import com.springboot.springboot.model.loginTickets; import com.springboot.springboot.utils.WendaUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils;  import java.util.*;  @Service public class userService {     Random random = new Random();      @Autowired     userDAO uDAO;      @Autowired     loginTicketsDAO lTicketsDAO;      //注册      public Map<String,String > register(String userName,String password){          Map<String,String> map = new HashMap<String, String >();         if(StringUtils.isEmpty(userName)){             map.put("msg", "用户名不能为空");             return map;         }          if(StringUtils.isEmpty(password)){             map.put("msg","密码不能为空");             return  map;         }          User user = uDAO.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.setHead_url(String.format("http://images.nowcoder.com/head/%dt.png", random.nextInt(1000)));         user.setPassword(WendaUtil.MD5(password + user.getSalt()));         uDAO.addUser(user);          //注册完成下发ticket之后自动登录         String ticket = addLoginTicket(user.getId());         map.put("ticket",ticket);          return map;     }      //登陆     public Map<String,Object> login(String username, String password){         Map<String,Object> map = new HashMap<String,Object>();         if(StringUtils.isEmpty(username)){             map.put("msg","用户名不能为空");             return map;         }          if(StringUtils.isEmpty(password)){             map.put("msg","密码不能为空");             return map;         }          User user = uDAO.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;     }      public String addLoginTicket(int user_id){         loginTickets ticket = new loginTickets();         ticket.setUserId(user_id);         Date nowDate = new Date();         nowDate.setTime(3600*24*100 + nowDate.getTime());         ticket.setExpired(nowDate);         ticket.setStatus(0);         ticket.setTicket(UUID.randomUUID().toString().replaceAll("_",""));         lTicketsDAO.addTicket(ticket);         return ticket.getTicket();      }      public User getUser(int id){         return uDAO.selectById(id);     } }
package com.springboot.springboot.controller;  import com.springboot.springboot.service.userService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam;  import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import java.util.Map;  //首页的登录功能 @Controller public class registerController {     private static final Logger logger = LoggerFactory.getLogger(registerController.class);      @Autowired     userService uService;      //注册     @RequestMapping(path = {"/reg/"}, method = {RequestMethod.POST})     public String reg(Model model, @RequestParam("username") String username, @RequestParam("password") String password,                       @RequestParam(value = "rememberme",defaultValue = "false") boolean rememberme,                       HttpServletResponse response) {         try {             Map<String, String> map = uService.register(username, password);             if (map.containsKey("ticket")) {                 Cookie cookie = new Cookie("ticket",map.get("ticket"));                 cookie.setPath("/");                 response.addCookie(cookie);                 return "redirect:/";             }else{                 model.addAttribute("msg", map.get("msg"));                 return "login";             }         } catch (Exception e) {             logger.error("注册异常" + e.getMessage());             return "login";         }     }      @RequestMapping(path = {"/reglogin"}, method = {RequestMethod.GET})     public String register(Model model) {         return "login";     }      //登陆     @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,                         HttpServletResponse response){         try{             Map<String,Object> map = uService.login(username,password);             if(map.containsKey("ticket")){                Cookie cookie = new Cookie("ticket",map.get("ticket").toString());                cookie.setPath("/");           //可在同一应用服务器内共享cookie                response.addCookie(cookie);                return "redirect:/";             }              else{                 model.addAttribute("msg",map.get("msg"));                 return "login";             }         }catch (Exception e){             logger.error("登陆异常" + e.getMessage());             return "login";         }     }  }

从上面能够清楚的看出来,用户先去请求注册或者是登陆,然后服务器去验证他的用户名和密码

验证成功后会下发一个Token,我这里是ticket,客户端收到ticket之后呢会把ticket存在Cookie中,如下图,我登录成功之后会有一个与当前用户对应的ticket


每次访问服务器资源的时候需要带着这个ticket,然后怎么判断是否有呢?就要用拦截器来实现,用拦截器去判断这个ticket当前的状态是什么样的?有没有过期?身份状态是不是有效的?然后根据这个来判断应该赋予什么样的权限?当验证成功之后就把ticket对应的用户的通过下面一段发送给freemaker的上下文,实现页面的正常的渲染

 @Override     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {         //就是为了能够在渲染之前所有的freemaker模板能够访问这个对象user,就是在所有的controller渲染之前将这个user加进去         if(modelAndView != null){             //这个其实就和model.addAttribute一样的功能,就是把这个变量与前端视图进行交互 //就是与header.html页面的user对应             modelAndView.addObject("user",hostHolder.getUser());         }     }
完整的如下:
package com.springboot.springboot.interceptor;  import com.springboot.springboot.dao.loginTicketsDAO; import com.springboot.springboot.dao.userDAO; import com.springboot.springboot.model.HostHolder; import com.springboot.springboot.model.User; import com.springboot.springboot.model.loginTickets; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;  import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Date;  /**  * 拦截器  * @ 用来判断用户的  *1. 当preHandle方法返回false时,从当前拦截器往回执行所有拦截器的afterCompletion方法,再退出拦截器链。也就是说,请求不继续往下传了,直接沿着来的链往回跑。  2.当preHandle方法全为true时,执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的Controller。然后进入拦截器链,运  行所有拦截器的postHandle方法,完后从最后一个拦截器往回执行所有拦截器的afterCompletion方法.  */  //@component (把普通pojo实例化到spring容器中,相当于配置文件中的 @Component public class PassportInterceptor implements HandlerInterceptor{      @Autowired     loginTicketsDAO lTicketsDAO;      @Autowired     userDAO uDAO;      @Autowired     HostHolder hostHolder;      //判断然后进行用户拦截     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         String tickets = null;         if(request.getCookies() != null){             for(Cookie cookie : request.getCookies()){                 if(cookie.getName().equals("ticket")){                     tickets = cookie.getValue();                     break;                 }             }         }          if(tickets != null ){             loginTickets loginTickets  = lTicketsDAO.selectByTicket(tickets);             if(loginTickets == null || loginTickets.getExpired().before(new Date()) || loginTickets.getStatus() != 0){                 return true;             }              User user = uDAO.selectById(loginTickets.getUserId());             hostHolder.setUser(user);         }         return true;     }      @Override     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {         //就是为了能够在渲染之前所有的freemaker模板能够访问这个对象user,就是在所有的controller渲染之前将这个user加进去         if(modelAndView != null){             //这个其实就和model.addAttribute一样的功能,就是把这个变量与前端视图进行交互 //就是与header.html页面的user对应             modelAndView.addObject("user",hostHolder.getUser());         }     }      @Override     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {         hostHolder.clear();   //当执行完成之后呢需要将变量清空     } }

当用户登出的时候就把ticket的身份状态置位为无效状态即可

public void logout(String ticket){         lTicketsDAO.updateStatus(ticket,1);     }
这样就完成了在SpringBoot实现基于Token的身份验证



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!