项目架构参见之前的笔记。
集成JWT
1、增加jwt依赖
<!-- JWT依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
2、新建生成token的类和方法及可能遇见的坑
编写jwt的生成token类,
package com.zhuoaninfo.vueDemo.common;
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
public class TokenUtil {
public static String genToken(String uid,String pwd){
return JWT.create().withAudience(uid)
.withExpiresAt(DateUtil.offsetHour(new Date(),2)) //2小时候过期
.sign(Algorithm.HMAC256(pwd));
}
}
上面的代码容易出现的坑,JWT可能会导错,hutool依赖中也有一个JWT的类,如果这里的JWT导错包,则.withAudience就会报错。
3、把TOKEN应用于登录方法
给userdto增加token属性
package com.zhuoaninfo.vueDemo.dto;
import cn.hutool.core.annotation.Alias;
import lombok.Data;
@Data
public class UserDto {
@Alias("用户名")
String username;
@Alias("密码")
String password;
private String awata;
private String token;
}
给userSerice中的login方法增加token
package com.zhuoaninfo.vueDemo.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuoaninfo.vueDemo.common.Contants;
import com.zhuoaninfo.vueDemo.common.TokenUtil;
import com.zhuoaninfo.vueDemo.common.exception.ServiceException;
import com.zhuoaninfo.vueDemo.dto.UserDto;
import com.zhuoaninfo.vueDemo.entity.User;
import com.zhuoaninfo.vueDemo.mapper.UserMapper;
import com.zhuoaninfo.vueDemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{
private static final Log LOG = Log.get();
public UserDto login(UserDto userDto){
QueryWrapper<User> query = new QueryWrapper<>();
query.eq("name", userDto.getUsername());
query.eq("password", userDto.getPassword());
User one;
try{
one=getOne(query);
}catch (Exception e){
LOG.error("");
throw new ServiceException(Contants.Code_500,"系统错误");
}
if(one!=null){
BeanUtil.copyProperties(one, userDto,true);
String s = TokenUtil.genToken(one.getId().toString(), one.getPassword());
userDto.setToken(s);
return userDto;
}else{
throw new ServiceException(Contants.Code_400,"用户名或密码错误");
}
}
@Override
public User register(UserDto vo) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name",vo.getUsername());
wrapper.eq("password",vo.getPassword());
User one = getOne(wrapper);
if(one == null){
one= new User();
BeanUtil.copyProperties(vo,one,true);
save(one);
}else {
throw new ServiceException(Contants.Code_401,"用户已存在");
}
return one;
}
}
现在登录返回的数据里面已经有了token属性和值。
4、新建增加拦截器
新建JwtInterceptor类,该类可以自定义名称。代码如下:
package com.zhuoaninfo.vueDemo.common.interceptor;
import cn.hutool.core.util.StrUtil;
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.zhuoaninfo.vueDemo.common.Contants;
import com.zhuoaninfo.vueDemo.common.exception.ServiceException;
import com.zhuoaninfo.vueDemo.entity.User;
import com.zhuoaninfo.vueDemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class JwtInterceptor implements HandlerInterceptor {
@Resource
private UserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
//如果不是方法类,则可以放行
if(!(handler instanceof HandlerMethod)){
return true;
}
//验证token是否为空
if(StrUtil.isBlank(token)){
throw new ServiceException(Contants.Code_401,"无TOKEN");
}
String uid;
try{
//获取token中的用户信息
uid= JWT.decode(token).getAudience().get(0);
}catch (JWTDecodeException e){
throw new ServiceException(Contants.Code_401,"token用户信息验证失败");
}
User user=userService.getById(Integer.parseInt(uid));
if(null==user){
throw new ServiceException(Contants.Code_401,"用户不存在,请重新登录");
}
JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try{
jwtVerifier.verify(token);
}catch(JWTVerificationException e){
throw new RuntimeException("token加密信息验证失败");
}
return true;
}
}
这里的 JWT.require(Algorithm.HMAC256(user.getPassword())).build()中HMAC256和对应生产类中的加密必须一致,否则会报错,无法进行token验证。导入的包比较多,今天导入的还比较顺利,没有错,如果代码有错的话,请先检查导入的包。
5、注册拦截器到项目中
新建InterceptorConfig类,名称可以自定义
package com.zhuoaninfo.vueDemo.config;
import com.zhuoaninfo.vueDemo.common.interceptor.JwtInterceptor;
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(jwtInterceptor())
.addPathPatterns("/**") //拦截所有请求
.excludePathPatterns("/**/login","/**/register"); //放行路径
}
@Bean
public JwtInterceptor jwtInterceptor(){
return new JwtInterceptor();
}
}
这里容易出现的坑是,@Configuration和 @Bean注释有时候会漏掉,项目编译不报错,但使用中userService会出现null,没有实现自定加载的问题,无法完成用户查询,导致无法进行数据查询。
后端的文件架构为:
6、前端增加配置token
前端的request.js把config代码处增加token,代码如下:
import axios from "axios";
const request=axios.create({
// baseURL:'/api',
baseURL:'http://localhost:8181',
timeout:5000
})
// request拦截器
// 可以自请求发送前对请求做一些处理
// 比如同意加token,对请求参数统一加密
request.interceptors.request.use(config=>{
config.headers['Content-Type']='application/json;charset=utf-8';
let user=localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")):{}
if(user){
config.headers['token']=user.token;
}
return config
},error=>{
return Promise.reject(error)
}
);
request.interceptors.response.use(
response=>{
let res=response.data;
if(response.config.responseType=='blob'){
return res
}
if(typeof res ==='string'){
res=res?JSON.parse(res):res
}
return res;
},error=>{
console.log(error);
return Promise.reject(error);
}
)
export default request
前后端集成jwt完成。