springboot+vue2集成JWT token实现权限验证
前端项目搭建参考:
Vue项目的搭建和启动_vue项目启动 csdn-CSDN博客
Vue + ElementUI 登录页面_vue用户登录页面-CSDN博客
跨域问题前端解决-CSDN博客
实现思路:
1. 实现的目的:为了保护网站安全信息,使用jwt进行权限验证,也就是说只有登录用户才可以看到网站的一些特定内容。那么浏览器每次发起请求时,就需要带上携带加密登录用户信息的token令牌。而token是用户登录时后端返回给前端的。
2. 编写登录接口,传入的登录form表单跟数据库储存的数据一致时,接口返回给前端之前,生成一个token,跟着登录信息一起返回
3. 前端拿到从服务端返回的token,储存于localStorage中,然后在前端配置发起请求时统一的拦截器,在请求头中塞入这个token,这样每个请求就都携带了token
4. 后端这个时候就需要校验了,你这个请求想要查看我网站的安全信息,我得先看看你有没有相应用户的权限,是否之前登录过,这个依据就是是否携带了正确的token。后端需要对大多数请求都进行这个校验,而每个请求的校验逻辑是一样的,所以后端需要写一个拦截器,拦截所有的请求,只有检验你是带着正确的token来的,才可以访问资源,当然登录接口需要放开。
5. 最后还需要在前端的接收请求拦截器中,当某个请求没有权限,即返回code=401时,需要跳转到登录页面引导用户登录或者注册。
1. pom文件添加JWT依赖
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>${jwt-version}</version>
</dependency>
2. 生成token,在登录接口返回给前端
/**
* 生成token
* @param userId 账号
* @param sign 密码
* @return String
*/
public static String getToken(String userId, String sign) {
return JWT.create().withAudience(userId) //将userId荷载
.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //当前时间2小时后过期
.sign(Algorithm.HMAC256(sign)); //password作为token的密钥
}
@RestController
public class LoginController {
@Autowired
private UserService userService;
@PostMapping("/login")
public Result<JSONObject> login(@RequestBody User user) {
Result<JSONObject> result = new Result<JSONObject>();
if (StrUtil.isBlank(user.getUserName()) || StrUtil.isBlank(user.getPassword())) {
return Result.error("数据输入不合法");
}
User dbUser = userService.login(user);
//生成token
String token = TokenUtils.getToken(user.getUserName(), user.getPassword());
JSONObject obj = new JSONObject();
obj.set("user", dbUser);
obj.set("token", token);
result.setResult(obj);
result.success("登录成功");
return result;
}
}
3. 前端将token存入localStorage,设置请求拦截器,给每个请求带上token
submitForm() {
this.$refs.form.validate((valid) => {
if (valid) {
let loginUrl = '/login'
postAction(loginUrl, this.form).then((res) => {
if (res.success) {
let token = res.result.token;
localStorage.setItem("token", token)
this.$message.success("登录成功!")
router.push("/")
}
})
}else {
this.$message({
message: '请输入用户名和密码',
type: 'warning'
});
}
return false;
})
}
// request拦截器
// 可以在请求发送之前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(
config => {
// 可以在这里添加认证 token
config.headers['Content-Type'] = 'application/json;charset=utf-8';
// config.headers['Authorization'] = 'Bearer your-token';
config.headers['token'] = localStorage.getItem("token");
return config;
},
error => {
return Promise.reject(error);
}
);
4. 后端设置jwt拦截器,并配置
/**
* @Author: EstellaQ
* @Date: 2025/4/7 9:20
* @Description: jwt拦截器
**/
public class JwtInterceptor implements HandlerInterceptor {
@Resource
private UserMapper userMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if (StrUtil.isBlank(token)) {
token = request.getParameter("token");
}
//如果不是映射到方法直接通过
if (handler instanceof HandlerMethod) {
//通过反射拿到方法上的注解
AuthAccess annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthAccess.class);
if (annotation != null) {
return true;
}
}
//执行认证
if (StrUtil.isBlank(token)) {
throw new ServiceException(401, "请登录");
}
//获取token中的userid
String userId;
try {
//解码
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException e) {
throw new ServiceException(401, "请登录");
}
//根据token中的userid查询数据库
User user = userMapper.selectByUserName(userId);
if (user == null) {
throw new ServiceException(401, "请登录");
}
//用户密码加签验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token); //验证token
} catch (JWTVerificationException e) {
throw new ServiceException(401, "请登录");
}
return true;
}
}
/**
* @Author: EstellaQ
* @Date: 2025/4/7 9:54
* @Description: 配置拦截规则
**/
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/**") //拦截所有的请求路径
.excludePathPatterns("/login"); //将登录放行
super.addInterceptors(registry);
}
@Bean
public JwtInterceptor jwtInterceptor() {
return new JwtInterceptor();
}
}
5. 前端设置接口接收之后的拦截器,若权限认证失败,返回401,则返回登录接口
request.interceptors.response.use(
response => {
// 处理响应数据
let res = response.data
// 兼容服务端返回的字符串数据
if (typeof res == 'string') {
res = res ? JSON.parse(res) : res
}
if (res.code === 401) {
router.push("/login")
}
return res
},
error => {
// 处理响应错误
return Promise.reject(error);
}
);