[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-m033ng06":3,"public-project-articles-m033ng06":17},{"id":4,"uuid":5,"project_id":6,"title":7,"content":8,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},45,"m033ng06",39,"12. SpringBoot集成JWT token实现权限验证","\npom JWT 依赖\n\n```xml\n\u003C!-- JWT -->\n\u003Cdependency>\n  \u003CgroupId>com.auth0\u003C\u002FgroupId>\n  \u003CartifactId>java-jwt\u003C\u002FartifactId>\n  \u003Cversion>4.3.0\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n自定义注解 AuthAccess\n\n```java\nimport java.lang.annotation.*;\n\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\npublic @interface AuthAccess {\n}\n```\n\nGlobalException\n\n```java\n@ControllerAdvice\npublic class GlobalException {\n\n    @ExceptionHandler(ServiceException.class)\n    @ResponseBody\n    public Result serviceException(ServiceException e) {\n        return Result.error(e.getCode(), e.getMessage());\n    }\n\n}\n```\n\nServiceException\n\n```java\n@Getter\npublic class ServiceException extends RuntimeException {\n\n    private final String code;\n\n    public ServiceException(String msg) {\n        super(msg);\n        this.code = \"500\";\n    }\n\n    public ServiceException(String code, String msg) {\n        super(msg);\n        this.code = code;\n    }\n\n}\n\n```\n\n**401 是权限错误**\n\n自定义拦截器 JwtInterceptor\n\n```java\nimport cn.hutool.core.util.StrUtil;\nimport com.auth0.jwt.JWT;\nimport com.auth0.jwt.JWTVerifier;\nimport com.auth0.jwt.algorithms.Algorithm;\nimport com.auth0.jwt.exceptions.JWTDecodeException;\nimport com.auth0.jwt.exceptions.JWTVerificationException;\nimport com.example.boot.common.Result;\nimport com.example.boot.entity.User;\nimport com.example.boot.exception.ServiceException;\nimport com.example.boot.mapper.UserMapper;\nimport org.springframework.web.method.HandlerMethod;\nimport org.springframework.web.servlet.HandlerInterceptor;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class JwtInterceptor implements HandlerInterceptor {\n\n    @Resource\n    private UserMapper userMapper;\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        String token = request.getHeader(\"token\");\n        if (StrUtil.isBlank(token)) {\n            token = request.getParameter(\"token\");\n        }\n        \u002F\u002F 如果不是映射到方法直接通过\n        if (handler instanceof HandlerMethod) {\n            AuthAccess annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthAccess.class);\n            if (annotation != null) {\n                return true;\n            }\n        }\n        \u002F\u002F 执行认证\n        if (StrUtil.isBlank(token)) {\n            throw new ServiceException(\"401\", \"请登录\");\n        }\n        \u002F\u002F 获取 token 中的 user id\n        String userId;\n        try {\n            userId = JWT.decode(token).getAudience().get(0);\n        } catch (JWTDecodeException j) {\n            throw new ServiceException(\"401\", \"请登录\");\n        }\n        \u002F\u002F 根据token中的userid查询数据库\n        User user = userMapper.selectById(userId);\n        if (user == null) {\n            throw new ServiceException(\"401\", \"请登录\");\n        }\n        \u002F\u002F 用户密码加签验证 token\n        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();\n        try {\n            jwtVerifier.verify(token); \u002F\u002F 验证token\n        } catch (JWTVerificationException e) {\n            throw new ServiceException(\"401\", \"请登录\");\n        }\n        return true;\n    }\n}\n```\n\n配置拦截器 InterceptorConfig\n\n```java\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;\n\n@Configuration\npublic class InterceptorConfig extends WebMvcConfigurationSupport {\n\n    @Override\n    protected void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(jwtInterceptor())\n                .addPathPatterns(\"\u002F**\")\n        super.addInterceptors(registry);\n    }\n\n    @Bean\n    public JwtInterceptor jwtInterceptor() {\n        return new JwtInterceptor();\n    }\n\n}\n```\n\n工具类 TokenUtils\n\n```java\n\n\nimport cn.hutool.core.date.DateUtil;\nimport cn.hutool.core.util.StrUtil;\nimport com.auth0.jwt.JWT;\nimport com.auth0.jwt.algorithms.Algorithm;\nimport com.example.boot.entity.User;\nimport com.example.boot.mapper.UserMapper;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.context.request.RequestContextHolder;\nimport org.springframework.web.context.request.ServletRequestAttributes;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.Date;\n\n@Component\npublic class TokenUtils {\n\n    private static UserMapper staticUserMapper;\n\n    @Resource\n    UserMapper userMapper;\n\n    @PostConstruct\n    public void setUserService() {\n        staticUserMapper = userMapper;\n    }\n\n    \u002F**\n     * 生成token\n     *\n     * @return\n     *\u002F\n    public static String genToken(String userId, String sign) {\n        return JWT.create().withAudience(userId) \u002F\u002F 将 user id 保存到 token 里面,作为载荷\n                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) \u002F\u002F 2小时后token过期\n                .sign(Algorithm.HMAC256(sign)); \u002F\u002F 以 password 作为 token 的密钥\n    }\n\n    \u002F**\n     * 获取当前登录的用户信息\n     *\n     * @return user对象\n     *\u002F\n    public static User getCurrentUser() {\n        try {\n            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();\n            String token = request.getHeader(\"token\");\n            if (StrUtil.isNotBlank(token)) {\n                String userId = JWT.decode(token).getAudience().get(0);\n                return staticUserMapper.selectById(Integer.valueOf(userId));\n            }\n        } catch (Exception e) {\n            return null;\n        }\n        return null;\n    }\n}\n\n```\n\n**token**\n![image.png](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2023\u002Fpng\u002F751015\u002F1692710004401-545d6871-b53b-4b97-af12-dc7700701072.png#averageHue=%23fef9f8&clientId=u860e231f-6c9d-4&from=paste&height=216&id=u75cba61c&originHeight=270&originWidth=1680&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=32466&status=done&style=none&taskId=ua2e423c9-2715-44a5-a9d4-638f67009aa&title=&width=1344)\n\n登录后存储数据\n![image.png](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2023\u002Fpng\u002F751015\u002F1692710126560-2a396f7a-22bb-4232-a480-8eb7f0f261c5.png#averageHue=%23fdfaf9&clientId=u860e231f-6c9d-4&from=paste&height=427&id=ue90288a5&originHeight=534&originWidth=1911&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=75918&status=done&style=none&taskId=u2fa99863-a5b5-4a45-ab5d-f9179f33cd6&title=&width=1528.8)\n\n```javascript\nlet user = JSON.parse(localStorage.getItem(\"honey-user\") || '{}')\nconfig.headers['token'] = user.token  \u002F\u002F 设置请求头\n```\n\n我们发现请求的头多了一个 token 数据\n这个 token 就是登录接口返回的 token\n在每次的请求的时候，都会在请求头带上这个 token 作为验证信息\n![image.png](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2023\u002Fpng\u002F751015\u002F1692710355383-1049472f-5662-4a8c-9412-fc009406dfd7.png#averageHue=%23fefcfc&clientId=u860e231f-6c9d-4&from=paste&height=374&id=u8ff4d064&originHeight=468&originWidth=1815&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=47617&status=done&style=none&taskId=uf0900c2b-4d18-4813-9e9d-f2e95dc096a&title=&width=1452)\n","coding",1,3243,67,"2024-04-16 02:33:00","2026-05-03 22:49:02","【青哥带小白做毕设2024】完整教程资料汇总","qingge-graduation-project-2024",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,28,35,42,49,56,63,69,76,83,90,97,104,105,112,119,126,133,140,147,154,161,168],{"id":21,"uuid":22,"project_id":6,"title":23,"type":9,"status":10,"public_enabled":10,"views":24,"sort":25,"created_at":26,"updated_at":27,"project_title":15,"project_slug":16},33,"R1oMCsCX","00. 从0开始带小白做SpringBoot+Vue+uniapp微信小程序实战项目",12130,55,"2025-04-08 11:28:17","2026-05-07 15:33:28.189425+00",{"id":29,"uuid":30,"project_id":6,"title":31,"type":9,"status":10,"public_enabled":10,"views":32,"sort":33,"created_at":34,"updated_at":14,"project_title":15,"project_slug":16},34,"s3u3u8W7","01. 网页布局技巧",3326,56,"2025-04-08 11:28:13",{"id":36,"uuid":37,"project_id":6,"title":38,"type":9,"status":10,"public_enabled":10,"views":39,"sort":40,"created_at":41,"updated_at":14,"project_title":15,"project_slug":16},35,"21zUHQYS","02. JavaScript入门",2017,57,"2025-04-08 11:27:55",{"id":43,"uuid":44,"project_id":6,"title":45,"type":9,"status":10,"public_enabled":10,"views":46,"sort":47,"created_at":48,"updated_at":14,"project_title":15,"project_slug":16},36,"4XVgY9Ti","03. Vue脚手架搭建",3719,58,"2025-04-08 11:27:46",{"id":50,"uuid":51,"project_id":6,"title":52,"type":9,"status":10,"public_enabled":10,"views":53,"sort":54,"created_at":55,"updated_at":14,"project_title":15,"project_slug":16},37,"S8vLLLvk","04. Git速成，推送代码到云端",1585,59,"2025-04-08 11:27:41",{"id":57,"uuid":58,"project_id":6,"title":59,"type":9,"status":10,"public_enabled":10,"views":60,"sort":61,"created_at":62,"updated_at":14,"project_title":15,"project_slug":16},38,"9EbwnGDp","05. 网页布局神器ElementUI速成",2670,60,"2025-04-08 11:27:37",{"id":6,"uuid":64,"project_id":6,"title":65,"type":9,"status":10,"public_enabled":10,"views":66,"sort":67,"created_at":68,"updated_at":14,"project_title":15,"project_slug":16},"tmzahWer","06. Vue管理系统速成",3744,61,"2025-04-08 11:27:32",{"id":70,"uuid":71,"project_id":6,"title":72,"type":9,"status":10,"public_enabled":10,"views":73,"sort":74,"created_at":75,"updated_at":14,"project_title":15,"project_slug":16},40,"2agqAUQK","07. SpringBoot速成",3654,62,"2025-04-08 11:27:27",{"id":77,"uuid":78,"project_id":6,"title":79,"type":9,"status":10,"public_enabled":10,"views":80,"sort":81,"created_at":82,"updated_at":14,"project_title":15,"project_slug":16},41,"SXPAzgy7","08. Http扫盲，让小白也能听懂",2337,63,"2025-04-08 11:27:20",{"id":84,"uuid":85,"project_id":6,"title":86,"type":9,"status":10,"public_enabled":10,"views":87,"sort":88,"created_at":89,"updated_at":14,"project_title":15,"project_slug":16},42,"ostBIxAV","09. SpringBoot集成Mybatis实现增删改查",4190,64,"2025-04-08 11:27:13",{"id":91,"uuid":92,"project_id":6,"title":93,"type":9,"status":10,"public_enabled":10,"views":94,"sort":95,"created_at":96,"updated_at":14,"project_title":15,"project_slug":16},43,"6Sv7afpa","10. Vue封装前后端数据交互工具",3716,65,"2024-04-16 02:33:13",{"id":98,"uuid":99,"project_id":6,"title":100,"type":9,"status":10,"public_enabled":10,"views":101,"sort":102,"created_at":103,"updated_at":14,"project_title":15,"project_slug":16},44,"d53BPIQs","11. Vue登录（含验证码）、注册页面开发",4867,66,"2024-04-16 02:33:08",{"id":4,"uuid":5,"project_id":6,"title":7,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},{"id":106,"uuid":107,"project_id":6,"title":108,"type":9,"status":10,"public_enabled":10,"views":109,"sort":110,"created_at":111,"updated_at":14,"project_title":15,"project_slug":16},46,"7xzyVD06","13. SpringBoot+Vue实现单文件、多文件上传和下载",2784,68,"2024-04-16 02:32:52",{"id":113,"uuid":114,"project_id":6,"title":115,"type":9,"status":10,"public_enabled":10,"views":116,"sort":117,"created_at":118,"updated_at":14,"project_title":15,"project_slug":16},47,"BdOLUenp","14. 多角色登录（Vue-Router路由守卫）",2318,69,"2024-04-16 02:32:39",{"id":120,"uuid":121,"project_id":6,"title":122,"type":9,"status":10,"public_enabled":10,"views":123,"sort":124,"created_at":125,"updated_at":14,"project_title":15,"project_slug":16},48,"2Wkx3igg","15. Vue个人信息修改、修改密码、重置密码",2092,70,"2024-04-16 02:32:33",{"id":127,"uuid":128,"project_id":6,"title":129,"type":9,"status":10,"public_enabled":10,"views":130,"sort":131,"created_at":132,"updated_at":14,"project_title":15,"project_slug":16},49,"BDvVa4By","16. SpringBoot+Vue管理系统实现增删改查",2598,71,"2024-04-16 02:32:29",{"id":134,"uuid":135,"project_id":6,"title":136,"type":9,"status":10,"public_enabled":10,"views":137,"sort":138,"created_at":139,"updated_at":14,"project_title":15,"project_slug":16},50,"FJVl0rCu","17. SpringBoot+Vue实现数据的批量导入和导出",1684,72,"2024-04-16 02:32:26",{"id":141,"uuid":142,"project_id":6,"title":143,"type":9,"status":10,"public_enabled":10,"views":144,"sort":145,"created_at":146,"updated_at":14,"project_title":15,"project_slug":16},51,"FvW9oHgj","18. SpringBoot+Vue项目部署上线",2845,73,"2024-04-16 02:32:22",{"id":148,"uuid":149,"project_id":6,"title":150,"type":9,"status":10,"public_enabled":10,"views":151,"sort":152,"created_at":153,"updated_at":14,"project_title":15,"project_slug":16},52,"xyqrxxiR","19. SpringBoot+Vue集成富文本编辑器",1499,74,"2024-04-16 02:32:18",{"id":155,"uuid":156,"project_id":6,"title":157,"type":9,"status":10,"public_enabled":10,"views":158,"sort":159,"created_at":160,"updated_at":14,"project_title":15,"project_slug":16},53,"XAaCXz8W","20. SpringBoot+Vue集成系统公告",1043,75,"2024-04-16 02:32:14",{"id":162,"uuid":163,"project_id":6,"title":164,"type":9,"status":10,"public_enabled":10,"views":165,"sort":166,"created_at":167,"updated_at":14,"project_title":15,"project_slug":16},54,"2havlmaC","21. SpringBoot+Vue集成AOP系统日志",1159,76,"2024-04-16 02:32:11",{"id":25,"uuid":169,"project_id":6,"title":170,"type":9,"status":10,"public_enabled":10,"views":171,"sort":172,"created_at":173,"updated_at":14,"project_title":15,"project_slug":16},"ObvLqJdX","22. SpringBoot+Vue实现Echarts数据报表（柱状图、饼图、折线图）",1688,99,"2024-04-16 02:30:25"]