├── .gitignore ├── LICENSE ├── README.md ├── vueblog-java ├── Dockerfile ├── README.md ├── docker-compose.yml ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── markerhub │ │ │ ├── CodeGenerator.java │ │ │ ├── VueblogApplication.java │ │ │ ├── common │ │ │ ├── dto │ │ │ │ └── LoginDto.java │ │ │ ├── exception │ │ │ │ └── GlobalExceptionHandler.java │ │ │ └── lang │ │ │ │ └── Result.java │ │ │ ├── config │ │ │ ├── CorsConfig.java │ │ │ ├── MybatisPlusConfig.java │ │ │ └── ShiroConfig.java │ │ │ ├── controller │ │ │ ├── AccountController.java │ │ │ ├── BlogController.java │ │ │ └── UserController.java │ │ │ ├── entity │ │ │ ├── Blog.java │ │ │ └── User.java │ │ │ ├── mapper │ │ │ ├── BlogMapper.java │ │ │ └── UserMapper.java │ │ │ ├── service │ │ │ ├── BlogService.java │ │ │ ├── UserService.java │ │ │ └── impl │ │ │ │ ├── BlogServiceImpl.java │ │ │ │ └── UserServiceImpl.java │ │ │ ├── shiro │ │ │ ├── AccountProfile.java │ │ │ ├── AccountRealm.java │ │ │ ├── JwtFilter.java │ │ │ └── JwtToken.java │ │ │ └── util │ │ │ ├── JwtUtils.java │ │ │ └── ShiroUtil.java │ └── resources │ │ ├── META-INF │ │ └── spring-devtools.properties │ │ ├── application-default.yml │ │ ├── application-pro.yml │ │ ├── application.yml │ │ ├── mapper │ │ ├── BlogMapper.xml │ │ └── UserMapper.xml │ │ └── vueblog.sql │ └── test │ └── java │ └── com │ └── markerhub │ └── VueblogApplicationTests.java └── vueblog-vue ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── axios.js ├── components │ └── Header.vue ├── main.js ├── permission.js ├── router │ └── index.js ├── store │ └── index.js └── views │ ├── BlogDetail.vue │ ├── BlogEdit.vue │ ├── Blogs.vue │ └── Login.vue └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | **/.idea/** 14 | **/target/** 15 | 16 | **/node_modules/** 17 | 18 | **/*.iml 19 | 20 | *.iml 21 | 22 | # Package Files # 23 | *.jar 24 | *.war 25 | *.nar 26 | *.ear 27 | *.zip 28 | *.tar.gz 29 | *.rar 30 | 31 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 32 | hs_err_pid* 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **项目:vueblog** 2 | > 3 | > **公众号:MarkerHub** 4 | 5 | ### 介绍 6 | 7 | 一个基于SpringBoot + Vue开发的前后端分离博客项目,带有超级详细开发文档和讲解视频。还未接触过vue开发,或者前后端分离的同学,学起来哈。别忘了给vueblog一个star!感谢 8 | 9 | ### 技术栈: 10 | 11 | ![](https://oscimg.oschina.net/oscnet/up-4626cb696c003e36c4515e77adc7632c6ed.png) 12 | 13 | ### 项目效果: 14 | 15 | ![图片](https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20200613/b1c18a3fe33544578971c3a15d0d9425.png) 16 | 17 | ![图片](https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20200613/5e291faeaef648af87b8b33483eef5bd.png) 18 | 19 | 20 | ### 项目文档: 21 | 22 | 开发文档:https://juejin.im/post/5ecfca676fb9a04793456fb8 23 | 24 | vue入门视频:https://www.bilibili.com/video/BV125411W73W/ 25 | 26 | **vueblog讲解视频:** https://www.bilibili.com/video/BV1PQ4y1P7hZ/ 27 | 28 | 关注我的B站,后续陆续还有 29 | 30 | * 前后端分离类百度搜索引擎项目 31 | * 即时聊天等项目 32 | 33 | 等项目分享出来哈! 34 | 35 | **更多项目请关注公众号:MarkerHub** 36 | 37 | ![MarkerHub](https://camo.githubusercontent.com/061df651b4fcfec5d258dc2beb78f441b9360e42/68747470733a2f2f696d6167652d313330303536363531332e636f732e61702d6775616e677a686f752e6d7971636c6f75642e636f6d2f6d696e652f4d61726b65724875622e6a7067) 38 | -------------------------------------------------------------------------------- /vueblog-java/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8 2 | 3 | EXPOSE 8080 4 | 5 | ADD vueblog-0.0.1-SNAPSHOT.jar app.jar 6 | RUN bash -c 'touch /app.jar' 7 | 8 | ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=pro"] -------------------------------------------------------------------------------- /vueblog-java/README.md: -------------------------------------------------------------------------------- 1 | ### 工具获取 2 | 3 | * xshell 6 绿色破解版:关注公众号:JavaCat,回复 xshell 获取 4 | * Navicat 11 简体中文版:关注公众号:JavaCat,回复 navicat 获取 5 | 6 | 公众号二维码: 7 | 8 | ![JavaCat](//image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20201020/7fa16a1f957f4cfebe7be1f6675f6f36.png "JavaCat") 9 | 10 | 直接扫码回复对应关键字 11 | 12 | **推荐阅读:** 13 | 14 | [B站86K播放量,SpringBoot+Vue前后端分离完整入门教程!](https://mp.weixin.qq.com/s/jGEkHTf2X8l-wUenc-PpEw) 15 | 16 | [分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!](https://mp.weixin.qq.com/s/jz6e977xP-OyaAKNjNca8w) 17 | 18 | [Github上最值得学习的100个Java开源项目,涵盖各种技术栈!](https://mp.weixin.qq.com/s/N-U0TaEUXnBFfBsmt_OESQ) 19 | 20 | [2020年最新的常问企业面试题大全以及答案](https://mp.weixin.qq.com/s/lR5LC5GnD2Gs59ecV5R0XA) -------------------------------------------------------------------------------- /vueblog-java/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | nginx: 4 | image: nginx:latest 5 | ports: 6 | - 80:80 7 | volumes: 8 | - /root/nginx/html:/usr/share/nginx/html 9 | - /root/nginx/nginx.conf:/etc/nginx/nginx.conf 10 | privileged: true 11 | mysql: 12 | image: mysql:5.7.27 13 | ports: 14 | - 3306:3306 15 | environment: 16 | - MYSQL_ROOT_PASSWORD=admin 17 | redis: 18 | image: redis:latest 19 | vueblog: 20 | image: vueblog:latest 21 | build: . 22 | ports: 23 | - 8081:8081 24 | depends_on: 25 | - mysql 26 | - redis -------------------------------------------------------------------------------- /vueblog-java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.6.RELEASE 9 | 10 | 11 | com.markerhub 12 | vueblog 13 | 0.0.1-SNAPSHOT 14 | vueblog 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-devtools 30 | runtime 31 | true 32 | 33 | 34 | mysql 35 | mysql-connector-java 36 | runtime 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | true 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-test 46 | test 47 | 48 | 49 | org.junit.vintage 50 | junit-vintage-engine 51 | 52 | 53 | 54 | 55 | 56 | 57 | com.baomidou 58 | mybatis-plus-boot-starter 59 | 3.2.0 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-freemarker 64 | 65 | 66 | mysql 67 | mysql-connector-java 68 | runtime 69 | 70 | 71 | 72 | 73 | com.baomidou 74 | mybatis-plus-generator 75 | 3.2.0 76 | 77 | 78 | 79 | org.crazycake 80 | shiro-redis-spring-boot-starter 81 | 3.2.1 82 | 83 | 84 | 85 | 86 | cn.hutool 87 | hutool-all 88 | 5.3.3 89 | 90 | 91 | 92 | 93 | io.jsonwebtoken 94 | jjwt 95 | 0.9.1 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/CodeGenerator.java: -------------------------------------------------------------------------------- 1 | package com.markerhub; 2 | 3 | import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; 4 | import com.baomidou.mybatisplus.core.toolkit.StringPool; 5 | import com.baomidou.mybatisplus.core.toolkit.StringUtils; 6 | import com.baomidou.mybatisplus.generator.AutoGenerator; 7 | import com.baomidou.mybatisplus.generator.InjectionConfig; 8 | import com.baomidou.mybatisplus.generator.config.*; 9 | import com.baomidou.mybatisplus.generator.config.po.TableInfo; 10 | import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; 11 | import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Scanner; 16 | 17 | // 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中 18 | public class CodeGenerator { 19 | 20 | /** 21 | *

22 | * 读取控制台内容 23 | *

24 | */ 25 | public static String scanner(String tip) { 26 | Scanner scanner = new Scanner(System.in); 27 | StringBuilder help = new StringBuilder(); 28 | help.append("请输入" + tip + ":"); 29 | System.out.println(help.toString()); 30 | if (scanner.hasNext()) { 31 | String ipt = scanner.next(); 32 | if (StringUtils.isNotEmpty(ipt)) { 33 | return ipt; 34 | } 35 | } 36 | throw new MybatisPlusException("请输入正确的" + tip + "!"); 37 | } 38 | 39 | public static void main(String[] args) { 40 | // 代码生成器 41 | AutoGenerator mpg = new AutoGenerator(); 42 | 43 | // 全局配置 44 | GlobalConfig gc = new GlobalConfig(); 45 | String projectPath = System.getProperty("user.dir"); 46 | gc.setOutputDir(projectPath + "/src/main/java"); 47 | // gc.setOutputDir("D:\\test"); 48 | gc.setAuthor("关注公众号:MarkerHub"); 49 | gc.setOpen(false); 50 | // gc.setSwagger2(true); 实体属性 Swagger2 注解 51 | gc.setServiceName("%sService"); 52 | mpg.setGlobalConfig(gc); 53 | 54 | // 数据源配置 55 | DataSourceConfig dsc = new DataSourceConfig(); 56 | dsc.setUrl("jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC"); 57 | // dsc.setSchemaName("public"); 58 | dsc.setDriverName("com.mysql.cj.jdbc.Driver"); 59 | dsc.setUsername("root"); 60 | dsc.setPassword("admin"); 61 | mpg.setDataSource(dsc); 62 | 63 | // 包配置 64 | PackageConfig pc = new PackageConfig(); 65 | pc.setModuleName(null); 66 | pc.setParent("com.markerhub"); 67 | mpg.setPackageInfo(pc); 68 | 69 | // 自定义配置 70 | InjectionConfig cfg = new InjectionConfig() { 71 | @Override 72 | public void initMap() { 73 | // to do nothing 74 | } 75 | }; 76 | 77 | // 如果模板引擎是 freemarker 78 | String templatePath = "/templates/mapper.xml.ftl"; 79 | // 如果模板引擎是 velocity 80 | // String templatePath = "/templates/mapper.xml.vm"; 81 | 82 | // 自定义输出配置 83 | List focList = new ArrayList<>(); 84 | // 自定义配置会被优先输出 85 | focList.add(new FileOutConfig(templatePath) { 86 | @Override 87 | public String outputFile(TableInfo tableInfo) { 88 | // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! 89 | return projectPath + "/src/main/resources/mapper/" 90 | + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; 91 | } 92 | }); 93 | 94 | cfg.setFileOutConfigList(focList); 95 | mpg.setCfg(cfg); 96 | 97 | // 配置模板 98 | TemplateConfig templateConfig = new TemplateConfig(); 99 | 100 | templateConfig.setXml(null); 101 | mpg.setTemplate(templateConfig); 102 | 103 | // 策略配置 104 | StrategyConfig strategy = new StrategyConfig(); 105 | strategy.setNaming(NamingStrategy.underline_to_camel); 106 | strategy.setColumnNaming(NamingStrategy.underline_to_camel); 107 | strategy.setEntityLombokModel(true); 108 | strategy.setRestControllerStyle(true); 109 | strategy.setInclude(scanner("表名,多个英文逗号分割").split(",")); 110 | strategy.setControllerMappingHyphenStyle(true); 111 | strategy.setTablePrefix("m_"); 112 | mpg.setStrategy(strategy); 113 | mpg.setTemplateEngine(new FreemarkerTemplateEngine()); 114 | mpg.execute(); 115 | } 116 | } -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/VueblogApplication.java: -------------------------------------------------------------------------------- 1 | package com.markerhub; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class VueblogApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(VueblogApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/common/dto/LoginDto.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.common.dto; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | import java.io.Serializable; 7 | 8 | @Data 9 | public class LoginDto implements Serializable { 10 | 11 | @NotBlank(message = "昵称不能为空") 12 | private String username; 13 | 14 | @NotBlank(message = "密码不能为空") 15 | private String password; 16 | } 17 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/common/exception/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.common.exception; 2 | 3 | import com.markerhub.common.lang.Result; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.shiro.ShiroException; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.validation.BindingResult; 8 | import org.springframework.validation.ObjectError; 9 | import org.springframework.web.bind.MethodArgumentNotValidException; 10 | import org.springframework.web.bind.annotation.ExceptionHandler; 11 | import org.springframework.web.bind.annotation.ResponseStatus; 12 | import org.springframework.web.bind.annotation.RestControllerAdvice; 13 | 14 | @Slf4j 15 | @RestControllerAdvice 16 | public class GlobalExceptionHandler { 17 | 18 | @ResponseStatus(HttpStatus.UNAUTHORIZED) 19 | @ExceptionHandler(value = ShiroException.class) 20 | public Result handler(ShiroException e) { 21 | log.error("运行时异常:----------------{}", e); 22 | return Result.fail(401, e.getMessage(), null); 23 | } 24 | 25 | @ResponseStatus(HttpStatus.BAD_REQUEST) 26 | @ExceptionHandler(value = MethodArgumentNotValidException.class) 27 | public Result handler(MethodArgumentNotValidException e) { 28 | log.error("实体校验异常:----------------{}", e); 29 | BindingResult bindingResult = e.getBindingResult(); 30 | ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get(); 31 | 32 | return Result.fail(objectError.getDefaultMessage()); 33 | } 34 | 35 | @ResponseStatus(HttpStatus.BAD_REQUEST) 36 | @ExceptionHandler(value = IllegalArgumentException.class) 37 | public Result handler(IllegalArgumentException e) { 38 | log.error("Assert异常:----------------{}", e); 39 | return Result.fail(e.getMessage()); 40 | } 41 | 42 | @ResponseStatus(HttpStatus.BAD_REQUEST) 43 | @ExceptionHandler(value = RuntimeException.class) 44 | public Result handler(RuntimeException e) { 45 | log.error("运行时异常:----------------{}", e); 46 | return Result.fail(e.getMessage()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/common/lang/Result.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.common.lang; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class Result implements Serializable { 9 | 10 | private int code; // 200是正常,非200表示异常 11 | private String msg; 12 | private Object data; 13 | 14 | public static Result succ(Object data) { 15 | return succ(200, "操作成功", data); 16 | } 17 | 18 | public static Result succ(int code, String msg, Object data) { 19 | Result r = new Result(); 20 | r.setCode(code); 21 | r.setMsg(msg); 22 | r.setData(data); 23 | return r; 24 | } 25 | 26 | public static Result fail(String msg) { 27 | return fail(400, msg, null); 28 | } 29 | 30 | public static Result fail(String msg, Object data) { 31 | return fail(400, msg, data); 32 | } 33 | 34 | public static Result fail(int code, String msg, Object data) { 35 | Result r = new Result(); 36 | r.setCode(code); 37 | r.setMsg(msg); 38 | r.setData(data); 39 | return r; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/config/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | /** 8 | * 解决跨域问题 9 | */ 10 | @Configuration 11 | public class CorsConfig implements WebMvcConfigurer { 12 | 13 | @Override 14 | public void addCorsMappings(CorsRegistry registry) { 15 | registry.addMapping("/**") 16 | .allowedOrigins("*") 17 | .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS") 18 | .allowCredentials(true) 19 | .maxAge(3600) 20 | .allowedHeaders("*"); 21 | } 22 | } -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/config/MybatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.config; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.transaction.annotation.EnableTransactionManagement; 8 | 9 | @Configuration 10 | @EnableTransactionManagement 11 | @MapperScan("com.markerhub.mapper") 12 | public class MybatisPlusConfig { 13 | 14 | @Bean 15 | public PaginationInterceptor paginationInterceptor() { 16 | PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); 17 | return paginationInterceptor; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/config/ShiroConfig.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.config; 2 | 3 | import com.markerhub.shiro.AccountRealm; 4 | import com.markerhub.shiro.JwtFilter; 5 | import org.apache.shiro.mgt.SecurityManager; 6 | import org.apache.shiro.session.mgt.SessionManager; 7 | import org.apache.shiro.spring.web.ShiroFilterFactoryBean; 8 | import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition; 9 | import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition; 10 | import org.apache.shiro.web.mgt.DefaultWebSecurityManager; 11 | import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; 12 | import org.crazycake.shiro.RedisCacheManager; 13 | import org.crazycake.shiro.RedisSessionDAO; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.context.annotation.Bean; 16 | import org.springframework.context.annotation.Configuration; 17 | 18 | import javax.servlet.Filter; 19 | import java.util.HashMap; 20 | import java.util.LinkedHashMap; 21 | import java.util.Map; 22 | 23 | @Configuration 24 | public class ShiroConfig { 25 | 26 | @Autowired 27 | JwtFilter jwtFilter; 28 | 29 | @Bean 30 | public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) { 31 | DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); 32 | 33 | // inject redisSessionDAO 34 | sessionManager.setSessionDAO(redisSessionDAO); 35 | return sessionManager; 36 | } 37 | 38 | @Bean 39 | public DefaultWebSecurityManager securityManager(AccountRealm accountRealm, 40 | SessionManager sessionManager, 41 | RedisCacheManager redisCacheManager) { 42 | 43 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(accountRealm); 44 | 45 | //inject sessionManager 46 | securityManager.setSessionManager(sessionManager); 47 | 48 | // inject redisCacheManager 49 | securityManager.setCacheManager(redisCacheManager); 50 | return securityManager; 51 | } 52 | 53 | @Bean 54 | public ShiroFilterChainDefinition shiroFilterChainDefinition() { 55 | DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); 56 | 57 | Map filterMap = new LinkedHashMap<>(); 58 | 59 | filterMap.put("/**", "jwt"); 60 | chainDefinition.addPathDefinitions(filterMap); 61 | return chainDefinition; 62 | } 63 | 64 | @Bean("shiroFilterFactoryBean") 65 | public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, 66 | ShiroFilterChainDefinition shiroFilterChainDefinition) { 67 | ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); 68 | shiroFilter.setSecurityManager(securityManager); 69 | 70 | Map filters = new HashMap<>(); 71 | filters.put("jwt", jwtFilter); 72 | shiroFilter.setFilters(filters); 73 | 74 | Map filterMap = shiroFilterChainDefinition.getFilterChainMap(); 75 | 76 | shiroFilter.setFilterChainDefinitionMap(filterMap); 77 | return shiroFilter; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/controller/AccountController.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.controller; 2 | 3 | import cn.hutool.core.map.MapUtil; 4 | import cn.hutool.crypto.SecureUtil; 5 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 6 | import com.markerhub.common.dto.LoginDto; 7 | import com.markerhub.common.lang.Result; 8 | import com.markerhub.entity.User; 9 | import com.markerhub.service.UserService; 10 | import com.markerhub.util.JwtUtils; 11 | import org.apache.shiro.SecurityUtils; 12 | import org.apache.shiro.authz.annotation.RequiresAuthentication; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.util.Assert; 15 | import org.springframework.validation.annotation.Validated; 16 | import org.springframework.web.bind.annotation.GetMapping; 17 | import org.springframework.web.bind.annotation.PostMapping; 18 | import org.springframework.web.bind.annotation.RequestBody; 19 | import org.springframework.web.bind.annotation.RestController; 20 | 21 | import javax.servlet.http.HttpServletResponse; 22 | 23 | @RestController 24 | public class AccountController { 25 | 26 | @Autowired 27 | UserService userService; 28 | 29 | @Autowired 30 | JwtUtils jwtUtils; 31 | 32 | @PostMapping("/login") 33 | public Result login(@Validated @RequestBody LoginDto loginDto, HttpServletResponse response) { 34 | 35 | User user = userService.getOne(new QueryWrapper().eq("username", loginDto.getUsername())); 36 | Assert.notNull(user, "用户不存在"); 37 | 38 | if(!user.getPassword().equals(SecureUtil.md5(loginDto.getPassword()))){ 39 | return Result.fail("密码不正确"); 40 | } 41 | String jwt = jwtUtils.generateToken(user.getId()); 42 | 43 | response.setHeader("Authorization", jwt); 44 | response.setHeader("Access-control-Expose-Headers", "Authorization"); 45 | 46 | return Result.succ(MapUtil.builder() 47 | .put("id", user.getId()) 48 | .put("username", user.getUsername()) 49 | .put("avatar", user.getAvatar()) 50 | .put("email", user.getEmail()) 51 | .map() 52 | ); 53 | } 54 | 55 | @RequiresAuthentication 56 | @GetMapping("/logout") 57 | public Result logout() { 58 | SecurityUtils.getSubject().logout(); 59 | return Result.succ(null); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/controller/BlogController.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.controller; 2 | 3 | 4 | import cn.hutool.core.bean.BeanUtil; 5 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 6 | import com.baomidou.mybatisplus.core.metadata.IPage; 7 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 8 | import com.markerhub.common.lang.Result; 9 | import com.markerhub.entity.Blog; 10 | import com.markerhub.service.BlogService; 11 | import com.markerhub.util.ShiroUtil; 12 | import org.apache.shiro.authz.annotation.RequiresAuthentication; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.util.Assert; 15 | import org.springframework.validation.annotation.Validated; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import java.time.LocalDateTime; 19 | 20 | /** 21 | *

22 | * 前端控制器 23 | *

24 | * 25 | * @author 关注公众号:MarkerHub 26 | * @since 2020-05-25 27 | */ 28 | @RestController 29 | public class BlogController { 30 | 31 | @Autowired 32 | BlogService blogService; 33 | 34 | @GetMapping("/blogs") 35 | public Result list(@RequestParam(defaultValue = "1") Integer currentPage) { 36 | 37 | Page page = new Page(currentPage, 5); 38 | IPage pageData = blogService.page(page, new QueryWrapper().orderByDesc("created")); 39 | 40 | return Result.succ(pageData); 41 | } 42 | 43 | @GetMapping("/blog/{id}") 44 | public Result detail(@PathVariable(name = "id") Long id) { 45 | Blog blog = blogService.getById(id); 46 | Assert.notNull(blog, "该博客已被删除"); 47 | 48 | return Result.succ(blog); 49 | } 50 | 51 | @RequiresAuthentication 52 | @PostMapping("/blog/edit") 53 | public Result edit(@Validated @RequestBody Blog blog) { 54 | 55 | // Assert.isTrue(false, "公开版不能任意编辑!"); 56 | 57 | Blog temp = null; 58 | if(blog.getId() != null) { 59 | temp = blogService.getById(blog.getId()); 60 | // 只能编辑自己的文章 61 | System.out.println(ShiroUtil.getProfile().getId()); 62 | Assert.isTrue(temp.getUserId().longValue() == ShiroUtil.getProfile().getId().longValue(), "没有权限编辑"); 63 | 64 | } else { 65 | 66 | temp = new Blog(); 67 | temp.setUserId(ShiroUtil.getProfile().getId()); 68 | temp.setCreated(LocalDateTime.now()); 69 | temp.setStatus(0); 70 | } 71 | 72 | BeanUtil.copyProperties(blog, temp, "id", "userId", "created", "status"); 73 | blogService.saveOrUpdate(temp); 74 | 75 | return Result.succ(null); 76 | } 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.controller; 2 | 3 | 4 | import com.markerhub.common.lang.Result; 5 | import com.markerhub.entity.User; 6 | import com.markerhub.service.UserService; 7 | import org.apache.shiro.authz.annotation.RequiresAuthentication; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.validation.annotation.Validated; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | /** 13 | *

14 | * 前端控制器 15 | *

16 | * 17 | * @author 关注公众号:MarkerHub 18 | * @since 2020-05-25 19 | */ 20 | @RestController 21 | @RequestMapping("/user") 22 | public class UserController { 23 | 24 | @Autowired 25 | UserService userService; 26 | 27 | @RequiresAuthentication 28 | @GetMapping("/index") 29 | public Result index() { 30 | User user = userService.getById(1L); 31 | return Result.succ(user); 32 | } 33 | 34 | @PostMapping("/save") 35 | public Result save(@Validated @RequestBody User user) { 36 | return Result.succ(user); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/entity/Blog.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import com.fasterxml.jackson.annotation.JsonFormat; 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.experimental.Accessors; 10 | 11 | import javax.validation.constraints.NotBlank; 12 | import java.io.Serializable; 13 | import java.time.LocalDateTime; 14 | 15 | /** 16 | *

17 | * 18 | *

19 | * 20 | * @author 关注公众号:MarkerHub 21 | * @since 2020-05-25 22 | */ 23 | @Data 24 | @EqualsAndHashCode(callSuper = false) 25 | @Accessors(chain = true) 26 | @TableName("m_blog") 27 | public class Blog implements Serializable { 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | @TableId(value = "id", type = IdType.AUTO) 32 | private Long id; 33 | 34 | private Long userId; 35 | 36 | @NotBlank(message = "标题不能为空") 37 | private String title; 38 | 39 | @NotBlank(message = "摘要不能为空") 40 | private String description; 41 | 42 | @NotBlank(message = "内容不能为空") 43 | private String content; 44 | 45 | @JsonFormat(pattern="yyyy-MM-dd") 46 | private LocalDateTime created; 47 | 48 | private Integer status; 49 | 50 | 51 | } 52 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableId; 5 | import com.baomidou.mybatisplus.annotation.TableName; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.experimental.Accessors; 9 | 10 | import javax.validation.constraints.Email; 11 | import javax.validation.constraints.NotBlank; 12 | import java.io.Serializable; 13 | import java.time.LocalDateTime; 14 | 15 | /** 16 | *

17 | * 18 | *

19 | * 20 | * @author 关注公众号:MarkerHub 21 | * @since 2020-05-25 22 | */ 23 | @Data 24 | @EqualsAndHashCode(callSuper = false) 25 | @Accessors(chain = true) 26 | @TableName("m_user") 27 | public class User implements Serializable { 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | @TableId(value = "id", type = IdType.AUTO) 32 | private Long id; 33 | 34 | @NotBlank(message = "昵称不能为空") 35 | private String username; 36 | 37 | private String avatar; 38 | 39 | @NotBlank(message = "邮箱不能为空") 40 | @Email(message = "邮箱格式不正确") 41 | private String email; 42 | 43 | private String password; 44 | 45 | private Integer status; 46 | 47 | private LocalDateTime created; 48 | 49 | private LocalDateTime lastLogin; 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/mapper/BlogMapper.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.mapper; 2 | 3 | import com.markerhub.entity.Blog; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * Mapper 接口 9 | *

10 | * 11 | * @author 关注公众号:MarkerHub 12 | * @since 2020-05-25 13 | */ 14 | public interface BlogMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.mapper; 2 | 3 | import com.markerhub.entity.User; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * Mapper 接口 9 | *

10 | * 11 | * @author 关注公众号:MarkerHub 12 | * @since 2020-05-25 13 | */ 14 | public interface UserMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/service/BlogService.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.service; 2 | 3 | import com.markerhub.entity.Blog; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author 关注公众号:MarkerHub 12 | * @since 2020-05-25 13 | */ 14 | public interface BlogService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.service; 2 | 3 | import com.markerhub.entity.User; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author 关注公众号:MarkerHub 12 | * @since 2020-05-25 13 | */ 14 | public interface UserService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/service/impl/BlogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.service.impl; 2 | 3 | import com.markerhub.entity.Blog; 4 | import com.markerhub.mapper.BlogMapper; 5 | import com.markerhub.service.BlogService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 服务实现类 12 | *

13 | * 14 | * @author 关注公众号:MarkerHub 15 | * @since 2020-05-25 16 | */ 17 | @Service 18 | public class BlogServiceImpl extends ServiceImpl implements BlogService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.service.impl; 2 | 3 | import com.markerhub.entity.User; 4 | import com.markerhub.mapper.UserMapper; 5 | import com.markerhub.service.UserService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 服务实现类 12 | *

13 | * 14 | * @author 关注公众号:MarkerHub 15 | * @since 2020-05-25 16 | */ 17 | @Service 18 | public class UserServiceImpl extends ServiceImpl implements UserService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/shiro/AccountProfile.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.shiro; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | @Data 8 | public class AccountProfile implements Serializable { 9 | 10 | private Long id; 11 | 12 | private String username; 13 | 14 | private String avatar; 15 | 16 | private String email; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/shiro/AccountRealm.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.shiro; 2 | 3 | import cn.hutool.core.bean.BeanUtil; 4 | import com.markerhub.entity.User; 5 | import com.markerhub.service.UserService; 6 | import com.markerhub.util.JwtUtils; 7 | import org.apache.shiro.authc.*; 8 | import org.apache.shiro.authz.AuthorizationInfo; 9 | import org.apache.shiro.realm.AuthorizingRealm; 10 | import org.apache.shiro.subject.PrincipalCollection; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | public class AccountRealm extends AuthorizingRealm { 16 | 17 | @Autowired 18 | JwtUtils jwtUtils; 19 | 20 | @Autowired 21 | UserService userService; 22 | 23 | @Override 24 | public boolean supports(AuthenticationToken token) { 25 | return token instanceof JwtToken; 26 | } 27 | 28 | @Override 29 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 30 | return null; 31 | } 32 | 33 | @Override 34 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 35 | 36 | JwtToken jwtToken = (JwtToken) token; 37 | 38 | String userId = jwtUtils.getClaimByToken((String) jwtToken.getPrincipal()).getSubject(); 39 | 40 | User user = userService.getById(Long.valueOf(userId)); 41 | if (user == null) { 42 | throw new UnknownAccountException("账户不存在"); 43 | } 44 | 45 | if (user.getStatus() == -1) { 46 | throw new LockedAccountException("账户已被锁定"); 47 | } 48 | 49 | AccountProfile profile = new AccountProfile(); 50 | BeanUtil.copyProperties(user, profile); 51 | 52 | return new SimpleAuthenticationInfo(profile, jwtToken.getCredentials(), getName()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/shiro/JwtFilter.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.shiro; 2 | 3 | import cn.hutool.json.JSONUtil; 4 | import com.markerhub.common.lang.Result; 5 | import com.markerhub.util.JwtUtils; 6 | import io.jsonwebtoken.Claims; 7 | import org.apache.shiro.authc.AuthenticationException; 8 | import org.apache.shiro.authc.AuthenticationToken; 9 | import org.apache.shiro.authc.ExpiredCredentialsException; 10 | import org.apache.shiro.web.filter.authc.AuthenticatingFilter; 11 | import org.apache.shiro.web.util.WebUtils; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Component; 14 | import org.springframework.util.StringUtils; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | 17 | import javax.servlet.ServletRequest; 18 | import javax.servlet.ServletResponse; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | 23 | @Component 24 | public class JwtFilter extends AuthenticatingFilter { 25 | 26 | @Autowired 27 | JwtUtils jwtUtils; 28 | 29 | @Override 30 | protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { 31 | 32 | HttpServletRequest request = (HttpServletRequest) servletRequest; 33 | String jwt = request.getHeader("Authorization"); 34 | if(StringUtils.isEmpty(jwt)) { 35 | return null; 36 | } 37 | 38 | return new JwtToken(jwt); 39 | } 40 | 41 | @Override 42 | protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { 43 | 44 | HttpServletRequest request = (HttpServletRequest) servletRequest; 45 | String jwt = request.getHeader("Authorization"); 46 | if(StringUtils.isEmpty(jwt)) { 47 | return true; 48 | } else { 49 | 50 | // 校验jwt 51 | Claims claim = jwtUtils.getClaimByToken(jwt); 52 | if(claim == null || jwtUtils.isTokenExpired(claim.getExpiration())) { 53 | throw new ExpiredCredentialsException("token已失效,请重新登录"); 54 | } 55 | 56 | // 执行登录 57 | return executeLogin(servletRequest, servletResponse); 58 | } 59 | } 60 | 61 | @Override 62 | protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { 63 | 64 | HttpServletResponse httpServletResponse = (HttpServletResponse) response; 65 | 66 | Throwable throwable = e.getCause() == null ? e : e.getCause(); 67 | Result result = Result.fail(throwable.getMessage()); 68 | String json = JSONUtil.toJsonStr(result); 69 | 70 | try { 71 | httpServletResponse.getWriter().print(json); 72 | } catch (IOException ioException) { 73 | 74 | } 75 | return false; 76 | } 77 | 78 | @Override 79 | protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 80 | 81 | HttpServletRequest httpServletRequest = WebUtils.toHttp(request); 82 | HttpServletResponse httpServletResponse = WebUtils.toHttp(response); 83 | httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin")); 84 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE"); 85 | httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers")); 86 | // 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态 87 | if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) { 88 | httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value()); 89 | return false; 90 | } 91 | 92 | return super.preHandle(request, response); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/shiro/JwtToken.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.shiro; 2 | 3 | import org.apache.shiro.authc.AuthenticationToken; 4 | 5 | public class JwtToken implements AuthenticationToken { 6 | 7 | private String token; 8 | 9 | public JwtToken(String jwt) { 10 | this.token = jwt; 11 | } 12 | 13 | @Override 14 | public Object getPrincipal() { 15 | return token; 16 | } 17 | 18 | @Override 19 | public Object getCredentials() { 20 | return token; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/util/JwtUtils.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.util; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.SignatureAlgorithm; 6 | import lombok.Data; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.Date; 12 | 13 | /** 14 | * jwt工具类 15 | */ 16 | @Slf4j 17 | @Data 18 | @Component 19 | @ConfigurationProperties(prefix = "markerhub.jwt") 20 | public class JwtUtils { 21 | 22 | private String secret; 23 | private long expire; 24 | private String header; 25 | 26 | /** 27 | * 生成jwt token 28 | */ 29 | public String generateToken(long userId) { 30 | Date nowDate = new Date(); 31 | //过期时间 32 | Date expireDate = new Date(nowDate.getTime() + expire * 1000); 33 | 34 | return Jwts.builder() 35 | .setHeaderParam("typ", "JWT") 36 | .setSubject(userId+"") 37 | .setIssuedAt(nowDate) 38 | .setExpiration(expireDate) 39 | .signWith(SignatureAlgorithm.HS512, secret) 40 | .compact(); 41 | } 42 | 43 | public Claims getClaimByToken(String token) { 44 | try { 45 | return Jwts.parser() 46 | .setSigningKey(secret) 47 | .parseClaimsJws(token) 48 | .getBody(); 49 | }catch (Exception e){ 50 | log.debug("validate is token error ", e); 51 | return null; 52 | } 53 | } 54 | 55 | /** 56 | * token是否过期 57 | * @return true:过期 58 | */ 59 | public boolean isTokenExpired(Date expiration) { 60 | return expiration.before(new Date()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /vueblog-java/src/main/java/com/markerhub/util/ShiroUtil.java: -------------------------------------------------------------------------------- 1 | package com.markerhub.util; 2 | 3 | import com.markerhub.shiro.AccountProfile; 4 | import org.apache.shiro.SecurityUtils; 5 | 6 | public class ShiroUtil { 7 | 8 | public static AccountProfile getProfile() { 9 | return (AccountProfile) SecurityUtils.getSubject().getPrincipal(); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/META-INF/spring-devtools.properties: -------------------------------------------------------------------------------- 1 | restart.include.shiro-redis=/shiro-[\\w-\\.]+jar -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/application-default.yml: -------------------------------------------------------------------------------- 1 | # DataSource Config 2 | spring: 3 | datasource: 4 | driver-class-name: com.mysql.cj.jdbc.Driver 5 | url: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai 6 | username: root 7 | password: admin 8 | shiro-redis: 9 | enabled: true 10 | redis-manager: 11 | host: 127.0.0.1:6379 -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/application-pro.yml: -------------------------------------------------------------------------------- 1 | # DataSource Config 2 | spring: 3 | datasource: 4 | driver-class-name: com.mysql.cj.jdbc.Driver 5 | url: jdbc:mysql://mysql:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai 6 | username: root 7 | password: admin 8 | shiro-redis: 9 | enabled: true 10 | redis-manager: 11 | host: redis:6379 -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | mybatis-plus: 2 | mapper-locations: classpath*:/mapper/**Mapper.xml 3 | server: 4 | port: 8081 5 | markerhub: 6 | jwt: 7 | secret: f4e2e52034348f86b67cde581c0f9eb5 8 | expire: 604800 9 | header: Authorization -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/mapper/BlogMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /vueblog-java/src/main/resources/vueblog.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : pro-markerhub 5 | Source Server Version : 50727 6 | Source Host : 129.204.23.53:3306 7 | Source Database : vueblog 8 | 9 | Target Server Type : MYSQL 10 | Target Server Version : 50727 11 | File Encoding : 65001 12 | 13 | Date: 2020-10-20 13:07:15 14 | */ 15 | 16 | SET FOREIGN_KEY_CHECKS=0; 17 | 18 | -- ---------------------------- 19 | -- Table structure for m_blog 20 | -- ---------------------------- 21 | DROP TABLE IF EXISTS `m_blog`; 22 | CREATE TABLE `m_blog` ( 23 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 24 | `user_id` bigint(20) NOT NULL, 25 | `title` varchar(255) NOT NULL, 26 | `description` varchar(255) NOT NULL, 27 | `content` longtext, 28 | `created` datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP, 29 | `status` tinyint(4) DEFAULT NULL, 30 | PRIMARY KEY (`id`) 31 | ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4; 32 | 33 | -- ---------------------------- 34 | -- Records of m_blog 35 | -- ---------------------------- 36 | INSERT INTO `m_blog` VALUES ('1', '1', '生活就像海洋,只有意志坚强的人才能到达彼岸', '这里是摘要哈哈哈', '内容???', '2020-05-21 22:08:42', '0'); 37 | INSERT INTO `m_blog` VALUES ('2', '1', '最值得学习的博客项目eblog', 'eblog是一个基于Springboot2.1.2开发的博客学习项目,为了让项目融合更多的知识点,达到学习目的,编写了详细的从0到1开发文档。主要学习包括:自定义Freemarker标签,使用shiro+redis完成了会话共享,redis的zset结构完成本周热议排行榜,t-io+websocket完成即时消息通知和群聊,rabbitmq+elasticsearch完成博客内容搜索引擎等。值得学习的地方很多!', '**推荐阅读:**\r\n\r\n[分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!](https://mp.weixin.qq.com/s/jz6e977xP-OyaAKNjNca8w)\r\n\r\n[Github上最值得学习的100个Java开源项目,涵盖各种技术栈!](https://mp.weixin.qq.com/s/N-U0TaEUXnBFfBsmt_OESQ)\r\n\r\n[2020年最新的常问企业面试题大全以及答案](https://mp.weixin.qq.com/s/lR5LC5GnD2Gs59ecV5R0XA)', '2020-05-28 09:36:38', '0'); 38 | INSERT INTO `m_blog` VALUES ('3', '1', '关注公众号JavaCat,回复xshell或navicat获取破解对应工具', '视频中所用到的xshell和navicat直接获取哈!', '### 工具获取\r\n\r\n* xshell 6 绿色破解版:关注公众号:JavaCat,回复 xshell 获取\r\n* Navicat 11 简体中文版:关注公众号:JavaCat,回复 navicat 获取\r\n\r\n公众号二维码:\r\n\r\n![JavaCat](//image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20201020/7fa16a1f957f4cfebe7be1f6675f6f36.png \"JavaCat\")\r\n\r\n直接扫码回复对应关键字\r\n\r\n**推荐阅读:**\r\n\r\n[B站86K播放量,SpringBoot+Vue前后端分离完整入门教程!](https://mp.weixin.qq.com/s/jGEkHTf2X8l-wUenc-PpEw)\r\n\r\n[分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!](https://mp.weixin.qq.com/s/jz6e977xP-OyaAKNjNca8w)\r\n\r\n[Github上最值得学习的100个Java开源项目,涵盖各种技术栈!](https://mp.weixin.qq.com/s/N-U0TaEUXnBFfBsmt_OESQ)\r\n\r\n[2020年最新的常问企业面试题大全以及答案](https://mp.weixin.qq.com/s/lR5LC5GnD2Gs59ecV5R0XA)', '2020-10-20 05:05:31', '0'); 39 | INSERT INTO `m_blog` VALUES ('7', '1', '你真的会写单例模式吗?', '单例模式可能是代码最少的模式了,但是少不一定意味着简单,想要用好、用对单例模式,还真得费一番脑筋。本文对 Java 中常见的单例模式写法做了一个总结,如有错漏之处,恳请读者指正。', '> 作者:吃桔子的攻城狮 来源:http://www.tekbroaden.com/singleton-java.html\n\n\n单例模式可能是代码最少的模式了,但是少不一定意味着简单,想要用好、用对单例模式,还真得费一番脑筋。本文对 Java 中常见的单例模式写法做了一个总结,如有错漏之处,恳请读者指正。\n\n饿汉法\n===\n\n顾名思义,饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。代码如下:\n\n```\npublic class Singleton { \n private static Singleton = new Singleton();\n private Singleton() {}\n public static getSignleton(){\n return singleton;\n }\n}\n\n```\n\n这样做的好处是编写简单,但是无法做到延迟创建对象。但是我们很多时候都希望对象可以尽可能地延迟加载,从而减小负载,所以就需要下面的懒汉法:\n', '2020-05-22 00:42:44', '0'); 40 | INSERT INTO `m_blog` VALUES ('9', '1', '真正理解Mysql的四种隔离级别@', '事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。\n\n事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。', '### 什么是事务 \n\n> 事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。\n> \n> 事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。\n\n**事务的 ACID**\n\n事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。\n\n> 1 、原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做\n> \n> 2 、一致性。事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。', '2020-05-22 22:04:46', '0'); 41 | INSERT INTO `m_blog` VALUES ('10', '1', '博客项目eblog讲解视频上线啦,长达17个小时!!', '1. 慕课网免费资源好久都没更新了,新教程大都付费\n2. B站上的视频繁多,通过收藏和弹幕数量通常很容易判断出视频是否优质\n3. 讲真,B站的弹幕文化,让我觉得,我不是一个在学习,自古人才出评论。哈哈哈\n4. B站视频通常广告少,up主的用心录制,通常只为了你关注他', 'ok,再回到我们的eblog项目,源码、文档、视频我都开源出来了。来些基本操作:github上给个star,B站视频给个三连支持咧。\n\neblog源码:https://github.com/MarkerHub/eblog\n\n点击这里:[10+篇完整开发文档](https://mp.weixin.qq.com/mp/homepage?__biz=MzIwODkzOTc1MQ==&hid=1&sn=8e512316c3dfe140e636d0c996951166)\n\n![](//image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20200508/c290d945b7d24c79b172759bdb5b94e0.png)\n\n视频讲解:(记得关注我噢!)\n\nhttps://www.bilibili.com/video/BV1ri4y1x71A\n\n![](//image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20200508/983b5abc1c934360a1a1362347a275f7.png)\n\n项目其实还很多bug的,哈哈,我还需要进行二次迭代,到时候再发迭代文档出来。\n\n关注下我的B站,作为一个自媒体的自由职业者,没有什么比涨粉更让我开心的了,嘻嘻。\n\n近期即将推出的视频教程:\n\n1. 搭建脚手架,前后端分离首秀\n2. Shiro入门到精通教程\n3. SpringBoot2.2.6最新入门教程', '2020-05-22 22:05:49', '0'); 42 | 43 | -- ---------------------------- 44 | -- Table structure for m_user 45 | -- ---------------------------- 46 | DROP TABLE IF EXISTS `m_user`; 47 | CREATE TABLE `m_user` ( 48 | `id` bigint(20) NOT NULL AUTO_INCREMENT, 49 | `username` varchar(64) DEFAULT NULL, 50 | `avatar` varchar(255) DEFAULT NULL, 51 | `email` varchar(64) DEFAULT NULL, 52 | `password` varchar(64) DEFAULT NULL, 53 | `status` int(5) NOT NULL, 54 | `created` datetime DEFAULT NULL, 55 | `last_login` datetime DEFAULT NULL, 56 | PRIMARY KEY (`id`), 57 | KEY `UK_USERNAME` (`username`) USING BTREE 58 | ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 59 | 60 | -- ---------------------------- 61 | -- Records of m_user 62 | -- ---------------------------- 63 | INSERT INTO `m_user` VALUES ('1', 'markerhub', 'https://image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/5a9f48118166308daba8b6da7e466aab.jpg', null, '96e79218965eb72c92a549dd5a330112', '0', '2020-04-20 10:44:01', null); 64 | -------------------------------------------------------------------------------- /vueblog-java/src/test/java/com/markerhub/VueblogApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.markerhub; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class VueblogApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /vueblog-vue/README.md: -------------------------------------------------------------------------------- 1 | ### 工具获取 2 | 3 | * xshell 6 绿色破解版:关注公众号:JavaCat,回复 xshell 获取 4 | * Navicat 11 简体中文版:关注公众号:JavaCat,回复 navicat 获取 5 | 6 | 公众号二维码: 7 | 8 | ![JavaCat](//image-1300566513.cos.ap-guangzhou.myqcloud.com/upload/images/20201020/7fa16a1f957f4cfebe7be1f6675f6f36.png "JavaCat") 9 | 10 | 直接扫码回复对应关键字 11 | 12 | **推荐阅读:** 13 | 14 | [B站86K播放量,SpringBoot+Vue前后端分离完整入门教程!](https://mp.weixin.qq.com/s/jGEkHTf2X8l-wUenc-PpEw) 15 | 16 | [分享一套SpringBoot开发博客系统源码,以及完整开发文档!速度保存!](https://mp.weixin.qq.com/s/jz6e977xP-OyaAKNjNca8w) 17 | 18 | [Github上最值得学习的100个Java开源项目,涵盖各种技术栈!](https://mp.weixin.qq.com/s/N-U0TaEUXnBFfBsmt_OESQ) 19 | 20 | [2020年最新的常问企业面试题大全以及答案](https://mp.weixin.qq.com/s/lR5LC5GnD2Gs59ecV5R0XA) -------------------------------------------------------------------------------- /vueblog-vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /vueblog-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vueblog-vue", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.19.2", 11 | "core-js": "^3.6.4", 12 | "element-ui": "^2.13.1", 13 | "markdown-it": "^11.0.0", 14 | "mavon-editor": "^2.9.0", 15 | "vue": "^2.6.11", 16 | "vue-router": "^3.1.6", 17 | "vuex": "^3.1.3" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "~4.3.0", 21 | "@vue/cli-plugin-router": "~4.3.0", 22 | "@vue/cli-plugin-vuex": "~4.3.0", 23 | "@vue/cli-service": "~4.3.0", 24 | "vue-template-compiler": "^2.6.11" 25 | }, 26 | "browserslist": [ 27 | "> 1%", 28 | "last 2 versions", 29 | "not dead" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /vueblog-vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkerHub/vueblog/c042ee4a37e865c08addc680af26428de7e8ed89/vueblog-vue/public/favicon.ico -------------------------------------------------------------------------------- /vueblog-vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /vueblog-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /vueblog-vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkerHub/vueblog/c042ee4a37e865c08addc680af26428de7e8ed89/vueblog-vue/src/assets/logo.png -------------------------------------------------------------------------------- /vueblog-vue/src/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import Element from 'element-ui' 3 | import router from './router' 4 | import store from './store' 5 | 6 | 7 | axios.defaults.baseURL = "http://localhost:8081" 8 | 9 | // 前置拦截 10 | axios.interceptors.request.use(config => { 11 | return config 12 | }) 13 | 14 | axios.interceptors.response.use(response => { 15 | let res = response.data; 16 | 17 | console.log("=================") 18 | console.log(res) 19 | console.log("=================") 20 | 21 | if (res.code === 200) { 22 | return response 23 | } else { 24 | 25 | Element.Message.error('错了哦,这是一条错误消息', {duration: 3 * 1000}) 26 | 27 | return Promise.reject(response.data.msg) 28 | } 29 | }, 30 | error => { 31 | console.log(error) 32 | if(error.response.data) { 33 | error.message = error.response.data.msg 34 | } 35 | 36 | if(error.response.status === 401) { 37 | store.commit("REMOVE_INFO") 38 | router.push("/login") 39 | } 40 | 41 | Element.Message.error(error.message, {duration: 3 * 1000}) 42 | return Promise.reject(error) 43 | } 44 | ) -------------------------------------------------------------------------------- /vueblog-vue/src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 60 | 61 | -------------------------------------------------------------------------------- /vueblog-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | import Element from 'element-ui' 6 | import axios from 'axios' 7 | 8 | import mavonEditor from 'mavon-editor' 9 | 10 | import "element-ui/lib/theme-chalk/index.css" 11 | import 'mavon-editor/dist/css/index.css' 12 | 13 | import "./axios" 14 | import "./permission" 15 | 16 | Vue.use(Element) 17 | Vue.use(mavonEditor) 18 | 19 | Vue.config.productionTip = false 20 | Vue.prototype.$axios = axios 21 | 22 | new Vue({ 23 | router, 24 | store, 25 | render: h => h(App) 26 | }).$mount('#app') 27 | -------------------------------------------------------------------------------- /vueblog-vue/src/permission.js: -------------------------------------------------------------------------------- 1 | import router from "./router"; 2 | 3 | // 路由判断登录 根据路由配置文件的参数 4 | router.beforeEach((to, from, next) => { 5 | 6 | if (to.matched.some(record => record.meta.requireAuth)) { // 判断该路由是否需要登录权限 7 | 8 | const token = localStorage.getItem("token") 9 | console.log("------------" + token) 10 | 11 | if (token) { // 判断当前的token是否存在 ; 登录存入的token 12 | if (to.path === '/login') { 13 | 14 | } else { 15 | next() 16 | } 17 | } else { 18 | next({ 19 | path: '/login' 20 | }) 21 | } 22 | } else { 23 | next() 24 | } 25 | }) -------------------------------------------------------------------------------- /vueblog-vue/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Login from '../views/Login.vue' 4 | import Blogs from '../views/Blogs.vue' 5 | import BlogEdit from '../views/BlogEdit.vue' 6 | import BlogDetail from '../views/BlogDetail.vue' 7 | 8 | Vue.use(VueRouter) 9 | 10 | const routes = [ 11 | { 12 | path: '/', 13 | name: 'Index', 14 | redirect: {name: "Blogs"} 15 | }, 16 | { 17 | path: '/blogs', 18 | name: 'Blogs', 19 | component: Blogs 20 | }, 21 | { 22 | path: '/login', 23 | name: 'Login', 24 | component: Login 25 | }, 26 | { 27 | path: '/blog/add', 28 | name: 'BlogAdd', 29 | component: BlogEdit, 30 | meta: { 31 | requireAuth: true 32 | } 33 | }, 34 | { 35 | path: '/blog/:blogId', 36 | name: 'BlogDetail', 37 | component: BlogDetail 38 | }, 39 | { 40 | path: '/blog/:blogId/edit', 41 | name: 'BlogEdit', 42 | component: BlogEdit, 43 | meta: { 44 | requireAuth: true 45 | } 46 | } 47 | ] 48 | 49 | const router = new VueRouter({ 50 | mode: 'history', 51 | base: process.env.BASE_URL, 52 | routes 53 | }) 54 | 55 | export default router 56 | -------------------------------------------------------------------------------- /vueblog-vue/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | token: '', 9 | userInfo: JSON.parse(sessionStorage.getItem("userInfo")) 10 | }, 11 | mutations: { 12 | // set 13 | SET_TOKEN: (state, token) => { 14 | state.token = token 15 | localStorage.setItem("token", token) 16 | }, 17 | SET_USERINFO: (state, userInfo) => { 18 | state.userInfo = userInfo 19 | sessionStorage.setItem("userInfo", JSON.stringify(userInfo)) 20 | }, 21 | REMOVE_INFO: (state) => { 22 | state.token = '' 23 | state.userInfo = {} 24 | localStorage.setItem("token", '') 25 | sessionStorage.setItem("userInfo", JSON.stringify('')) 26 | } 27 | 28 | }, 29 | getters: { 30 | // get 31 | getUser: state => { 32 | return state.userInfo 33 | } 34 | 35 | }, 36 | actions: { 37 | }, 38 | modules: { 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /vueblog-vue/src/views/BlogDetail.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 57 | 58 | -------------------------------------------------------------------------------- /vueblog-vue/src/views/BlogEdit.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 108 | 109 | -------------------------------------------------------------------------------- /vueblog-vue/src/views/Blogs.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 67 | 68 | -------------------------------------------------------------------------------- /vueblog-vue/src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 82 | 83 | -------------------------------------------------------------------------------- /vueblog-vue/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: '/' 3 | } --------------------------------------------------------------------------------