1.令牌的生成与存储
我们原来的代码
@PostMapping("/login")@ResponseBodypublic Map<String,Object> login1(@RequestBody Map<String,Object> map,HttpServletRequest request) {String account = (String) map.get("account");String admin_pwd = (String) map.get("admin_pwd");Admin login = adminService.login(account,admin_pwd);if(login!=null){SessionUtils.setAdmin(request,login); //增加sessionmap.put("code",200);map.put("msg","登录成功");return map;}else{map.put("code",600);map.put("error","用户名密码错误");return map;}}
使用Token令牌
@Resourseprivate RedisTemplate<String,Object> redisTemplate;@PostMapping("/login")@ResponseBodypublic Map<String,Object> login1(@RequestBody Map<String,Object> map,HttpServletRequest request) {String account = (String) map.get("account");String admin_pwd = (String) map.get("admin_pwd");Admin login = adminService.login(account,admin_pwd);if(login!=null){//生成Token令牌(令牌一定不能重复)String token = UUID.randomUUID()+"";//存到Redis数据库redisTemplate.opsForValue().set(token,user,Duration.ofMinutes(30L));map.put("code",200);map.put("msg","登录成功");return map;}else{map.put("code",600);map.put("error","用户名密码错误");return map;}}
2.登录成功后使用Token获取登录用户
/**登录成功后 访问view页面的时候想要获取到当前的Token因为view页面需要验证我们的登录信息104成功200登录失败*/@GetMapping("/view")public Result getUserLogin(HttpServletRequest request) throws UnsupportedEncodingException{//我们要将Token放到请求头中String token = request.getHeader("token");Object user = redisTemplate.opsForValue().get(token);if(user!=null){return new Result(user,message:"获取用户成功",code:104);}}return new Result(null,message:"失败没有找到token",code:200)
3.登录过滤器的Token处理
原来的代码
package com.DCWJ.springmvc.utils;import com.DCWJ.springmvc.po.Admin;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SessionInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Admin admin = SessionUtils.getAdmin(request);if(admin == null){response.setContentType("text/html;charset=utf-8");response.getWriter().print("您没有权限,请<a href='login'>登录</a>");return false;}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
使用Token后
package com.DCWJ.springmvc.utils;import com.DCWJ.springmvc.po.Admin;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SessionInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取headers中的参数String token = request.getHeader("token");token = token ==null ? "" : token;//查询token在Reids中的剩余时间Long expire = redisTemplate.getExpire(token);if(expire <=0){response.setContentType("text/html;charset=utf-8");response.getWriter().print("您没有权限,请<a href='login'>登录</a>");return false;}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
带来的问题 我们使用 redisTemplate.getExpire(token) 获取到登录时间后 我们会遇到一种情况 当这个时间只剩下1秒后,用户登录成功,但是当用户去访问另一个需要Token验证的页面,此时经过我们的过滤器会判定用户未登录,此时我们的用户需要重新登录 这给我们的用户带来很不好的体验 所以我们需要来优化一下这段代码:
第一种优化方案 :只要token时间大于0,我们使用flag=true 以后验证使用flag验证
package com.DCWJ.springmvc.utils;import com.DCWJ.springmvc.po.Admin;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SessionInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {boolean flag = False;//获取headers中的参数String token = request.getHeader("token");token = token ==null ? "" : token;//查询token在Reids中的剩余时间Long expire = redisTemplate.getExpire(token);if (expire>0){flag = true}if(flag=false){response.setContentType("text/html;charset=utf-8");response.getWriter().print("您没有权限,请<a href='login'>登录</a>");return false;}return flag;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
第二种:当大于0的时候直接重置成30分钟(推荐)
第一种优化方案 当用户登录成功后如果长时间未操作的话,则会一直的认定为用户登录的状态·,这样的用户万一多的话会占用我们的Redis内存,所以我们个他重置一下存储时间,这样就会防止这种情况
优化后的代码:
g.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class SessionInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {boolean flag = False;//获取headers中的参数String token = request.getHeader("token");token = token ==null ? "" : token;//查询token在Reids中的剩余时间Long expire = redisTemplate.getExpire(token);//如果时间大于0 我们就给他重置一下时间 30L是重置30分钟redisTemplate.expire(token,30L,TimeUnit.MINUTES);if(expire>0){response.setContentType("text/html;charset=utf-8");response.getWriter().print("您没有权限,请<a href='login'>登录</a>");return false;}return flag;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}}
4.前端存储Token
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>存储Token</title></head><body><script>//永久性存储// 第一种存储Token “{{是后端返回给前端的token}}” localStorage.setItem("key", "value");window.localStorage.setItem("Token","{{token}}")//读取Tonken var lastname = localStorage.getItem("key");var item = window.localStorage.getItem("Token");//删除数据语法 localStorage.removeItem("key");//删除tokenlocalStorage.removeItem("Token")//第二种Token存储localStorage.Token = "{{}}"//读取console.log(localStorage.Token)</script></body></html>
5.在AJAX请求中携带Token
$.ajax({url:"/view",type:"POST",contentType:'application/json',dataType:'json',data:JSON.stringify(data.field),header:{"token":localStorage.token}success:function(data){layer.msg(data.msg,{time:500},function(){parent.layer.close(index);});}});
6.使用Token改造退出功能
@Getmapping("/logout")public Result logout(HttpServletResponse response){String token = request.getHeader("token");//需要在Redis里面把令牌删掉Boolean delete = redisTemplate.delete(token);if(delete= true){system.out.println("删除成功")}elif{system.out.println("删除失败")}}
