项目登录流程如下
用户进入前端登录界面,输入账号密码等,输入完成之后前端发送请求到后端(拦截器不会拦截登录请求),后端验证账号密码等成功之后生成Token并存储到数据库,数据库中包含该Token过期时间,然后返回生成的Token到前端。
前端收到Token,表示登录成功,把这个Token存储到浏览器Cookie中。然后跳转到用户中心页面,用户中心页面从浏览器Cookie中扣出Token,跟随请求用户数据接口一起带到后端。
后端通过拦截器拦截到这个请求,去判断这个Token是否有效,有效就放过去做他该做的事情,无效就抛出异常。
跨域配置
先说一下这个前后分离的项目,已经配置过跨域这些问题。我这里后端配置的方式如下:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") .maxAge(3600) .allowCredentials(true); } }
前端每次发送请求也都有在ajax里面设置xhrFields:{withCredentials: true}属性。
拦截器代码
@Component public class LoginInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //从header中获取token String token = request.getHeader("token"); //token为空 if(StringUtils.isBlank(token)){ throw new XjxccException("登录信息不存在,请重新登录", 601); } //查询token信息 TbUserToken tbToken = tokenService.queryByToken(token); if(tbToken == null || tbToken.getExpireTime().getTime() < System.currentTimeMillis()){ throw new XjxccException("登录信息已失效,请重新登录", 602); } return true; } }
问题
现在的情况是,如果Token正常就没问题,如果Token失效了,前端发送的请求就会提示跨域问题,可是该项目已经配置过跨域问题了。既然又提示跨域问题,那我们修改代码如下:
@Component public class LoginInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //从header中获取token String token = request.getHeader("token"); //token为空 if(StringUtils.isBlank(token)){ setCorsMappings(request, response); throw new XjxccException("登录信息不存在,请重新登录", 601); } //查询token信息 TbUserToken tbToken = tokenService.queryByToken(token); if(tbToken == null || tbToken.getExpireTime().getTime() < System.currentTimeMillis()){ setCorsMappings(request, response); throw new XjxccException("登录信息已失效,请重新登录", 602); } return true; } private void setCorsMappings(HttpServletRequest request, HttpServletResponse response){ String origin = request.getHeader("Origin"); response.setHeader("Access-Control-Allow-Origin", origin); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization"); response.setHeader("Access-Control-Allow-Credentials", "true"); } }
这样修改之后,如果Token无效,前端发送的请求就不会提示跨域问题。而且也可以在ajax的success方法中根据错误码(601/602)做不同的判断,代码如下:
$.ajax({ type: "POST", url: "请求用户信息接口", xhrFields:{withCredentials: true}, headers: {token: vm.token}, contentType: 'application/json', success: function(result){ //如果是token无效 if(result.code == 601 || result.code == 602){ layer.msg(result.msg + "<br>稍后为您自动跳转到登录页面"); window.setInterval(function (){ window.location.href = "user-login.html"; },1500); }else if(判断用户信息接口返回的code){ ... } },error: function(){ layer.msg("系统异常"); } });
下面附上上面代码中的XjxccException类:
/** * <p>Title: XjxccException.java</p> * <p>Description: 自定义异常</p> * <p>Copyright: Copyright (c) 2018</p> * * @email lgqxjxcc@163.com * @author liguoqing * @date 2018年10月21日 * @version 1.0 */ public class XjxccException extends RuntimeException { private static final long serialVersionUID = 1L; private String msg; private int code = 500; public XjxccException(String msg) { super(msg); this.msg = msg; } public XjxccException(String msg, Throwable e) { super(msg, e); this.msg = msg; } public XjxccException(String msg, int code) { super(msg); this.msg = msg; this.code = code; } public XjxccException(String msg, int code, Throwable e) { super(msg, e); this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } }
