前端
token验证
1、前端接受后台发过来的token,将其存入sessionStorage,并且在store.js中进行存储
state: {
// 存储token
Authorization: sessionStorage.getItem('Authorization') ? sessionStorage.getItem('Authorization') : ''
},
mutations: {
// 修改token,并将token存入localStorage
changeLogin (state, user) {
state.Authorization = user.Authorization;
sessionStorage.setItem('Authorization', user.Authorization);
},
logout(state) {
sessionStorage.removeItem('Authorization');
sessionStorage.removeItem('user');
state.token = null
}
},
2、路由守卫:将没有登录信息的访问请求全部转到login.vue,有相关信息则放行
router.beforeEach((to, from, next) => { // 路由跳转前监控(保证登录状态)
// 重登陆删除本地数据
if (to.path === '/login') {
sessionStorage.removeItem('user')
}
let user = JSON.parse(sessionStorage.getItem('user'))
// 登录验证:如果本地没有储存用户且不在登录页面则跳转
if (!user && to.path !== '/login') {
next({ path: '/login' })
} else {
next()
}
})
3、配置请求头:这里使用axios,所以在axios的配置文件中添加:
axios.interceptors.request.use(
config => {
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = sessionStorage.getItem('Authorization');
}
return config;
},
error => {
return Promise.reject(error);
});
//http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
console.log(this);
Vue.$store.commit("logout");
Vue.$router.push({ path: "/login" });
}
return Promise.reject(error.response.data)
});
后端
token验证(集成jwt)
在pom.xml文件中导入相关依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
生成token
@Service
public class TokenService {
public String getToken(User user) {
Date start = new Date();
long currentTime = System.currentTimeMillis() + 60 * 60 * 1000;//一小时有效时间
Date end = new Date(currentTime);
String token = "";
token = JWT.create().withAudience(user.getUid().toString()).withIssuedAt(start).withExpiresAt(end)
.sign(Algorithm.HMAC256(user.getPassword()));
return token;
}
}
添加两个相关注解
//用来跳过验证的PassToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
boolean required() default true;
}
//需要登录才能进行操作的注解UserLoginToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
boolean required() default true;
}
添加一个拦截器判断访问该controller是否需要验证token
public class tokenInterceptor implements HandlerInterceptor {
@Autowired
UserService userService;
/**
* 预处理回调,判断每个处理器是否符合要求,返回true表示符合要求,放行
* 这里主要是对user的id和password进行解密,如果要改变,还需要改变生成token的service
* @param httpServletRequest
* @param httpServletResponse
* @param object
* 拦截的对象
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("Authorization");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if(!(object instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod=(HandlerMethod)object;
Method method=handlerMethod.getMethod();
//检查是否有passtoken注释,有则跳过认证
if (method.isAnnotationPresent(PassToken.class)) {
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserLoginToken.class)) {
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 user id
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
//jwt解码失败
throw new RuntimeException("401");
}
User user = userService.findUserById(userId);
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token);
} catch (JWTVerificationException e) {
throw new RuntimeException("401");
}
//将验证通过后的用户信息放到请求中
httpServletRequest.setAttribute("currentUser", user);
return true;
}
}
return true;
}
/**
* 处理器执行之后,视图解析器渲染之前 执行的回调
* @param httpServletRequest
* @param httpServletResponse
* @param o
* 处理器执行后返回的数据
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {
}
/**
* 视图解析器将视图解析之后的回调
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param e
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}
}
jwt的配置类
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**"); // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
}
@Bean
public tokenInterceptor authenticationInterceptor() {
return new tokenInterceptor();
}
}
使用
在controller中需要进行token验证的方法中添加@UserLoginToken
--------------------------------------分隔符
vue手动刷新页面,axios拦截器不执行情况
- 需求: vue里面的拦截器在每次请求前添加上token(Authorization)
- 问题:正常点击请求可以添加,一旦手动F5刷新页面,就直接没有进入拦截器,再次发送请求就恢复
- 分析原因: 需要把axios放外面单独封装,不放mian.js里面即可
src下面新建文件夹utils,文件名称intercept.js
import axios from 'axios'
axios.defaults.withCredentials = true
export const request = (config) => {
return axios(config)
}
// 请求前设置header
axios.interceptors.request.use(
config => {
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = localStorage.getItem('Authorization')
}
return config
},
error => {
return Promise.reject(error)
})
// 请求完成后 拦截器
axios.interceptors.response.use(
response => {
console.log(response)
if (response.data.code === 1003) {
router.replace({
path: '/login'
})
localStorage.removeItem('Authorization')
}
return response
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
localStorage.removeItem('Authorization')
router.replace({
path: '/login',
query: {redirect: router.currentRoute.fullPath} // 登录成功后 跳转当前页面
})
}
}
}
)
// 这句一定要写
export default axios
然后在main.js引入,这里要注意import引入顺序,否则可能导致其他有问题。