本次实验是基于前后端分离项目:springboot+vue+redis
接口校验时机:
后端:拦截所有请求,放行登录请求、请求头中包含token(用户名和密码正确时,回生成一个令牌,用来放心别的请求)的请求。
前端:拦截所有请求,放行登录请求、对非登录请求{如果,用户已经登录,则给请求头中添加token,如果用户未登录,跳到登陆页面}
1 后端实现
1.1 集成redis
1.2 书写WebAppConfigurer配置类
第17行:拦截所有请求
第18行:放行登录请求,
第16行:其他所有的请求经过loginINterceptor
@Configuration
public class WebAppConfigurer extends WebMvcConfigurationSupport {
@Resource
LoginInterceptor loginInterceptor;
/**
* 注册拦截器
*
* @param registry
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
System.out.println("addInterceptors");
super.addInterceptors(registry);
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/login")
;
}
}
1.3 LoginInterceptor
① return false表示拦截,不向下执行
② return true表示放行
③ 本类的作用:对经过本类的请求,放行login(登录)、放行OPTIONS请求(post请求的第一次请求。对于post请求,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)?如果这里说的不对,请大家不吝赐教)、拦截剩余所有请求,判断请求头中是否包含token,以及如果包含,该token是否在redis数据库中也同样存在
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Resource
private RedisTemplate<String,String> redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setContentType("text/html; charset=UTF-8");
String url = request.getRequestURI();
// System.out.println("url: "+url);
// 获取请求头中的token
String headerToken = request.getHeader("token");
// 规定,请求错误时,返回数据,
Result headerError = Result.error("请求头错误,请登录");
Result tokenError = Result.error("token过期,请重新登录");
// 如果进行登录提交,放行
if (url.indexOf("login") >= 0) {
return true;
}
// 如果是option请求,放行
// option请求是post请求的第一次请求
if(request.getMethod().equals("OPTIONS")){
return true;
}
// 判断请求头中是否含有token
// 没有token
if(headerToken==null || headerToken.length()==0){
response.getWriter().write(JSON.toJSONString(headerError));
return false;
}else {
// 含有token,且redis中存在该token
// return true;
Boolean isExist = redisTemplate.opsForSet().isMember("token", headerToken);
log.info(isExist.toString());
return isExist;
}
//return false表示拦截,不向下执行
//return true表示放行
}
}
2 前端实现
前端是一个vue项目,集成了axios组件、路由组件
2.1 创建request.js,封装前端的请求,添加请求头
这里要用到axios,如果没有安装axios组件,请先安装axios组件
15行:判断用户是否登录。这一步是从localStorage中获取loginUser,该数据是我在登录时向浏览器存储的用户信息
16行:判断请求路径是否为,“/”,在我的代码里对应的是登录请求
17行:解构在浏览器存储的用户信息,获得token
20行:给所有到20行的请求(用户已登录且,当前请求不是登录请求)添加请求头[“token”,token],key-value格式
import axios from 'axios'
// 创建axios实例
const service = axios.create({
baseURL: 'http://8.141.49.4:8010', // api的base_url
timeout: 100000 // 请求超时时间
})
//请求拦截器
service.interceptors.request.use(
(config) => {
console.log("请求拦截");
let pathname = location.pathname;
if(localStorage.getItem("loginUser")){
if(pathname != '/'){
let {token} = JSON.parse(localStorage.getItem("loginUser"));
console.log("token")
console.log(token)
config.headers.common['token'] = token;
}
}
console.log(config);
return config;
},
(error) => {
return Promise.reject(error);
})
export default service
2.2 撰写/router/index.js
如果使用vue-cli安装了路由组件,那么在项目的/src/router目录下会有一个index.js文件。用来管理前端的路由
需要注意的是:这段代码的核心在17-19行与38行的组合,给一个路由添加17-19行实例代码表示该路由需要用户登陆后才会放行,如果未登录,则会跳转到登录页。
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Login',
component: () => import('@/components/commons/Login')
},
{
path: '/home',
name: 'Container',
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
}
component: () => import('../layout/Container')
},
{
path: '*',
name: 'error',
component: () => import('../components/commons/Error')
},
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
router.beforeEach((to, from, next) => {
console.log("beforeEach")
// 判断该路由是否需要登录权限
if (to.meta.requireAuth) {
// 判断用户是否登录
if (localStorage.getItem("loginUser")) {
next();
}
else {
next({
path: '/',
// 将跳转的路由path作为参数,登录成功后跳转到该路由
query: {redirect: to.fullPath}
})
}
}
else {
next();
}
})
export default router