Spring Boot集成JWT实现登录token验证

1 参考文档

SpringBoot集成JWT实现token验证 | 苍穹帝-CSDN


2 导入依赖pom.xml

<!-- jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

3 生成Token工具类TokenUtils

@Component
public class TokenUtils {
    private static final long EXPIRE_TIME = 60 * 60 * 1000;  //过期时间1小时

    @Autowired
    public static UserService staticUserService;

    @Autowired
    public UserService userService;

    @PostConstruct
    public void setUserService() {
        staticUserService = userService;
    }

    //生成token
    public static String getToken(String userId, String sign) {
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        String token = "";
        token = JWT.create().withAudience(userId) // 将 userId 保存到 token 里面
                .withExpiresAt(date) //1小时后token过期
                .sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
        return token;
    }

    //获取当前登录的用户信息
    public static User getCurrentUser() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = request.getHeader("token");
            if (!StringUtils.isEmpty(token)) {
                String userId = JWT.decode(token).getAudience().get(0);
                return staticUserService.getById(Integer.valueOf(userId));
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }
}

4 拦截token拦截器JwtInterceptor

//token拦截器
public class JwtInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token

        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        // 执行认证
        if (StringUtils.isEmpty(token)) {
            throw new MyException(ResultCodeEnum.TOKEN_ERROR);
        }
        // 获取token中的userId
        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException j) {
            throw new MyException(ResultCodeEnum.TOKEN_CHECK_ERROR);
        }
        // 根据token中的userId查询数据库
        User user = userService.getById(userId);
        if (user == null) {
            throw new MyException(ResultCodeEnum.USER_NOT_EXISTS);
        }

        // 用户密码加签验证 token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new MyException(ResultCodeEnum.TOKEN_CHECK_ERROR);
        }

        return true;
    }
}

5 拦截器配置InterceptorConfig

//拦截器配置
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("/**")// 拦截所有请求,通过判断是否有token注解决定是否需要登录
                .excludePathPatterns(//添加不拦截路径
                        "/user/login",                //登录
                        "/user/register"              //注册
                );
    }
    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }
}

6 配置全局异常捕获GlobalExceptionHandler和自定义异常处理MyException

//全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e) {
        e.printStackTrace();
        return Result.fail();
    }

    //自定义异常处理方法
    @ExceptionHandler(MyException.class)
    @ResponseBody
    public Result error(MyException e) {
        return Result.build(e.getCode(), e.getMessage());
    }
}
//自定义全局异常类
@Data
public class MyException extends RuntimeException {

    private Integer code;

    //通过状态码和错误消息创建异常对象
    public MyException(String message, Integer code) {
        super(message);
        this.code = code;
    }

    //接收枚举类型对象
    public MyException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }

    @Override
    public String toString() {
        return "YyghException{" +
                "code=" + code +
                ", message=" + this.getMessage() +
                '}';
    }
}

7 状态信息枚举类ResultCodeEnum

//统一返回结果状态信息类
@Getter
public enum ResultCodeEnum {
    TOKEN_ERROR(400, "无token,请重新登录"),
    TOKEN_CHECK_ERROR(401,"token验证失败,请重新登录"),
    USER_NOT_EXISTS(402,"用户不存在,请重新登录"),
    ;

    private Integer code;
    private String message;

    private ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

8 在login的SeviceImpl中生成token

    @Override
    public UserVo login(UserVo userVo) {
        User one = getUserInfo(userVo);
        if (one != null) {
            BeanUtils.copyProperties(one, userVo);
            // 设置token
            String token = TokenUtils.getToken(one.getId().toString(), one.getPassword());
            userVo.setToken(token);
            return userVo;
        } else {
            throw new MyException(ResultCodeEnum.LOGIN_ERROR);
        }
    }

9 前端在request.js封装axios基本结构

import axios from 'axios'
import ElementUI from 'element-ui';
import {serverIp} from "../../public/config";
import router from "@/router";

const request = axios.create({
    baseURL: `http://${serverIp}/`,
    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")) : null
    if (user) {
        config.headers['token'] = user.token;  // 设置请求头
    }

    return config
}, error => {
    return Promise.reject(error)
});

//response 拦截器
//可以在接口响应后统一处理结果
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
        }
        // 当权限验证不通过的时候给出提示
        if (res.code >= 400 && res.code < 500) {
            ElementUI.Message({
                message: res.message,
                type: 'error'
            });
            router.push("/login")
        }
        return res;
    },
    error => {
        console.log('err' + error) // for debug
        return Promise.reject(error)
    }
)


export default request

你可能感兴趣的:(Java,java,spring,boot)