├── src ├── main │ ├── resources │ │ ├── application-test.yml │ │ ├── application-prod.yml │ │ ├── application.yml │ │ ├── application-dev.yml │ │ ├── mapper │ │ │ └── UserMapper.xml │ │ └── banner.txt │ └── java │ │ └── com │ │ └── company │ │ └── project │ │ ├── dao │ │ └── UserMapper.java │ │ ├── service │ │ ├── IUserService.java │ │ └── impl │ │ │ └── UserServiceImpl.java │ │ ├── core │ │ ├── ServiceException.java │ │ ├── ResultCode.java │ │ ├── ResultGenerator.java │ │ ├── ApplicationContextUtil.java │ │ └── Result.java │ │ ├── configurer │ │ ├── MyBatisPlusConfig.java │ │ ├── SwaggerConfiguration.java │ │ ├── LoginInterceptor.java │ │ └── WebMvcConfigurer.java │ │ ├── utils │ │ ├── MD5Utils.java │ │ └── JwtUtils.java │ │ ├── model │ │ └── User.java │ │ ├── Application.java │ │ └── web │ │ └── UserController.java └── test │ ├── java │ ├── com │ │ └── company │ │ │ └── project │ │ │ └── Tester.java │ └── CodeGenerator.java │ └── resources │ ├── user.sql │ └── templates │ └── controller.java.ftl ├── .gitignore ├── README.md └── pom.xml /src/main/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | # 测试环境配置 2 | -------------------------------------------------------------------------------- /src/main/resources/application-prod.yml: -------------------------------------------------------------------------------- 1 | # 生产环境配置 2 | knife4j: 3 | production: true #生成环境禁用查看接口文档 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | 3 | ### STS ### 4 | .apt_generated 5 | .classpath 6 | .factorypath 7 | .project 8 | .settings 9 | .springBeans 10 | 11 | ### IntelliJ IDEA ### 12 | .idea 13 | *.iws 14 | *.iml 15 | *.ipr 16 | 17 | ### NetBeans ### 18 | nbproject/private/ 19 | build/ 20 | nbbuild/ 21 | dist/ 22 | nbdist/ 23 | .nb-gradle/ -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: dev 4 | mvc: 5 | throw-exception-if-no-handler-found: true 6 | resources: 7 | add-mappings: false 8 | application: 9 | name: Springboot-api 10 | jackson: 11 | date-format: yyyy-MM-dd HH:mm:ss 12 | time-zone: GMT+8 13 | server: 14 | port: 8080 15 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/dao/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.company.project.dao; 2 | 3 | import com.company.project.model.User; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | 6 | /** 7 | *

8 | * Mapper 接口 9 | *

10 | * 11 | * @author project 12 | * @since 2020-01-08 13 | */ 14 | public interface UserMapper extends BaseMapper { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package com.company.project.service; 2 | 3 | import com.company.project.model.User; 4 | import com.baomidou.mybatisplus.extension.service.IService; 5 | 6 | /** 7 | *

8 | * 服务类 9 | *

10 | * 11 | * @author project 12 | * @since 2020-01-08 13 | */ 14 | public interface IUserService extends IService { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/core/ServiceException.java: -------------------------------------------------------------------------------- 1 | package com.company.project.core; 2 | 3 | /** 4 | * 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer 5 | */ 6 | public class ServiceException extends RuntimeException { 7 | public ServiceException() { 8 | } 9 | 10 | public ServiceException(String message) { 11 | super(message); 12 | } 13 | 14 | public ServiceException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/company/project/Tester.java: -------------------------------------------------------------------------------- 1 | package com.company.project; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | /** 9 | * 单元测试 10 | */ 11 | @RunWith(SpringRunner.class) 12 | @SpringBootTest(classes = Application.class) 13 | public class Tester { 14 | 15 | @Test 16 | public void test() { 17 | 18 | } 19 | } 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/core/ResultCode.java: -------------------------------------------------------------------------------- 1 | package com.company.project.core; 2 | 3 | /** 4 | * 响应码枚举,参考HTTP状态码的语义 5 | */ 6 | public enum ResultCode { 7 | SUCCESS(200),//成功 8 | FAIL(400),//失败 9 | UNAUTHORIZED(401),//未认证(签名错误) 10 | NOT_FOUND(404),//接口不存在 11 | INTERNAL_SERVER_ERROR(500),//服务器内部错误 12 | PARAM_FAIL(10001);//参数异常 13 | 14 | private final int code; 15 | 16 | ResultCode(int code) { 17 | this.code = code; 18 | } 19 | 20 | public int code() { 21 | return code; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.company.project.service.impl; 2 | 3 | import com.company.project.model.User; 4 | import com.company.project.dao.UserMapper; 5 | import com.company.project.service.IUserService; 6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | *

11 | * 服务实现类 12 | *

13 | * 14 | * @author project 15 | * @since 2020-01-08 16 | */ 17 | @Service 18 | public class UserServiceImpl extends ServiceImpl implements IUserService { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/configurer/MyBatisPlusConfig.java: -------------------------------------------------------------------------------- 1 | package com.company.project.configurer; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @ClassName MyBatisPlusConfig 9 | * @Version 1.0 10 | **/ 11 | @Configuration 12 | public class MyBatisPlusConfig { 13 | /** 14 | * 配置mybatis-plus 分页查件 15 | * @return 16 | */ 17 | @Bean 18 | public PaginationInterceptor paginationInterceptor() { 19 | PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); 20 | return paginationInterceptor; 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/com/company/project/core/ResultGenerator.java: -------------------------------------------------------------------------------- 1 | package com.company.project.core; 2 | 3 | /** 4 | * 响应结果生成工具 5 | */ 6 | public class ResultGenerator { 7 | private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; 8 | 9 | public static Result genSuccessResult() { 10 | return new Result() 11 | .setSuccess(true) 12 | .setCode(ResultCode.SUCCESS) 13 | .setMessage(DEFAULT_SUCCESS_MESSAGE); 14 | } 15 | 16 | public static Result genSuccessResult(T data) { 17 | return new Result() 18 | .setSuccess(true) 19 | .setCode(ResultCode.SUCCESS) 20 | .setMessage(DEFAULT_SUCCESS_MESSAGE) 21 | .setData(data); 22 | } 23 | 24 | public static Result genFailResult(String message) { 25 | return new Result() 26 | .setSuccess(false) 27 | .setCode(ResultCode.FAIL) 28 | .setMessage(message); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/core/ApplicationContextUtil.java: -------------------------------------------------------------------------------- 1 | package com.company.project.core; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @className: ApplicationContextUtil 10 | * @Description: 解决定时任务获取不到service的问题 11 | * @Author moneylee 12 | * @Date 2019-05-11 14:28 13 | * @Version 1.0 14 | **/ 15 | @Component 16 | public class ApplicationContextUtil implements ApplicationContextAware { 17 | 18 | private static ApplicationContext applicationContext; 19 | 20 | public static ApplicationContext getApplicationContext() { 21 | return applicationContext; 22 | } 23 | 24 | 25 | @Override 26 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 27 | ApplicationContextUtil.applicationContext = applicationContext; 28 | 29 | } 30 | 31 | public static Object getBean(String beanName) { 32 | return applicationContext.getBean(beanName); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/java/com/company/project/core/Result.java: -------------------------------------------------------------------------------- 1 | package com.company.project.core; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | 5 | /** 6 | * 统一API响应结果封装 7 | */ 8 | public class Result { 9 | private int code; 10 | private String message; 11 | private T data; 12 | private Boolean success; 13 | 14 | public Result setCode(ResultCode resultCode) { 15 | this.code = resultCode.code(); 16 | return this; 17 | } 18 | 19 | public int getCode() { 20 | return code; 21 | } 22 | 23 | public String getMessage() { 24 | return message; 25 | } 26 | 27 | public Result setMessage(String message) { 28 | this.message = message; 29 | return this; 30 | } 31 | 32 | public T getData() { 33 | return data; 34 | } 35 | 36 | public Result setData(T data) { 37 | this.data = data; 38 | return this; 39 | } 40 | 41 | public Boolean getSuccess() { 42 | return success; 43 | } 44 | 45 | public Result setSuccess(Boolean success) { 46 | this.success = success; 47 | return this; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return JSON.toJSONString(this); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | # 开发环境配置 2 | spring: 3 | datasource: 4 | dynamic: 5 | primary: master #设置默认的数据源或者数据源组,默认值即为master 6 | datasource: 7 | master: 8 | username: root 9 | password: 123456 10 | driver-class-name: com.mysql.cj.jdbc.Driver 11 | url: jdbc:mysql://localhost:3306/project?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2b8 12 | slave_1: 13 | username: root 14 | password: 123456 15 | driver-class-name: com.mysql.cj.jdbc.Driver 16 | url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2b8 17 | slave_2: 18 | username: root 19 | password: 123456 20 | driver-class-name: com.mysql.cj.jdbc.Driver 21 | url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2b8 22 | #......省略 23 | #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2 24 | mybatis-plus: 25 | configuration: 26 | log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 27 | mapper-locations: classpath:mapper/*.xml 28 | global-config: 29 | db-config: 30 | logic-delete-value: 1 31 | logic-not-delete-value: 0 32 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | id, username, password, nick_name, sex, create_date, create_user, update_date, update_user, del_flag 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/utils/MD5Utils.java: -------------------------------------------------------------------------------- 1 | package com.company.project.utils; 2 | 3 | import org.apache.commons.codec.digest.DigestUtils; 4 | import org.apache.commons.lang3.ArrayUtils; 5 | import org.apache.commons.lang3.StringUtils; 6 | 7 | public class MD5Utils { 8 | 9 | private static String salt = "springboot_api"; 10 | 11 | /** 12 | * 加密字符串 13 | * @param password 要加密的明文 14 | * @param isAddSalt 是否加默认盐 15 | * @return 加密之后的结果 16 | */ 17 | public static String Encrypt(String password, boolean isAddSalt){ 18 | if (StringUtils.isNotEmpty(password)){ 19 | if (isAddSalt){ 20 | return DigestUtils.md5Hex(DigestUtils.md5(password + salt)); 21 | } else { 22 | return DigestUtils.md5Hex(DigestUtils.md5(password)); 23 | } 24 | } 25 | return null; 26 | } 27 | 28 | /** 29 | * 30 | * @param bytes 31 | * @return 32 | */ 33 | public static String Encrypt(byte[] bytes){ 34 | if (ArrayUtils.isNotEmpty(bytes)){ 35 | return DigestUtils.md5Hex(DigestUtils.md5(bytes)); 36 | } 37 | return null; 38 | } 39 | 40 | /** 41 | * MD5加盐加密 42 | * @param password 要加密的明文 43 | * @param salt 盐 44 | * @return 加密之后的结果 45 | */ 46 | public static String Encrypt(String password, String salt){ 47 | if (StringUtils.isNotEmpty(password)){ 48 | return DigestUtils.md5Hex(DigestUtils.md5(password + salt)); 49 | } 50 | return null; 51 | } 52 | 53 | public static void main(String[] args){ 54 | System.out.println(MD5Utils.Encrypt("admin", true)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////// 2 | // _ooOoo_ // 3 | // o8888888o // 4 | // 88" . "88 // 5 | // (| ^_^ |) // 6 | // O\ = /O // 7 | // ____/`---'\____ // 8 | // .' \\| |// `. // 9 | // / \\||| : |||// \ // 10 | // / _||||| -:- |||||- \ // 11 | // | | \\\ - /// | | // 12 | // | \_| ''\---/'' | | // 13 | // \ .-\__ `-` ___/-. / // 14 | // ___`. .' /--.--\ `. . ___ // 15 | // ."" '< `.___\_<|>_/___.' >'"". // 16 | // | | : `- \`.;`\ _ /`;.`/ - ` : | | // 17 | // \ \ `-. \_ __\ /__ _/ .-` / / // 18 | // ========`-.____`-.___\_____/___.-`____.-'======== // 19 | // `=---=' // 20 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // 21 | // 佛祖保佑 永不宕机 永无BUG // 22 | //////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /src/main/java/com/company/project/configurer/SwaggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.company.project.configurer; 2 | 3 | import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.context.annotation.Import; 7 | import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; 8 | import springfox.documentation.builders.ApiInfoBuilder; 9 | import springfox.documentation.builders.PathSelectors; 10 | import springfox.documentation.builders.RequestHandlerSelectors; 11 | import springfox.documentation.service.ApiInfo; 12 | import springfox.documentation.spi.DocumentationType; 13 | import springfox.documentation.spring.web.plugins.Docket; 14 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 15 | 16 | @Configuration 17 | @EnableSwagger2 18 | @EnableKnife4j 19 | @Import(BeanValidatorPluginsConfiguration.class) 20 | public class SwaggerConfiguration { 21 | 22 | @Bean 23 | public Docket createRestApi() { 24 | return new Docket(DocumentationType.SWAGGER_2) 25 | .apiInfo(apiInfo()) 26 | .select() 27 | .apis(RequestHandlerSelectors.basePackage("com.company.project.web")) 28 | .paths(PathSelectors.any()) 29 | .build(); 30 | } 31 | 32 | private ApiInfo apiInfo() { 33 | return new ApiInfoBuilder() 34 | .title("Springboot-api APIs") 35 | .description("Springboot-api APIs") 36 | .termsOfServiceUrl("http://localhost:8080/") 37 | .contact("xxxxxxxxxx@163.com") 38 | .version("1.0") 39 | .build(); 40 | } 41 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | Spring Boot API 是一个基于Spring Boot & MyBatis plus的种子项目,用于快速构建中小型API项目,特点稳定、简单、快速,摆脱那些重复劳动 3 | 4 | ## 特征&提供 5 | - 统一响应结果封装及生成工具 6 | - 统一异常处理 7 | - 采用简单的jwt认证 8 | - 使用Druid Spring Boot Starter 集成Druid数据库连接池与监控 9 | - 集成MyBatis-Plus,实现单表业务零SQL 10 | - 支持多数据源,自由切换,只需方法或类上用 @DS 切换数据源 11 | - 集成国人风格的knife4j,自动生成接口文档 12 | - 提供代码生成器,生成controller,service,serviceImpl,dao,mapper.xml 13 | 14 | ## 快速开始 15 | 1. 克隆项目 16 | 2. 导入```test```包里的mysql脚本user.sql 17 | 3. 对```test```包内的代码生成器```CodeGenerator```进行配置,主要是JDBC,因为要根据表名来生成代码 18 | 4. 输入表名,运行```CodeGenerator.main()```方法,生成基础代码(可能需要刷新项目目录才会出来) 19 | 5. 根据业务在基础代码上进行扩展 20 | 6. 对开发环境配置文件```application-dev.yml```进行配置,启动项目,Have Fun! 21 | 22 | ## 开发建议 23 | - post调用接口ip:8080/api/user/login,参数json: {"username":"admin","password":"123456"},调用成功后, 返回token。以后调用api接口,header中传token 24 | - 正式环境已禁用接口文档的查看,配置文件添加knife4j:production: true 即可 25 | - Model内成员变量建议与表字段数量对应,如需扩展成员变量(比如连表查询)建议创建DTO,否则需在扩展的成员变量上加@TableField(exist = false),详见[MyBatis-Plus](https://mp.baomidou.com/guide/)文档说明 26 | - 建议业务失败直接使用ServiceException("ErrorMessage")抛出,由统一异常处理器来封装业务失败的响应结果,会直接被封装为{"code":400,"message":"ErrorMessage"}返回,尽情抛出;body方式传参,@Valid校验Model,更无需自己处理; 27 | 28 | ## 接口文档效果图 29 | ![image-20200313084433855](http://tuchuang.aitangbao.com.cn/image-20200313084433855.png) 30 | 31 | ## 相关文档 32 | - Spring Boot([springboot官方](https://spring.io/projects/spring-boot/)) 33 | - MyBatis-Plus ([查看官方中文文档](https://mp.baomidou.com/guide/)) 34 | - MyBatis-Plus分页插件([查看官方中文文档](https://mp.baomidou.com/guide/page.html)) 35 | - Druid Spring Boot Starter([查看官方中文文档](https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter/)) 36 | - Fastjson([查看官方中文文档](https://github.com/Alibaba/fastjson/wiki/%E9%A6%96%E9%A1%B5)) 37 | - 阿里巴巴Java开发手册[最新版下载](https://github.com/alibaba/p3c) 38 | 其他 39 | 40 | ## License 41 | 纯粹开源分享,感谢大家 [Star](https://github.com/aitangbao/springboot-api) 的支持。 42 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/model/User.java: -------------------------------------------------------------------------------- 1 | package com.company.project.model; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableLogic; 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | import lombok.Data; 11 | import lombok.EqualsAndHashCode; 12 | import lombok.experimental.Accessors; 13 | 14 | import javax.validation.constraints.NotEmpty; 15 | 16 | /** 17 | *

18 | * 19 | *

20 | * 21 | * @author project 22 | * @since 2020-01-08 23 | */ 24 | @Data 25 | @EqualsAndHashCode(callSuper = false) 26 | @Accessors(chain = true) 27 | public class User implements Serializable { 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | /** 32 | * 主键 33 | */ 34 | @TableId(value = "id", type = IdType.AUTO) 35 | private Integer id; 36 | 37 | /** 38 | * 用户名 39 | */ 40 | @NotEmpty(message = "用户名不能为空!") 41 | private String username; 42 | 43 | /** 44 | * 密码 45 | */ 46 | @NotEmpty(message = "密码不能为空!") 47 | private String password; 48 | 49 | /** 50 | * 昵称 51 | */ 52 | private String nickName; 53 | 54 | /** 55 | * 性别 56 | */ 57 | private Integer sex; 58 | 59 | /** 60 | * 创建时间 61 | */ 62 | private Date createDate; 63 | 64 | /** 65 | * 创建人 66 | */ 67 | private Integer createUser; 68 | 69 | /** 70 | * 修改时间 71 | */ 72 | private Date updateDate; 73 | 74 | /** 75 | * 修改人 76 | */ 77 | private Integer updateUser; 78 | 79 | /** 80 | * 删除标志0未删 1删除 81 | */ 82 | @TableLogic 83 | private Integer delFlag; 84 | 85 | /** 86 | * 登陆返回token 87 | */ 88 | @TableField(exist = false) 89 | private String token; 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/Application.java: -------------------------------------------------------------------------------- 1 | package com.company.project; 2 | 3 | import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.context.ConfigurableApplicationContext; 10 | import org.springframework.core.env.Environment; 11 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 12 | 13 | import java.net.InetAddress; 14 | 15 | @SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class) 16 | @MapperScan("com.company.project.dao") 17 | public class Application { 18 | 19 | private static Logger logger= LoggerFactory.getLogger(Application.class); 20 | 21 | public static void main(String[] args) throws Exception { 22 | 23 | ConfigurableApplicationContext application = SpringApplication.run(Application.class, args); 24 | 25 | Environment env = application.getEnvironment(); 26 | logger.info("\n----------------------------------------------------------\n\t" + 27 | "Application '{}' is running! Access URLs:\n\t" + 28 | "Local: \t\thttp://localhost:{}\n\t" + 29 | "External: \thttp://{}:{}\n\t" + 30 | "Doc: \thttp://{}:{}/doc.html\n" + 31 | "----------------------------------------------------------", 32 | env.getProperty("spring.application.name"), 33 | env.getProperty("server.port"), 34 | InetAddress.getLocalHost().getHostAddress(), 35 | env.getProperty("server.port"), 36 | InetAddress.getLocalHost().getHostAddress(), 37 | env.getProperty("server.port")); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/utils/JwtUtils.java: -------------------------------------------------------------------------------- 1 | package com.company.project.utils; 2 | 3 | import com.company.project.model.User; 4 | import io.jsonwebtoken.Claims; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.util.Date; 10 | 11 | /** 12 | * jwt工具类 13 | */ 14 | public class JwtUtils { 15 | 16 | public static final String USER_ID_KEY = "user_id_key"; 17 | 18 | public static final String USER_ACCOUNT_KEY = "user_account_key"; 19 | 20 | 21 | public static final String SUBJECT = "onehee"; 22 | 23 | public static final long EXPIRE = 1000*60*60*24*7; //过期时间,毫秒,一周 24 | 25 | //秘钥 26 | public static final String APPSECRET = "onehee666"; 27 | 28 | /** 29 | * 生成jwt 30 | * @param user 31 | * @return 32 | */ 33 | public static String geneJsonWebToken(User user){ 34 | 35 | String token = Jwts.builder().setSubject(SUBJECT) 36 | .claim("id",user.getId()) 37 | .claim("userName",user.getUsername()) 38 | .setIssuedAt(new Date()) 39 | .setExpiration(new Date(System.currentTimeMillis()+EXPIRE)) 40 | .signWith(SignatureAlgorithm.HS256,APPSECRET).compact(); 41 | 42 | return token; 43 | } 44 | 45 | 46 | /** 47 | * 校验token 48 | * @param token 49 | * @return 50 | */ 51 | public static Claims checkJWT(String token ){ 52 | 53 | try{ 54 | final Claims claims = Jwts.parser().setSigningKey(APPSECRET). 55 | parseClaimsJws(token).getBody(); 56 | return claims; 57 | 58 | }catch (Exception e){ } 59 | return null; 60 | 61 | } 62 | 63 | 64 | /** 65 | * 判断当前登陆用户是不是admin 66 | * @param request 67 | * @return 68 | */ 69 | public static boolean isAdmin(HttpServletRequest request) { 70 | if (request.getAttribute(USER_ACCOUNT_KEY) == null) { 71 | return false; 72 | } 73 | if ("admin".equals(request.getAttribute(USER_ACCOUNT_KEY).toString())){ 74 | return true; 75 | } else { 76 | return false; 77 | } 78 | } 79 | 80 | 81 | 82 | } -------------------------------------------------------------------------------- /src/main/java/com/company/project/configurer/LoginInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.company.project.configurer; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import com.company.project.core.Result; 6 | import com.company.project.core.ResultCode; 7 | import com.company.project.utils.JwtUtils; 8 | import io.jsonwebtoken.Claims; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | import java.io.IOException; 18 | 19 | import static com.company.project.utils.JwtUtils.USER_ACCOUNT_KEY; 20 | import static com.company.project.utils.JwtUtils.USER_ID_KEY; 21 | 22 | /** 23 | *

登陆拦截器 24 | */ 25 | public class LoginInterceptor extends HandlerInterceptorAdapter { 26 | 27 | private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); 28 | @Override 29 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 30 | //拦截接口 31 | //从header中获取token 32 | String token = request.getHeader("token"); 33 | //如果header中不存在token,则从参数中获取token 34 | if(StringUtils.isBlank(token)){ 35 | token = request.getParameter("token"); 36 | } 37 | //token为空返回 38 | if(StringUtils.isBlank(token)){ 39 | Result result = new Result(); 40 | result.setCode(ResultCode.UNAUTHORIZED).setMessage("token不能为空").setSuccess(false); 41 | responseResult(response, result); 42 | return false; 43 | }// 校验并解析token,如果token过期或者篡改,则会返回null 44 | Claims claims = JwtUtils.checkJWT(token); 45 | if(null == claims){ 46 | Result result = new Result(); 47 | result.setCode(ResultCode.UNAUTHORIZED).setMessage("登陆失效, 请重新登陆").setSuccess(false); 48 | responseResult(response, result); 49 | return false; 50 | } 51 | // 校验通过后,设置用户信息到request里,在Controller中从Request域中获取用户信息 52 | request.setAttribute(USER_ID_KEY, claims.get("id")); 53 | request.setAttribute(USER_ACCOUNT_KEY, claims.get("account")); 54 | return true; 55 | } 56 | 57 | 58 | 59 | private void responseResult(HttpServletResponse response, Result result) { 60 | response.setCharacterEncoding("UTF-8"); 61 | response.setHeader("Content-type", "application/json;charset=UTF-8"); 62 | response.setStatus(200); 63 | try { 64 | response.getWriter().write(JSON.toJSONString(result)); 65 | } catch (IOException ex) { 66 | logger.error(ex.getMessage()); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/resources/user.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : localhost 5 | Source Server Type : MySQL 6 | Source Server Version : 50529 7 | Source Host : localhost:3306 8 | Source Schema : project 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50529 12 | File Encoding : 65001 13 | 14 | Date: 08/01/2020 15:53:02 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for user 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `user`; 24 | CREATE TABLE `user` ( 25 | `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', 26 | `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名', 27 | `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码', 28 | `nick_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称', 29 | `sex` int(1) NULL DEFAULT NULL COMMENT '性别', 30 | `create_date` datetime NULL DEFAULT NULL COMMENT '创建时间', 31 | `create_user` int(11) NULL DEFAULT NULL COMMENT '创建人', 32 | `update_date` datetime NULL DEFAULT NULL COMMENT '修改时间', 33 | `update_user` int(11) NULL DEFAULT NULL COMMENT '修改人', 34 | `del_flag` int(1) NULL DEFAULT 0 COMMENT '删除标志0未删 1删除', 35 | PRIMARY KEY (`id`) USING BTREE 36 | ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; 37 | 38 | -- ---------------------------- 39 | -- Records of user 40 | -- ---------------------------- 41 | INSERT INTO `user` VALUES (1, 'admin', '9dc818b4bca3baa7d230fbf96c919638', '土豆', 1, NULL, NULL, NULL, NULL, 0); 42 | INSERT INTO `user` VALUES (2, '2@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-2', 1, NULL, NULL, NULL, NULL, 0); 43 | INSERT INTO `user` VALUES (3, '3@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-3', 1, NULL, NULL, NULL, NULL, 0); 44 | INSERT INTO `user` VALUES (4, '4@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-4', 1, NULL, NULL, NULL, NULL, 0); 45 | INSERT INTO `user` VALUES (5, '5@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-5', 1, NULL, NULL, NULL, NULL, 0); 46 | INSERT INTO `user` VALUES (6, '6@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-6', 1, NULL, NULL, NULL, NULL, 0); 47 | INSERT INTO `user` VALUES (7, '7@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-7', 1, NULL, NULL, NULL, NULL, 0); 48 | INSERT INTO `user` VALUES (8, '8@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-8', 1, NULL, NULL, NULL, NULL, 0); 49 | INSERT INTO `user` VALUES (9, '9@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-9', 1, NULL, NULL, NULL, NULL, 0); 50 | INSERT INTO `user` VALUES (10, '10@qq.com', '9dc818b4bca3baa7d230fbf96c919638', '土豆-10', 1, NULL, NULL, NULL, NULL, 0); 51 | 52 | SET FOREIGN_KEY_CHECKS = 1; 53 | -------------------------------------------------------------------------------- /src/test/resources/templates/controller.java.ftl: -------------------------------------------------------------------------------- 1 | package ${package.Controller}; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.company.project.core.Result; 5 | import com.company.project.core.ResultGenerator; 6 | import io.swagger.annotations.ApiImplicitParam; 7 | import io.swagger.annotations.ApiImplicitParams; 8 | import org.springframework.web.bind.annotation.*; 9 | import ${package.Service}.${table.serviceName}; 10 | import ${package.Entity}.${entity}; 11 | import io.swagger.annotations.Api; 12 | import io.swagger.annotations.ApiOperation; 13 | import lombok.extern.slf4j.Slf4j; 14 | import com.baomidou.mybatisplus.core.metadata.IPage; 15 | 16 | import javax.annotation.Resource; 17 | <#if restControllerStyle> 18 | import org.springframework.web.bind.annotation.RestController; 19 | <#else> 20 | import org.springframework.stereotype.Controller; 21 | 22 | <#if superControllerClassPackage??> 23 | import ${superControllerClassPackage}; 24 | 25 | 26 | /** 27 | *

28 | * ${table.comment!} 前端控制器 29 | *

30 | * 31 | * @author ${author} 32 | * @since ${date} 33 | */ 34 | <#if restControllerStyle> 35 | @Api(tags = {"${table.comment!}"}) 36 | @Slf4j 37 | @RestController 38 | <#else> 39 | @Controller 40 | 41 | @RequestMapping("<#if package.ModuleName??>/${package.ModuleName}/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}") 42 | <#if kotlin> 43 | class ${table.controllerName}<#if superControllerClass??>:${superControllerClass}() 44 | <#else> 45 | <#if superControllerClass??>public class ${table.controllerName} extends ${superControllerClass}{ 46 | <#else>public class ${table.controllerName} { 47 | 48 | 49 | @Resource 50 | private ${table.serviceName} ${(table.serviceName?substring(1))?uncap_first}; 51 | 52 | 53 | @ApiOperation(value = "新增${table.comment!}") 54 | @PostMapping("add") 55 | public Result add(@RequestBody ${entity} ${entity?uncap_first}){ 56 | ${(table.serviceName?substring(1))?uncap_first}.save(${entity?uncap_first}); 57 | return ResultGenerator.genSuccessResult(); 58 | } 59 | 60 | @ApiOperation(value = "删除${table.comment!}") 61 | @PostMapping("delete/{id}") 62 | public Result delete(@PathVariable("id") Long id){ 63 | ${(table.serviceName?substring(1))?uncap_first}.removeById(id); 64 | return ResultGenerator.genSuccessResult(); 65 | } 66 | 67 | @ApiOperation(value = "更新${table.comment!}") 68 | @PostMapping("update") 69 | public Result update(@RequestBody ${entity} ${entity?uncap_first}){ 70 | ${(table.serviceName?substring(1))?uncap_first}.updateById(${entity?uncap_first}); 71 | return ResultGenerator.genSuccessResult(); 72 | } 73 | 74 | @ApiOperation(value = "查询${table.comment!}分页数据") 75 | @ApiImplicitParams({ 76 | @ApiImplicitParam(name = "currentPage", value = "页码"), 77 | @ApiImplicitParam(name = "pageCount", value = "每页条数") 78 | }) 79 | @GetMapping("listByPage") 80 | public Result findListByPage(@RequestParam Integer currentPage, 81 | @RequestParam Integer pageCount){ 82 | Page page = new Page(currentPage, pageCount); 83 | IPage<${entity}> iPage = ${(table.serviceName?substring(1))?uncap_first}.page(page); 84 | return ResultGenerator.genSuccessResult(iPage); 85 | } 86 | 87 | @ApiOperation(value = "id查询${table.comment!}") 88 | @GetMapping("getById/{id}") 89 | public Result findById(@PathVariable Long id){ 90 | return ResultGenerator.genSuccessResult(${(table.serviceName?substring(1))?uncap_first}.getById(id)); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/web/UserController.java: -------------------------------------------------------------------------------- 1 | package com.company.project.web; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.company.project.core.Result; 6 | import com.company.project.core.ResultGenerator; 7 | import com.company.project.utils.JwtUtils; 8 | import com.company.project.utils.MD5Utils; 9 | import io.swagger.annotations.ApiImplicitParam; 10 | import io.swagger.annotations.ApiImplicitParams; 11 | import org.springframework.web.bind.annotation.*; 12 | import com.company.project.service.IUserService; 13 | import com.company.project.model.User; 14 | import io.swagger.annotations.Api; 15 | import io.swagger.annotations.ApiOperation; 16 | import lombok.extern.slf4j.Slf4j; 17 | import com.baomidou.mybatisplus.core.metadata.IPage; 18 | 19 | import javax.annotation.Resource; 20 | import javax.validation.Valid; 21 | 22 | import org.springframework.web.bind.annotation.RestController; 23 | 24 | /** 25 | *

26 | * 前端控制器 27 | *

28 | * 29 | * @author project 30 | * @since 2020-01-08 31 | */ 32 | @Slf4j 33 | @RestController 34 | @RequestMapping("/api/user") 35 | public class UserController { 36 | 37 | @Resource 38 | private IUserService userService; 39 | 40 | @ApiOperation("登陆") 41 | @PostMapping("/login") 42 | public Result login(@RequestBody @Valid User user) { 43 | QueryWrapper queryWrapper = new QueryWrapper<>(); 44 | queryWrapper.eq("username", user.getUsername()); 45 | User userO = userService.getOne(queryWrapper); 46 | if (userO == null) { 47 | return ResultGenerator.genFailResult("账号未找到"); 48 | } 49 | if (!MD5Utils.Encrypt(user.getPassword(),true).equals(userO.getPassword())) { 50 | return ResultGenerator.genFailResult("密码错误"); 51 | } 52 | String token = JwtUtils.geneJsonWebToken(user); 53 | user.setToken(token); 54 | user.setPassword(""); 55 | return ResultGenerator.genSuccessResult(user); 56 | } 57 | 58 | @ApiOperation("注册") 59 | @PostMapping("/register") 60 | public Result register(@RequestBody @Valid User user) { 61 | QueryWrapper queryWrapper = new QueryWrapper<>(); 62 | queryWrapper.eq("username", user.getUsername()); 63 | User userO = userService.getOne(queryWrapper); 64 | if (userO != null) { 65 | return ResultGenerator.genFailResult("账号已存在"); 66 | } 67 | user.setPassword(MD5Utils.Encrypt(user.getPassword(),true)); 68 | userService.save(user); 69 | return ResultGenerator.genSuccessResult(); 70 | } 71 | 72 | @ApiOperation(value = "删除") 73 | @PostMapping("delete/{id}") 74 | public Result delete(@PathVariable("id") Long id){ 75 | userService.removeById(id); 76 | return ResultGenerator.genSuccessResult(); 77 | } 78 | 79 | @ApiOperation(value = "更新") 80 | @PostMapping("update") 81 | public Result update(@RequestBody User user){ 82 | //密码不更新 83 | user.setPassword(null); 84 | userService.updateById(user); 85 | return ResultGenerator.genSuccessResult(); 86 | } 87 | 88 | @ApiOperation(value = "查询分页数据") 89 | @ApiImplicitParams({ 90 | @ApiImplicitParam(name = "currentPage", value = "页码"), 91 | @ApiImplicitParam(name = "pageCount", value = "每页条数") 92 | }) 93 | @GetMapping("listByPage") 94 | public Result findListByPage(@RequestParam(defaultValue = "1") Integer currentPage, 95 | @RequestParam(defaultValue = "10") Integer pageCount){ 96 | Page page = new Page(currentPage, pageCount); 97 | IPage iPage = userService.page(page); 98 | return ResultGenerator.genSuccessResult(iPage); 99 | } 100 | 101 | @ApiOperation(value = "id查询") 102 | @GetMapping("getById/{id}") 103 | public Result findById(@PathVariable Long id){ 104 | return ResultGenerator.genSuccessResult(userService.getById(id)); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/CodeGenerator.java: -------------------------------------------------------------------------------- 1 | import com.baomidou.mybatisplus.core.toolkit.StringPool; 2 | import com.baomidou.mybatisplus.generator.AutoGenerator; 3 | import com.baomidou.mybatisplus.generator.InjectionConfig; 4 | import com.baomidou.mybatisplus.generator.config.*; 5 | import com.baomidou.mybatisplus.generator.config.po.TableInfo; 6 | import com.baomidou.mybatisplus.generator.config.rules.DateType; 7 | import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; 8 | import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | // 演示例子,执行 main 方法控制台输入模块表名回车自动生成对应项目目录中 14 | public class CodeGenerator { 15 | 16 | 17 | //多个表逗号分隔 18 | static String tableName = "user_copy"; 19 | //逻辑删除字段名, 假如表没有逻辑删除字段,请忽视 20 | static String logicDeleteFieldName = "del_flag"; 21 | 22 | public static void main(String[] args) { 23 | // 代码生成器 24 | AutoGenerator mpg = new AutoGenerator(); 25 | 26 | // 全局配置 27 | GlobalConfig gc = new GlobalConfig(); 28 | String projectPath = System.getProperty("user.dir"); 29 | gc.setOutputDir(projectPath + "/src/main/java"); 30 | gc.setAuthor("aitangbao"); 31 | gc.setOpen(false); 32 | gc.setBaseColumnList(true); 33 | gc.setBaseResultMap(true); 34 | gc.setDateType(DateType.ONLY_DATE); 35 | // gc.setSwagger2(true); 实体属性 Swagger2 注解 36 | mpg.setGlobalConfig(gc); 37 | 38 | // 数据源配置 39 | DataSourceConfig dsc = new DataSourceConfig(); 40 | dsc.setUrl("jdbc:mysql://localhost:3306/project?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC"); 41 | // dsc.setSchemaName("public"); 42 | dsc.setDriverName("com.mysql.cj.jdbc.Driver"); 43 | dsc.setUsername("root"); 44 | dsc.setPassword("123456"); 45 | mpg.setDataSource(dsc); 46 | 47 | // 包配置 48 | PackageConfig pc = new PackageConfig(); 49 | pc.setParent("com.company.project"); 50 | pc.setEntity("model"); 51 | pc.setMapper("dao"); 52 | pc.setController("web"); 53 | mpg.setPackageInfo(pc); 54 | 55 | // 自定义配置 56 | InjectionConfig cfg = new InjectionConfig() { 57 | @Override 58 | public void initMap() { 59 | // to do nothing 60 | } 61 | }; 62 | 63 | // 如果模板引擎是 freemarker 64 | String templatePath = "/templates/mapper.xml.ftl"; 65 | // 如果模板引擎是 velocity 66 | // String templatePath = "/templates/mapper.xml.vm"; 67 | 68 | // 自定义输出配置 69 | List focList = new ArrayList<>(); 70 | // 自定义配置会被优先输出 71 | focList.add(new FileOutConfig(templatePath) { 72 | @Override 73 | public String outputFile(TableInfo tableInfo) { 74 | // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!! 75 | return projectPath + "/src/main/resources/mapper/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML; 76 | } 77 | }); 78 | cfg.setFileOutConfigList(focList); 79 | mpg.setCfg(cfg); 80 | 81 | // 配置模板 82 | TemplateConfig templateConfig = new TemplateConfig(); 83 | 84 | // 配置自定义输出模板 85 | //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别 86 | // templateConfig.setEntity("templates/entity.java"); 87 | // templateConfig.setService(); 88 | templateConfig.setController("templates/controller.java"); 89 | 90 | templateConfig.setXml(null); 91 | mpg.setTemplate(templateConfig); 92 | 93 | // 策略配置 94 | StrategyConfig strategy = new StrategyConfig(); 95 | strategy.setNaming(NamingStrategy.underline_to_camel); 96 | strategy.setColumnNaming(NamingStrategy.underline_to_camel); 97 | // strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!"); 98 | strategy.setEntityLombokModel(true); 99 | strategy.setRestControllerStyle(true); 100 | // 公共父类 101 | // strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!"); 102 | // 写于父类中的公共字段 103 | // strategy.setSuperEntityColumns("id"); 104 | strategy.setInclude(tableName.split(",")); 105 | strategy.setControllerMappingHyphenStyle(true); 106 | strategy.setLogicDeleteFieldName(logicDeleteFieldName); // 逻辑删除字段名称 107 | strategy.setTablePrefix(pc.getModuleName() + "_"); 108 | mpg.setStrategy(strategy); 109 | mpg.setTemplateEngine(new FreemarkerTemplateEngine()); 110 | mpg.execute(); 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.company.project 8 | spring-boot-api 9 | 1.0 10 | jar 11 | 12 | 13 | 1.8 14 | 3.3.0 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter-parent 21 | 2.2.2.RELEASE 22 | 23 | 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-web 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-jdbc 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-starter-test 37 | test 38 | 39 | 40 | 41 | com.baomidou 42 | dynamic-datasource-spring-boot-starter 43 | 2.5.5 44 | 45 | 46 | com.baomidou 47 | mybatis-plus 48 | ${mybatis-plus.version} 49 | 50 | 51 | com.baomidou 52 | mybatis-plus-boot-starter 53 | ${mybatis-plus.version} 54 | 55 | 56 | com.baomidou 57 | mybatis-plus-generator 58 | ${mybatis-plus.version} 59 | 60 | 61 | org.projectlombok 62 | lombok 63 | 1.18.10 64 | provided 65 | 66 | 67 | 68 | 69 | 70 | commons-codec 71 | commons-codec 72 | 73 | 74 | org.apache.commons 75 | commons-lang3 76 | 3.6 77 | 78 | 79 | 80 | mysql 81 | mysql-connector-java 82 | runtime 83 | 84 | 85 | 86 | 87 | com.alibaba 88 | fastjson 89 | 1.2.47 90 | 91 | 92 | 93 | com.alibaba 94 | druid-spring-boot-starter 95 | 1.1.10 96 | 97 | 98 | 99 | org.freemarker 100 | freemarker 101 | 2.3.30 102 | test 103 | 104 | 105 | 106 | com.github.xiaoymin 107 | knife4j-spring-boot-starter 108 | 2.0.2 109 | 110 | 111 | 112 | io.jsonwebtoken 113 | jjwt 114 | 0.9.0 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | org.springframework.boot 123 | spring-boot-maven-plugin 124 | 125 | 126 | maven-compiler-plugin 127 | 128 | ${java.version} 129 | ${java.version} 130 | UTF-8 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | aliyun-repos 139 | http://maven.aliyun.com/nexus/content/groups/public/ 140 | 141 | false 142 | 143 | 144 | 145 | 146 | 147 | 148 | aliyun-plugin 149 | http://maven.aliyun.com/nexus/content/groups/public/ 150 | 151 | false 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/main/java/com/company/project/configurer/WebMvcConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.company.project.configurer; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.Charset; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import javax.servlet.ServletException; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import com.alibaba.fastjson.JSON; 13 | import com.alibaba.fastjson.serializer.SerializerFeature; 14 | import com.alibaba.fastjson.support.config.FastJsonConfig; 15 | import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; 16 | 17 | import com.company.project.core.Result; 18 | import com.company.project.core.ResultCode; 19 | import com.company.project.core.ResultGenerator; 20 | import com.company.project.core.ServiceException; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | import org.springframework.http.MediaType; 26 | import org.springframework.http.converter.HttpMessageConverter; 27 | import org.springframework.web.bind.MethodArgumentNotValidException; 28 | import org.springframework.web.bind.annotation.ExceptionHandler; 29 | import org.springframework.web.method.HandlerMethod; 30 | import org.springframework.web.servlet.HandlerExceptionResolver; 31 | import org.springframework.web.servlet.ModelAndView; 32 | import org.springframework.web.servlet.NoHandlerFoundException; 33 | import org.springframework.web.servlet.config.annotation.*; 34 | 35 | /** 36 | * Spring MVC 配置 37 | */ 38 | @Configuration 39 | public class WebMvcConfigurer extends WebMvcConfigurerAdapter { 40 | 41 | private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); 42 | 43 | @Bean 44 | LoginInterceptor loginInterceptor() { 45 | 46 | return new LoginInterceptor(); 47 | } 48 | 49 | //使用阿里 FastJson 作为JSON MessageConverter 50 | @Override 51 | public void configureMessageConverters(List> converters) { 52 | FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); 53 | FastJsonConfig config = new FastJsonConfig(); 54 | config.setSerializerFeatures(SerializerFeature.WriteMapNullValue);//保留空的字段 55 | //SerializerFeature.WriteNullStringAsEmpty,//String null -> "" 56 | //SerializerFeature.WriteNullNumberAsZero//Number null -> 0 57 | // 按需配置,更多参考FastJson文档哈 58 | 59 | converter.setFastJsonConfig(config); 60 | converter.setDefaultCharset(Charset.forName("UTF-8")); 61 | converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8)); 62 | converters.add(converter); 63 | } 64 | 65 | 66 | //统一异常处理 67 | @Override 68 | public void configureHandlerExceptionResolvers(List exceptionResolvers) { 69 | exceptionResolvers.add(new HandlerExceptionResolver() { 70 | public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) { 71 | Result result = new Result(); 72 | if (e instanceof ServiceException) {//业务失败的异常,如“账号或密码错误” 73 | result.setCode(ResultCode.FAIL).setMessage(e.getMessage()).setSuccess(false); 74 | } else if (e instanceof MethodArgumentNotValidException) {//@valid注解验证参数 75 | MethodArgumentNotValidException m = (MethodArgumentNotValidException) e; 76 | m.getBindingResult().getFieldError().getDefaultMessage(); 77 | result.setCode(ResultCode.PARAM_FAIL).setMessage(m.getBindingResult().getFieldError().getDefaultMessage()).setSuccess(false); 78 | } else if (e instanceof NoHandlerFoundException) { 79 | result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在").setSuccess(false); 80 | } else if (e instanceof ServletException) { 81 | result.setCode(ResultCode.FAIL).setMessage(e.getMessage()).setSuccess(false); 82 | } else { 83 | result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员").setSuccess(false); 84 | String message; 85 | if (handler instanceof HandlerMethod) { 86 | HandlerMethod handlerMethod = (HandlerMethod) handler; 87 | message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s", 88 | request.getRequestURI(), 89 | handlerMethod.getBean().getClass().getName(), 90 | handlerMethod.getMethod().getName(), 91 | e.getMessage()); 92 | } else { 93 | message = e.getMessage(); 94 | } 95 | logger.error(message, e); 96 | } 97 | responseResult(response, result); 98 | return new ModelAndView(); 99 | } 100 | 101 | }); 102 | } 103 | 104 | @ExceptionHandler(MethodArgumentNotValidException.class) 105 | public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { 106 | return ResultGenerator.genFailResult(e.getBindingResult().getFieldError().getDefaultMessage()); 107 | } 108 | 109 | /** 110 | * 页面跨域访问Controller过滤 111 | * 112 | * @return 113 | */ 114 | @Override 115 | public void addCorsMappings(CorsRegistry registry) { 116 | WebMvcConfigurer.super.addCorsMappings(registry); 117 | registry.addMapping("/**") 118 | .allowedHeaders("*") 119 | .allowedMethods("POST","GET") 120 | .allowedOrigins("*"); 121 | } 122 | 123 | //添加拦截器 124 | @Override 125 | public void addInterceptors(InterceptorRegistry registry) { 126 | registry.addInterceptor(loginInterceptor()) 127 | .excludePathPatterns("/doc.html") 128 | .excludePathPatterns("/swagger-resources/**") 129 | .excludePathPatterns("/error") 130 | .excludePathPatterns("/webjars/**") 131 | .excludePathPatterns("/api/user/login") 132 | .excludePathPatterns("/api/user/register") 133 | .addPathPatterns("/**"); 134 | } 135 | 136 | 137 | private void responseResult(HttpServletResponse response, Result result) { 138 | response.setCharacterEncoding("UTF-8"); 139 | response.setHeader("Content-type", "application/json;charset=UTF-8"); 140 | response.setStatus(200); 141 | try { 142 | response.getWriter().write(JSON.toJSONString(result)); 143 | } catch (IOException ex) { 144 | logger.error(ex.getMessage()); 145 | } 146 | } 147 | 148 | /** 149 | * 发现如果继承了WebMvcConfigurationSupport,则在yml中配置的相关内容会失效。 需要重新指定静态资源 150 | * 151 | * @param registry 152 | */ 153 | @Override 154 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 155 | registry.addResourceHandler("/**").addResourceLocations( 156 | "classpath:/static/"); 157 | registry.addResourceHandler("doc.html").addResourceLocations( 158 | "classpath:/META-INF/resources/"); 159 | registry.addResourceHandler("/webjars/**").addResourceLocations( 160 | "classpath:/META-INF/resources/webjars/"); 161 | super.addResourceHandlers(registry); 162 | } 163 | 164 | 165 | /** 166 | * 配置servlet处理 167 | */ 168 | @Override 169 | public void configureDefaultServletHandling( 170 | DefaultServletHandlerConfigurer configurer) { 171 | configurer.enable(); 172 | } 173 | 174 | 175 | 176 | 177 | } 178 | 179 | --------------------------------------------------------------------------------