从0到1开发测试平台(十三)前后端接口token验证
| 前言
前端登录成功之后,token会保存在浏览器的本地缓存里面,然后每次接口访问我们都会在header里面带上这个token,后台拿到这个token会去做用户认证,认证通过才会继续执行并成功返回,不通过提示用户验证失败或者请重新登录。前面我们的登录接口增加了token返回,并且保存前端把token进行了保存,但在接口请求的时候做token验证我们还没有做,接下来这篇文章讲的就是如何做token验证。
| 后端增加dao层对user_login表根据token查询
package com.caomingyu.cctestplatform.dao;
import com.caomingyu.cctestplatform.bean.UserLogin;
import org.apache.ibatis.annotations.Mapper;
import java.sql.Timestamp;
@Mapper
public interface UserLoginDao {
UserLogin queryByToken(String token, Timestamp now);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.caomingyu.cctestplatform.dao.UserLoginDao">
<resultMap id="userLogin" type="com.caomingyu.cctestplatform.bean.UserLogin">
<result column="ID" property="id"/>
<result column="USER_ID" property="userId"/>
<result column="TOKEN" property="token"/>
<result column="EXPIRATION_DATE" property="expirationDate"/>
<result column="CREATE_DATE" property="createDate"/>
<result column="LAST_UPDATE_DATE" property="lastUpdateDate"/>
<result column="IS_VALID" property="isValid"/>
</resultMap>
<select id="queryByToken" resultMap="userLogin">
select *
from user_login
where TOKEN = #{token}
and EXPIRATION_DATE > #{now}
and IS_VALID = 1
</select>
</mapper>
| 后端增加后端登录拦截器,这样便可以在请求到后台时进行拦截,根据token查询登录表是否存在,不存在则提示请登录。
package com.caomingyu.cctestplatform.config;
import com.alibaba.fastjson.JSONObject;
import com.caomingyu.cctestplatform.bean.Result;
import com.caomingyu.cctestplatform.bean.UserLogin;
import com.caomingyu.cctestplatform.common.ResultType;
import com.caomingyu.cctestplatform.common.StatusCode;
import com.caomingyu.cctestplatform.dao.UserLoginDao;
import lombok.extern.slf4j.Slf4j;
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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.Timestamp;
import java.util.Date;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private UserLoginDao userLoginDao;
/**
* 预处理回调方法,实现处理器的预处理
* 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或处理器
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
String token = request.getHeader("token");
log.info("token:{}",token);
//登录表里查下token是否存在
UserLogin userLogin = userLoginDao.queryByToken(token, new Timestamp(new Date().getTime()));
System.out.println("userLogin:" + userLogin);
System.out.println("userLogin:" + request.getRequestURL());
if (userLogin == null){
//校验失败
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
response.setHeader("Access-Control-Allow-Headers", "token,content-type");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
Result result = Result.result(null,true, StatusCode.NEED_LOGIN, ResultType.NEED_LOGIN);
response.getWriter().write(JSONObject.toJSONString(result));
return false;
}
return true;
}
/**
* 后处理回调方法,实现处理器(controller)的后处理,但在渲染视图之前
* 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时回调,
* 如性能监控中我们可以在此记录结束时间并输出消耗时间,
* 还可以进行一些资源清理,类似于try-catch-finally中的finally,
* 但仅调用处理器执行链中
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
| 后端配置需要拦截校验token的接口地址
package com.caomingyu.cctestplatform.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
/**
* 和springmvc的webmvc拦截配置一样
* @author BIANP
*/
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
List<String> patternList = new ArrayList<>();
patternList.add("/api/v1/user/login");
registry.addInterceptor(LoginInterceptor()).addPathPatterns("/api/**").excludePathPatterns(patternList);
}
@Bean
public LoginInterceptor LoginInterceptor() {
return new LoginInterceptor();
}
}
| 前端main.js添加请求拦截器,设置config.headers.common['token']的值,便会对请求都默认带上token
//添加一个请求拦截器
axios.interceptors.request.use(function (config) {
config.headers.common['token'] = window.sessionStorage.getItem('token');
return config;
}, function (error) {
// Do something with request error
console.info("error: ");
console.info(error);
return Promise.reject(error);
});
| 补充
vue本地存储的几种方式的简单介绍和区别(sessionStorage、localStorage以及vuex)
sessionStorage 方法针对一个 session 进行数据存储。当用户关闭浏览器窗口后,数据会被删除。
localStorage 方法存储的数据没有时间限制。第二天、第二周或下一年之后,数据依然可用。
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
区别:vuex存储在内存,localstorage(本地存储)则以文件的方式存储在本地,永久保存;sessionstorage( 会话存储 ) ,临时保存。localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
应用场景:vuex用于组件之间的传值,localstorage,sessionstorage则主要用于不同页面之间的传值。
永久性:当刷新页面(这里的刷新页面指的是 --> F5刷新,属于清除内存了)时vuex存储的值会丢失,sessionstorage页面关闭后就清除掉了,localstorage不会。
注:很多同学觉得用localstorage可以代替vuex, 对于不变的数据确实可以,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage,sessionstorage无法做到,原因就是上面所说的区别。
- 32764端口后门重出江湖,影响多款路由器
- 安全科普:SQLi Labs 指南 Part 1
- Do You Kown Asp.Net Core - 根据实体类自动创建Razor Page CURD页面模板
- 2014上半年国内安卓银行应用隐私泄露和安全隐患研究报告
- Do You Kown Asp.Net Core -- Asp.Net Core 2.0 未来web开发新趋势 Razor Page
- Metasploitable2使用指南
- 在渗透测试中使用fuzz技术(附windows安装指南)
- 黑了记者:写个恶意软件玩玩(二)
- 开源BUG跟踪平台JIRA目录遍历漏洞分析
- 黑了记者:写个恶意软件玩玩(一)
- 使用 Python 工具 Locust 进行负载测试
- 玩转Google的XSS游戏
- iOS基于GPUImage的图像形变设计(简单形变部分)
- 不错的node.js入门
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- MySQL死锁产生原因和解决方法
- 文件操作与文件夹操作
- 使用文件字节输入流FileInputStream读取文件
- 二进制与十进制与十六进制介绍+转换+图解
- Swagger 3.0 官方 starter 诞生了,其它的都可以扔了~
- 原码+反码+补码概述与示范
- HTTP客户端连接,选择HttpClient还是OkHttp?
- 数据类型(基本数据类型和引用数据类型)范围与字符转换,代码示例+个位十位百位相加面试题
- Scanner关键字的使用+代码介绍+注意事项
- 将一个txt文件,复制到另一个txt文件中(缓冲字节流(BufferedInputStream,BufferedOutputStream))
- java实现客户端服务端互发消息并接收
- 使用NIO实现非阻塞式(相对的)多人聊天室
- 三次握手与四次挥手+图解
- 单例模式-->饿汉式+懒汉式
- 非常有必要了解的Springboot启动扩展点