前言
在使用 Vue开发前后分离的项目以前,后台可以通过 Session 储存和获取当前登录的用户信息,进行权限管理。但使用Vue之后,发现虽然前端浏览器没有打开过新的页面。但后台每一次获取请求的Session都不相同。后台无法通过Session辨别当前用户。经过查找资料才发现,如今可以通过token进行身份验证Vue+Spring Boot实现token认证主要可分为四步
- 后台设置拦截器拦截请求,判断请求是否携带token或者判断携带token的权限
- 用户登录成功后,后台根据用户信息生成token,并将token的信息封入response
- vue收到token后,存入localStorage
- axios发送请求时,将token的内容封装到请求
后台配置
创建注解类
package com.booksystem.token;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserToken {
boolean required() default true;
}
maven引入jwt
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
配置拦截器
package com.booksystem.token;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.booksystem.pojo.User;
import com.booksystem.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
public class Authentication implements HandlerInterceptor{
@Autowired
UserService userService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
// 如果不是映射到方法直接通过
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(UserToken.class)) {
UserToken userToken = method.getAnnotation(UserToken.class);
if (userToken.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 user id
String userId = null;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new RuntimeException("401");
}
User user = userService.findUserById(Integer.parseInt(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");
}
return true;
}
}
return true;
}
}
配置configuration
package com.booksystem.config;
import com.booksystem.token.Authentication;
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;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authentication())
.addPathPatterns("/**");
}
@Bean
public Authentication authentication() {
return new Authentication();
}
}
配置TokenService
package com.booksystem.service;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.booksystem.pojo.User;
import org.springframework.stereotype.Service;
import java.util.Date;
@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(String.valueOf(user.getId())).withIssuedAt(start).withExpiresAt(end)
.sign(Algorithm.HMAC256(user.getPassword()));
return token;
}
}
UserController
package com.booksystem.controller;
import com.alibaba.fastjson.JSONObject;
import com.booksystem.pojo.User;
import com.booksystem.service.UserService;
import com.booksystem.service.impl.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
public class LoginController {
@Autowired
UserService userService;
@Autowired
TokenService tokenService;
@ResponseBody
@GetMapping("/login")
public Object login(User user){
User u = userService.userLogin(user);
JSONObject jsonObject = new JSONObject();
if (u != null){
String token = tokenService.getToken(u);
jsonObject.put("token",token);
jsonObject.put("user",u);
jsonObject.put("msg","登录成功");
jsonObject.put("code",200);
}else{
jsonObject.put("msg","账号或密码错误");
jsonObject.put("code",500);
}
return jsonObject;
}
//测试方法
@GetMapping("/getMessage")
@ResponseBody
@UserToken
public String getMessage(){
System.out.println("访问成功");
return "你已通过验证";
}
}
前端配置
在 vue 的 src 目录下 main.js 配置 axios 。若 token 存在,axios 的每一次请求都会携带上 token
//将token添加到axios请求头部
axios.interceptors.request.use(
config => {
if (localStorage.getItem('token')) {
config.headers.token = localStorage.getItem('token')
}
return config
},
error => {
return Promise.reject(error)
}
)
配置Vuex
const store = new Vuex.Store({
state: {
user: localStorage.getItem('user') ? localStorage.getItem('user') : null,
//若localSorage存在token,将值赋给Vuex.state.token
token: localStorage.getItem('token') ? localStorage.getItem('token') : null,
},
mutations: {
setUser(state, user) {
state.user = user
localStorage.setItem('user', JSON.stringify(user))
},
setToken(state, token) {
localStorage.setItem('token', token)
state.token = token
},
logout(state) {
localStorage.removeItem('token')
state.token = null
localStorage.removeItem('user')
state.user = null
}
}
})
前端的登录方法
login(){
var vm = this
vm.axios.get('http://localhost:8080/login',{
params:{
username:this.username,
password:this.password
}
}).then(function (response) {
if(response.data.code == 200){
console.log(response.data.msg)
//将token和user保存到localStorage中
vm.$store.commit('setToken',response.data.token)
vm.$store.commit('setUser',response.data.user)
//跳转到登录成功后的页面
vm.$router.push({path:'/index'})
else{
alert(response.data.msg)
}
}).catch(function (e) {
console.log(e)
})
}
根据需要,也可以配置router拦截器。若localStorage中无token,则只能访问登录和注册页面
//配置路由拦截器
router.beforeEach((to, from, next) => {
if (to.path === '/login' || to.path === '/register') {
next()
} else {
const token = window.localStorage.getItem('token')
if (!token) {
return next('/login')
}
next()
}
})
经过以上配置后,就能实现token身份认证。
注意:以上配置是在原来的基础上进行的改动,可能需要一定的 Vue 和 spring boot 基础。
参考博客:https://www.cnblogs.com/zwq20134/p/11687820.html