├── .gitattributes
├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── LICENSE
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── spring
│ │ └── demo
│ │ └── springbootexample
│ │ ├── SpringBootExampleApplication.java
│ │ ├── annotation
│ │ └── ignoreJWT.java
│ │ ├── beans
│ │ ├── Product.java
│ │ ├── entity
│ │ │ └── JWTUser.java
│ │ └── msg
│ │ │ ├── Request.java
│ │ │ └── Response.java
│ │ ├── config
│ │ ├── Config.java
│ │ ├── InterceptorConfiger.java
│ │ └── SwaggerConfig.java
│ │ ├── constants
│ │ ├── ErrCodeEnum.java
│ │ ├── JWTConsts.java
│ │ └── ReqResConsts.java
│ │ ├── controller
│ │ ├── DocController.java
│ │ ├── JwtTestController.java
│ │ ├── TestController.java
│ │ └── global
│ │ │ ├── GlobalBasicErrorHandler.java
│ │ │ └── GlobalExceptionHandler.java
│ │ ├── exception
│ │ ├── JWTExpiredException.java
│ │ └── JWTIllegalException.java
│ │ ├── intercepter
│ │ └── JWTAuthInterceptor.java
│ │ ├── jwt
│ │ └── JsonWebTokenUtility.java
│ │ ├── result
│ │ ├── ERRORDetail.java
│ │ ├── Pagination.java
│ │ └── WebResult.java
│ │ ├── security
│ │ └── WebSecurityConfig.java
│ │ └── utils
│ │ └── JWTUtils.java
└── resources
│ ├── application-dev.yml
│ ├── application-prod.yml
│ ├── application-test.yml
│ ├── application.yml
│ ├── generator
│ ├── generator.properties
│ └── generatorConfig.xml
│ └── logback-spring.xml
└── test
└── java
└── com
└── spring
└── demo
└── springbootexample
└── SpringBootExampleApplicationTests.java
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 |
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 |
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | nbproject/private/
20 | build/
21 | nbbuild/
22 | dist/
23 | nbdist/
24 | .nb-gradle/
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calebman/spring-boot-example/415b7fdff1aea67da8c3033bf5d298a0bf4f3ebb/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 calebman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### 本文简介
2 | - 为什么使用SpringBoot
3 | - 搭建怎样一个环境
4 | - 开发环境
5 | - 导入快速启动项目
6 | - 集成前准备
7 | - 集成Mybatis
8 | - 集成Swagger2
9 | - 多环境配置
10 | - 多环境下的日志配置
11 | - jwt配置
12 | - 常用配置
13 | ### 为什么使用SpringBoot
14 | SpringBoot相对于传统的SSM框架的优点是提供了默认的样板化配置,简化了Spring应用的初始搭建过程,如果你不想被众多的xml配置文件困扰,可以考虑使用SpringBoot替代
15 | ### 搭建怎样一个环境
16 | 本文将基于Spring官方提供的快速启动项目模板集成Mybatis、Swagger2框架,并讲解mybatis generator一键生成代码插件、logback、一键生成文档以及多环境的配置方法,最后再介绍一下自定义配置的注解获取、全局异常处理等经常用到的东西。
17 | ### 开发环境
18 | 本人使用IDEA作为开发工具,IDEA下载时默认集成了SpringBoot的快速启动项目可以直接创建,如果使用Eclipse的同学可以考虑安装SpringBoot插件或者直接从[这里](https://start.spring.io/)配置并下载SpringBoot快速启动项目,需要注意的是本次环境搭建选择的是SpringBoot2.0的快速启动框架,SpringBoot2.0要求jdk版本必须要在1.8及以上。
19 | ### 导入快速启动项目
20 | 不管是由IDEA导入还是现实下载模板工程都需要初始化快速启动工程的配置,如果使用IDEA,在新建项目时选择Spring Initializr,主要配置如下图
21 | 
22 | 
23 | 点击next之后finish之后IDEA显示正在下载模板工程,下载完成后会根据pom.xml下载包依赖,依赖下载完毕后模板项目就算创建成功了,如果是直接从官方网站配置下载快速启动项目可参考下图配置
24 | 
25 | 从Search for dependencies 框中输入并选择Web、Mysql、Mybatis加入依赖,点击Generate Project下载快速启动项目,然后在IDE中选择导入Maven项目,项目导入完成后可见其目录结构如下图
26 | 
27 | 需要关注红色方框圈起来的部分,由上往下第一个java类是用来启动项目的入口函数,第二个properties后缀的文件是项目的配置文件,第三个是项目的依赖包以及执行插件的配置
28 | ### 集成前准备
29 | ##### 修改.properties为.yml
30 | yml相对于properties更加精简而且很多官方给出的Demo都是yml的配置形式,在这里我们采用yml的形式代替properties,相对于properties形式主要有以下两点不同
31 | >1. 对于键的描述由原有的 "." 分割变成了树的形状
32 | >2. 对于所有的键的后面一个要跟一个空格,不然启动项目会报配置解析错误
33 | ```
34 | # properties式语法描述
35 | spring.datasource.name = mysql
36 | spring.datasource.url = jdbc:mysql://localhost:3306/db?characterEncoding=utf-8
37 | spring.datasource.username = root
38 | spring.datasource.password = 123
39 | # yml式语法描述
40 | spring:
41 | datasource:
42 | name: mysql
43 | url: jdbc:mysql://localhost:3306/db?characterEncoding=utf-8
44 | username: root
45 | password: 123
46 | ```
47 |
48 | ##### 配置所需依赖
49 | 快速启动项目创建成功后我们观察其pom.xml文件中的依赖如下图,包含了我们选择的Web、Mybatis以及Mysql
50 | ```
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-starter-web
55 |
56 |
57 |
58 | org.mybatis.spring.boot
59 | mybatis-spring-boot-starter
60 | 1.3.1
61 |
62 |
63 |
64 | mysql
65 | mysql-connector-java
66 | runtime
67 |
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-starter-test
72 | test
73 |
74 | ```
75 | 但是我们使用ORM框架一般还会配合数据库连接池以及分页插件来使用,在这里我选择了阿里的druid以及pagehelper这个分页插件,再加上我们还需要整合swagger2文档自动化构建框架,所以增加了以下四个依赖项
76 | ```
77 |
78 |
79 | com.github.pagehelper
80 | pagehelper-spring-boot-starter
81 | 1.2.3
82 |
83 |
84 |
85 | com.alibaba
86 | druid-spring-boot-starter
87 | 1.1.1
88 |
89 |
90 |
91 | com.alibaba
92 | fastjson
93 | 1.2.31
94 |
95 |
96 |
97 | io.springfox
98 | springfox-swagger2
99 | 2.5.0
100 |
101 | ```
102 | ### 集成Mybatis
103 | Mybatis的配置主要包括了druid数据库连接池、pagehelper分页插件、mybatis-generator代码逆向生成插件以及mapper、pojo扫描配置
104 | ##### 配置druid数据库连接池
105 | 添加以下配置至application.yml文件中
106 | ```
107 | spring:
108 | datasource:
109 | # 如果存在多个数据源,监控的时候可以通过名字来区分开来
110 | name: mysql
111 | # 连接数据库的url
112 | url: jdbc:mysql://localhost:3306/db?characterEncoding=utf-8
113 | # 连接数据库的账号
114 | username: root
115 | # 连接数据库的密码
116 | password: 123
117 | # 使用druid数据源
118 | type: com.alibaba.druid.pool.DruidDataSource
119 | # 扩展插件
120 | # 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall
121 | filters: stat
122 | # 最大连接池数量
123 | maxActive: 20
124 | # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
125 | initialSize: 1
126 | # 获取连接时最大等待时间,单位毫秒
127 | maxWait: 60000
128 | # 最小连接池数量
129 | minIdle: 1
130 | timeBetweenEvictionRunsMillis: 60000
131 | # 连接保持空闲而不被驱逐的最长时间
132 | minEvictableIdleTimeMillis: 300000
133 | # 用来检测连接是否有效的sql,要求是一个查询语句
134 | # 如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用
135 | validationQuery: select count(1) from 'table'
136 | # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
137 | testWhileIdle: true
138 | # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
139 | testOnBorrow: false
140 | # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
141 | testOnReturn: false
142 | # 是否缓存preparedStatement,即PSCache
143 | poolPreparedStatements: false
144 | # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true
145 | maxOpenPreparedStatements: -1
146 | ```
147 | ##### 配置pagehelper分页插件
148 | ```
149 | # pagehelper分页插件
150 | pagehelper:
151 | # 数据库的方言
152 | helperDialect: mysql
153 | # 启用合理化,如果pageNum < 1会查询第一页,如果pageNum > pages会查询最后一页
154 | reasonable: true
155 | ```
156 | ##### 代码逆向生成插件mybatis-generator的配置及运行
157 | mybatis-generator插件的使用主要分为以下三步
158 | >1. pom.xml中添加mybatis-generator插件
159 | ```
160 |
161 |
162 |
163 |
164 | org.springframework.boot
165 | spring-boot-maven-plugin
166 |
167 |
168 |
169 | org.mybatis.generator
170 | mybatis-generator-maven-plugin
171 | 1.3.2
172 |
173 |
174 |
175 | ${basedir}/src/main/resources/generator/generatorConfig.xml
176 |
177 | true
178 | true
179 |
180 |
181 |
182 |
183 | ```
184 | >2.创建逆向代码生成配置文件generatorConfig.xml
185 |
186 | 参照pom.xml插件配置中的扫描位置,在resources目录下创建generator文件夹,在新建的文件夹中创建generatorConfig.xml配置文件,文件的详细配置信息如下
187 | ```
188 |
189 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | ```
225 | 为了将generatorConfig.xml配置模板化,在这里将变动性较大的配置项单独提取出来作为一个generatorConfig.xml的配置文件,然后通过properties标签读取此文件的配置,这样做的好处是当需要多处复用此xml时只需要关注少量的配置项。
226 | 在generatorConfig.xml同级创建generator.properties文件,现只需要配置generator.properties文件即可,配置内容如下
227 | ```
228 | # 请手动配置以下选项
229 | # 数据库驱动:选择你的本地硬盘上面的数据库驱动包
230 | classPathEntry = D:/CJH/maven-repository/mysql/mysql-connector-java/5.1.30/mysql-connector-java-5.1.30.jar
231 | # 数据库名称、用户名、密码
232 | db = db
233 | userId = root
234 | password = 123
235 | # 生成pojo的包名位置 在src/main/java目录下
236 | pojoTargetPackage = com.spring.demo.springbootexample.mybatis.po
237 | # 生成DAO的包名位置 在src/main/java目录下
238 | daoTargetPackage = com.spring.demo.springbootexample.mybatis.mapper
239 | # 生成Mapper的包名位置 位于src/main/resources目录下
240 | mapperTargetPackage = mapper
241 | ```
242 | >3. 运行mybatis-generator插件生成Dao、Model、Mapping
243 | ```
244 | # 打开命令行cd到项目pom.xml同级目录运行以下命令
245 | mvn mybatis-generator:generate -e
246 | ```
247 | ##### mybatis扫描包配置
248 | 至此已经生成了指定数据库对应的实体、映射类,但是还不能直接使用,需要配置mybatis扫描地址后才能正常调用
249 | >1. 在application.yml配置mapper.xml以及pojo的包地址
250 | ```
251 | mybatis:
252 | # mapper.xml包地址
253 | mapper-locations: classpath:mapper/*.xml
254 | # pojo生成包地址
255 | type-aliases-package: com.spring.demo.springbootexample.mybatis.po
256 | ```
257 | >2. 在SpringBootExampleApplication.java中开启Mapper扫描注解
258 | ```
259 | @SpringBootApplication
260 | @MapperScan("com.spring.demo.springbootexample.mybatis.mapper")
261 | public class SpringBootExampleApplication {
262 |
263 | public static void main(String[] args) {
264 | SpringApplication.run(SpringBootExampleApplication.class, args);
265 | }
266 | }
267 | ```
268 | ##### 测试mapper的有效性
269 | ```
270 | @Controller
271 | public class TestController {
272 | //替换成自己生成的mapper
273 | @Autowired
274 | UserMapper userMapper;
275 |
276 | @RequestMapping("/test")
277 | @ResponseBody
278 | public Object test(){
279 | //查询该表的所有数据
280 | return userMapper.selectByExample(null);
281 | }
282 | }
283 | ```
284 | 启动SpringBootExampleApplication.java的main函数,如果没有在application.yml特意配置server.port那么springboot会采用默认的8080端口运行,运行成功将打印如下日志
285 | ```
286 | Tomcat started on port(s): 8080 (http) with context path ''
287 | ```
288 | 在浏览器输入地址如果返回表格的中的所有数据代表mybatis集成成功
289 | ```
290 | http://localhost:8080/test
291 | ```
292 | ### 集成Swagger2
293 | Swagger2是一个文档快速构建工具,能够通过注解自动生成一个Restful风格json形式的接口文档,并可以通过如swagger-ui等工具生成html网页形式的接口文档,swagger2的集成比较简单,使用需要稍微熟悉一下,集成、注解与使用分如下四步
294 | >1. 建立SwaggerConfig文件
295 | ```
296 | @Configuration
297 | public class SwaggerConfig {
298 | // 接口版本号
299 | private final String version = "1.0";
300 | // 接口大标题
301 | private final String title = "SpringBoot示例工程";
302 | // 具体的描述
303 | private final String description = "API文档自动生成示例";
304 | // 服务说明url
305 | private final String termsOfServiceUrl = "http://www.kingeid.com";
306 | // licence
307 | private final String license = "MIT";
308 | // licnce url
309 | private final String licenseUrl = "https://mit-license.org/";
310 | // 接口作者联系方式
311 | private final Contact contact = new Contact("calebman", "https://github.com/calebman", "chenjianhui0428@gmail.com");
312 |
313 | @Bean
314 | public Docket buildDocket() {
315 | return new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInf())
316 | .select().build();
317 | }
318 |
319 | private ApiInfo buildApiInf() {
320 | return new ApiInfoBuilder().title(title).termsOfServiceUrl(termsOfServiceUrl).description(description)
321 | .version(version).license(license).licenseUrl(licenseUrl).contact(contact).build();
322 |
323 | }
324 |
325 | }
326 | ```
327 | >2. 在SpringBootExampleApplication.java中启用Swagger2注解
328 |
329 | 在@SpringBootApplication注解下面加上@EnableSwagger2注解
330 | >3. 常用注解示例
331 | ```
332 | //Contorller中的注解示例
333 | @Controller
334 | @RequestMapping("/v1/product")
335 | // 表示标识这个类是swagger的资源
336 | @Api(value = "DocController", tags = {"restful api示例"})
337 | public class DocController extends BaseController {
338 |
339 | @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
340 | @ResponseBody
341 | //表示一个http请求的操作
342 | @ApiOperation(value = "修改指定产品", httpMethod = "PUT", produces = "application/json")
343 | //@ApiImplicitParams用于方法,包含多个@ApiImplicitParam表示单独的请求参数
344 | @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "产品ID", required = true, paramType = "path")})
345 | public WebResult update(@PathVariable("id") Integer id, @ModelAttribute Product product) {
346 | logger.debug("修改指定产品接收产品id与产品信息=>%d,{}", id, product);
347 | if (id == null || "".equals(id)) {
348 | logger.debug("产品id不能为空");
349 | return WebResult.error(ERRORDetail.RC_0101001);
350 | }
351 | return WebResult.success();
352 | }
353 | }
354 | //Model中的注解示例
355 | //表示对类进行说明,用于参数用实体类接收
356 | @ApiModel(value = "产品信息")
357 | public class Product {
358 | //表示对model属性的说明或者数据操作更改
359 | @ApiModelProperty(required = true, name = "name", value = "产品名称", dataType = "query")
360 | private String name;
361 | @ApiModelProperty(name = "type", value = "产品类型", dataType = "query")
362 | private String type;
363 | }
364 | ```
365 | >4. 生成json形式的文档
366 |
367 | 集成成功后启动项目控制台会打印级别为INFO的日志,截取部分如下,表明可通过访问应用的v2/api-docs接口得到文档api的json格式数据,可在浏览器输入指定地址验证集成是否成功
368 | ```
369 | Mapped "{[/v2/api-docs],methods=[GET],produces=[application/json || application/hal+json]}"
370 | http://localhost:8080/v2/api-docs
371 | ```
372 | ### 多环境配置
373 | 应用研发过程中多环境是不可避免的,假设我们现在有开发、演示、生产三个不同的环境其配置也不同,如果每次都在打包环节来进行配置难免出错,SpringBoot支持通过命令启动不同的环境,但是配置文件需要满足application-{profile}.properties的格式,profile代表对应环境的标识,加载时可通过不同命令加载不同环境。
374 | ```
375 | application-dev.properties:开发环境
376 | application-test.properties:演示环境
377 | application-prod.properties:生产环境
378 | # 运行演示环境命令
379 | java -jar spring-boot-example-0.0.1-SNAPSHOT --spring.profiles.active=test
380 | ```
381 | 基于现在的项目实现多环境我们需要在application.yml同级目录新建application-dev.yml、application-test.yml、application-prod.yml三个不同环境的配置文件,将不变的公有配置如druid的大部分、pagehelper分页插件以及mybatis包扫描配置放置于application.yml中,并在application.yml中配置默认采用开发环境,那么如果不带--spring.profiles.active启动应用就默认为开发环境启动,变动较大的配置如数据库的账号密码分别写入不同环境的配置文件中
382 | ```
383 | spring:
384 | profiles:
385 | # 默认使用开发环境
386 | active: dev
387 | ```
388 | 配置到这里我们的项目目录结构如下图所示
389 | 
390 | 
391 |
392 | 至此我们分别完成了Mybatis、Swagger2以及多环境的集成,接下来我们配置多环境下的logger。对于logger我们总是希望在项目研发过程中越多越好,能够给予足够的信息定位bug,项目处于演示或者上线状态时为了不让日志打印影响程序性能我们只需要警告或者错误的日志,并且需要写入文件,那么接下来就基于logback实现多环境下的日志配置
393 | ### 多环境下的日志配置
394 | 创建logback-spring.xml在application.yml的同级目录,springboot推荐使用logback-spring.xml而不是logback.xml文件,logback-spring.xml的配置内容如下所示
395 | ```
396 |
397 |
398 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 | %d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n
432 |
433 |
434 |
435 |
436 |
437 |
442 | ${logdir}/${appname}.log
443 |
444 |
445 |
446 | ${logdir}/${appname}.%d{yyyy-MM-dd}.log
447 | ${maxdays}
448 | ${maxsize}
449 |
450 |
451 |
452 | UTF-8
453 | %d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 | ```
492 | 日志配置中引用了application.yml的配置信息,主要有logdir、appname、basepackage三项,logdir是日志文件的写入地址,可以传入相对路径,appname是应用名称,引入这项是为了通过日志文件名称区分是哪个应该输出的,basepackage是包过滤配置,比如开发环境中需要打印debug级别以上的日志,但是又想使除我写的logger之外的DEBUG不打印,可过滤到本项目的包名才用DEBUG打印,此外包名使用INFO级别打印,在application.yml中新建这三项配置,也可在不同环境配置不同属性
493 | ```
494 | #应用配置
495 | resources:
496 | # log文件写入地址
497 | logdir: logs/
498 | # 应用名称
499 | appname: spring-boot-example
500 | # 日志打印的基础扫描包
501 | basepackage: com.spring.demo.springbootexample
502 | ```
503 | 使用不同环境启动测试logger配置是否生效,在开发环境下将打印DEBUG级别以上的四条logger记录,在演示环境下降打印INFO级别以上的三条记录并写入文件,在生产环境下只打印ERROR级别以上的一条记录并写入文件
504 | ```
505 | @RequestMapping("/logger")
506 | @ResponseBody
507 | public WebResult logger() {
508 | logger.trace("日志输出 {}", "trace");
509 | logger.debug("日志输出 {}", "debug");
510 | logger.info("日志输出 {}", "info");
511 | logger.warn("日志输出 {}", "warn");
512 | logger.error("日志输出 {}", "error");
513 | return "00";
514 | }
515 | ```
516 | ### jwt配置
517 |
518 | pom文件添加jwt与spring security依赖
519 |
520 | ```
521 |
522 | org.springframework.boot
523 | spring-boot-starter-security
524 |
525 |
526 | io.jsonwebtoken
527 | jjwt
528 | 0.7.0
529 |
530 | ```
531 |
532 | 自定义配置文件中添加jwt超时时间,token生成私钥
533 |
534 | 在/intercepter/JWTAuthInterceptor中定义生成token置于response头的方法以及解析token校验的方法
535 | ```
536 |
537 |
538 | #jwt配置
539 | jwt:
540 | expiredTime: 600000
541 | key: jwtkey
542 |
543 | ```
544 |
545 |
546 | 配置某些url不被控制,例如 /login方法,只需要将@ignoreJWT注解用于Controller类或者UrlMapping的方法上即可
547 | JWT校验不通过采取在拦截器中抛出JWTExpiredException、JWTIllegalException来返回响应信息,异常被抛出后的处理在全局异常处理器中,声明捕获以上两个jwt相关异常即可。
548 | ```
549 | @RestController
550 | @Slf4j
551 | public class TestController {
552 |
553 | @Value("${jwt.key}")
554 | private String JWTKey;
555 | @Value("${jwt.expiredTime}")
556 | private long expiredTime;
557 |
558 | @ignoreJWT
559 | @PostMapping("/login")
560 | public Response JWTLogin(String userName, String password){
561 | log.info("用户登陆:{}",userName);
562 | Response response = new Response();
563 | if ("ohaha".equals(userName) && "123456".equals(password)){
564 | JWTUser user = JWTUser.builder().userName(userName).password(password).build();
565 | String JWTToken = JWTUtils.createJWT(user, UUID.randomUUID().toString(), user.getUserName(), JWTKey, expiredTime);
566 | response.setData(JWTToken);
567 | }else {
568 | response.fail("登陆失败");
569 | }
570 | return response;
571 | }
572 | }
573 | ```
574 |
575 | ### 常用配置
576 | ##### 加载自定义配置
577 | ```
578 | @Component
579 | @PropertySource(value = {"classpath:application.yml"}, encoding = "utf-8")
580 | public class Config {
581 |
582 | @Value("${resources.midpHost}")
583 | private String midpHost;
584 |
585 | public String getMidpHost() {
586 | return midpHost;
587 | }
588 | }
589 | ```
590 | ##### 全局异常处理器
591 | ```
592 | @ControllerAdvice
593 | public class GlobalExceptionHandler {
594 |
595 | Logger logger = LoggerFactory.getLogger(GlobalExceptionResolver.class);
596 |
597 | @ExceptionHandler(value = Exception.class)
598 | @ResponseBody
599 | public WebResult exceptionHandle(HttpServletRequest req, Exception ex) {
600 | ex.printStackTrace();
601 | logger.error("未知异常", ex);
602 | return WebResult.error(ERRORDetail.RC_0401001);
603 | }
604 | }
605 | ```
606 | ### 示例工程开源地址
607 | [github](https://github.com/calebman/spring-boot-example)
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven2 Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Migwn, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | # TODO classpath?
118 | fi
119 |
120 | if [ -z "$JAVA_HOME" ]; then
121 | javaExecutable="`which javac`"
122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
123 | # readlink(1) is not available as standard on Solaris 10.
124 | readLink=`which readlink`
125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
126 | if $darwin ; then
127 | javaHome="`dirname \"$javaExecutable\"`"
128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
129 | else
130 | javaExecutable="`readlink -f \"$javaExecutable\"`"
131 | fi
132 | javaHome="`dirname \"$javaExecutable\"`"
133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
134 | JAVA_HOME="$javaHome"
135 | export JAVA_HOME
136 | fi
137 | fi
138 | fi
139 |
140 | if [ -z "$JAVACMD" ] ; then
141 | if [ -n "$JAVA_HOME" ] ; then
142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
143 | # IBM's JDK on AIX uses strange locations for the executables
144 | JAVACMD="$JAVA_HOME/jre/sh/java"
145 | else
146 | JAVACMD="$JAVA_HOME/bin/java"
147 | fi
148 | else
149 | JAVACMD="`which java`"
150 | fi
151 | fi
152 |
153 | if [ ! -x "$JAVACMD" ] ; then
154 | echo "Error: JAVA_HOME is not defined correctly." >&2
155 | echo " We cannot execute $JAVACMD" >&2
156 | exit 1
157 | fi
158 |
159 | if [ -z "$JAVA_HOME" ] ; then
160 | echo "Warning: JAVA_HOME environment variable is not set."
161 | fi
162 |
163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
164 |
165 | # traverses directory structure from process work directory to filesystem root
166 | # first directory with .mvn subdirectory is considered project base directory
167 | find_maven_basedir() {
168 |
169 | if [ -z "$1" ]
170 | then
171 | echo "Path not specified to find_maven_basedir"
172 | return 1
173 | fi
174 |
175 | basedir="$1"
176 | wdir="$1"
177 | while [ "$wdir" != '/' ] ; do
178 | if [ -d "$wdir"/.mvn ] ; then
179 | basedir=$wdir
180 | break
181 | fi
182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
183 | if [ -d "${wdir}" ]; then
184 | wdir=`cd "$wdir/.."; pwd`
185 | fi
186 | # end of workaround
187 | done
188 | echo "${basedir}"
189 | }
190 |
191 | # concatenates all lines of a file
192 | concat_lines() {
193 | if [ -f "$1" ]; then
194 | echo "$(tr -s '\n' ' ' < "$1")"
195 | fi
196 | }
197 |
198 | BASE_DIR=`find_maven_basedir "$(pwd)"`
199 | if [ -z "$BASE_DIR" ]; then
200 | exit 1;
201 | fi
202 |
203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
204 | echo $MAVEN_PROJECTBASEDIR
205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
206 |
207 | # For Cygwin, switch paths to Windows format before running java
208 | if $cygwin; then
209 | [ -n "$M2_HOME" ] &&
210 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
211 | [ -n "$JAVA_HOME" ] &&
212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
213 | [ -n "$CLASSPATH" ] &&
214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
215 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
217 | fi
218 |
219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
220 |
221 | exec "$JAVACMD" \
222 | $MAVEN_OPTS \
223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
226 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven2 Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
40 |
41 | @REM set %HOME% to equivalent of $HOME
42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
43 |
44 | @REM Execute a user defined script before this one
45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
49 | :skipRcPre
50 |
51 | @setlocal
52 |
53 | set ERROR_CODE=0
54 |
55 | @REM To isolate internal variables from possible post scripts, we use another setlocal
56 | @setlocal
57 |
58 | @REM ==== START VALIDATION ====
59 | if not "%JAVA_HOME%" == "" goto OkJHome
60 |
61 | echo.
62 | echo Error: JAVA_HOME not found in your environment. >&2
63 | echo Please set the JAVA_HOME variable in your environment to match the >&2
64 | echo location of your Java installation. >&2
65 | echo.
66 | goto error
67 |
68 | :OkJHome
69 | if exist "%JAVA_HOME%\bin\java.exe" goto init
70 |
71 | echo.
72 | echo Error: JAVA_HOME is set to an invalid directory. >&2
73 | echo JAVA_HOME = "%JAVA_HOME%" >&2
74 | echo Please set the JAVA_HOME variable in your environment to match the >&2
75 | echo location of your Java installation. >&2
76 | echo.
77 | goto error
78 |
79 | @REM ==== END VALIDATION ====
80 |
81 | :init
82 |
83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
84 | @REM Fallback to current working directory if not found.
85 |
86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
88 |
89 | set EXEC_DIR=%CD%
90 | set WDIR=%EXEC_DIR%
91 | :findBaseDir
92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
93 | cd ..
94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
95 | set WDIR=%CD%
96 | goto findBaseDir
97 |
98 | :baseDirFound
99 | set MAVEN_PROJECTBASEDIR=%WDIR%
100 | cd "%EXEC_DIR%"
101 | goto endDetectBaseDir
102 |
103 | :baseDirNotFound
104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
105 | cd "%EXEC_DIR%"
106 |
107 | :endDetectBaseDir
108 |
109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
110 |
111 | @setlocal EnableExtensions EnableDelayedExpansion
112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
114 |
115 | :endReadAdditionalConfig
116 |
117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
118 |
119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
121 |
122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
123 | if ERRORLEVEL 1 goto error
124 | goto end
125 |
126 | :error
127 | set ERROR_CODE=1
128 |
129 | :end
130 | @endlocal & set ERROR_CODE=%ERROR_CODE%
131 |
132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
136 | :skipRcPost
137 |
138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
140 |
141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
142 |
143 | exit /B %ERROR_CODE%
144 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.spring.demo
7 | spring-boot-example
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 | spring-boot-example
12 | Demo project for Spring Boot
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter-parent
17 | 2.0.0.RELEASE
18 |
19 |
20 |
21 |
22 | UTF-8
23 | UTF-8
24 | 1.8
25 |
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-security
32 |
33 |
34 | org.projectlombok
35 | lombok
36 | true
37 |
38 |
39 |
40 | io.jsonwebtoken
41 | jjwt
42 | 0.9.1
43 |
44 |
45 |
46 |
47 | org.springframework.boot
48 | spring-boot-starter-web
49 |
50 |
51 |
52 | org.mybatis.spring.boot
53 | mybatis-spring-boot-starter
54 | 1.3.1
55 |
56 |
57 |
58 | mysql
59 | mysql-connector-java
60 | runtime
61 |
62 |
63 |
64 | org.springframework.boot
65 | spring-boot-starter-test
66 | test
67 |
68 |
69 |
70 | com.github.pagehelper
71 | pagehelper-spring-boot-starter
72 | 1.2.3
73 |
74 |
75 |
76 | com.alibaba
77 | druid-spring-boot-starter
78 | 1.1.1
79 |
80 |
81 |
82 | com.alibaba
83 | fastjson
84 | 1.2.31
85 |
86 |
87 |
88 | io.springfox
89 | springfox-swagger2
90 | 2.5.0
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | org.springframework.boot
99 | spring-boot-maven-plugin
100 |
101 |
102 |
103 | org.mybatis.generator
104 | mybatis-generator-maven-plugin
105 | 1.3.2
106 |
107 |
108 |
109 | ${basedir}/src/main/resources/generator/generatorConfig.xml
110 |
111 | true
112 | true
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/SpringBootExampleApplication.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample;
2 |
3 | import org.mybatis.spring.annotation.MapperScan;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import springfox.documentation.swagger2.annotations.EnableSwagger2;
7 |
8 | @SpringBootApplication
9 | @EnableSwagger2
10 | @MapperScan("com.spring.demo.springbootexample.mybatis.mapper")
11 | public class SpringBootExampleApplication {
12 |
13 | public static void main(String[] args) {
14 | SpringApplication.run(SpringBootExampleApplication.class, args);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/annotation/ignoreJWT.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * 在使用该注解的方法上不进行jwt校验
10 | */
11 | @Retention(RetentionPolicy.RUNTIME)
12 | @Target(value={ElementType.METHOD, ElementType.TYPE})
13 | public @interface ignoreJWT {
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/beans/Product.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.beans;
2 |
3 | import io.swagger.annotations.ApiModel;
4 | import io.swagger.annotations.ApiModelProperty;
5 | //
6 | @ApiModel(value = "产品信息")
7 | public class Product {
8 |
9 | @ApiModelProperty(required = true, name = "name", value = "产品名称", dataType = "query")
10 | private String name;
11 | @ApiModelProperty(name = "type", value = "产品类型", dataType = "query")
12 | private String type;
13 |
14 | public String getName() {
15 | return name;
16 | }
17 |
18 | public void setName(String name) {
19 | this.name = name;
20 | }
21 |
22 | public String getType() {
23 | return type;
24 | }
25 |
26 | public void setType(String type) {
27 | this.type = type;
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "Product{" +
33 | "name='" + name + '\'' +
34 | ", type='" + type + '\'' +
35 | '}';
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/beans/entity/JWTUser.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.beans.entity;
2 |
3 | import lombok.Builder;
4 | import lombok.Data;
5 | import lombok.ToString;
6 |
7 | @Data
8 | @Builder
9 | @ToString
10 | public class JWTUser {
11 | private String userName;
12 | private String password;
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/beans/msg/Request.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.beans.msg;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 |
7 | /**
8 | * 通用请求体
9 | * @param
10 | */
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | public class Request {
15 | private T data;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/beans/msg/Response.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.beans.msg;
2 |
3 | import com.spring.demo.springbootexample.constants.ReqResConsts;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | /**
9 | * 通用响应体
10 | * @param
11 | */
12 | @Data
13 | @AllArgsConstructor
14 | @NoArgsConstructor
15 | public class Response {
16 | private T data;
17 | private String code;
18 | private String msg;
19 |
20 | public Response success(String msg){
21 | this.code = ReqResConsts.SUCCESS_CODE;
22 | this.msg = msg;
23 | return this;
24 | }
25 | public Response success(){
26 | return success(null);
27 | }
28 |
29 | public Response fail(String msg){
30 | this.code = ReqResConsts.FAIL_CODE;
31 | this.msg = msg;
32 | return this;
33 | }
34 | public Responsefail(){
35 | return fail(null);
36 | }
37 | public Response msg(String code, String msg){
38 | this.code = code;
39 | this.msg = msg;
40 | return this;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/config/Config.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.config;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.context.annotation.PropertySource;
5 | import org.springframework.stereotype.Component;
6 |
7 | @Component
8 | @PropertySource(value = {"classpath:application.yml"}, encoding = "utf-8")
9 | public class Config {
10 |
11 | @Value("${resources.midpHost}")
12 | private String midpHost;
13 |
14 | @Value("${jwt.SECRET}")
15 | private String secret;
16 |
17 | @Value("${jwt.EXPIRATIONTIME}")
18 | private long expirationtime;
19 |
20 | public String getSecret() {
21 | return secret;
22 | }
23 |
24 | public long getExpirationtime() {
25 | return expirationtime;
26 | }
27 |
28 | public String getMidpHost() {
29 | return midpHost;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/config/InterceptorConfiger.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.config;
2 |
3 | import com.spring.demo.springbootexample.intercepter.JWTAuthInterceptor;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.web.servlet.HandlerInterceptor;
7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
9 |
10 | @Configuration
11 | public class InterceptorConfiger implements WebMvcConfigurer {
12 | @Override
13 | public void addInterceptors(InterceptorRegistry registry) {
14 | registry.addInterceptor(getJWTAuthInterceptor()).excludePathPatterns("/error","/user/login");
15 | }
16 |
17 | @Bean
18 | public HandlerInterceptor getJWTAuthInterceptor(){
19 | return new JWTAuthInterceptor();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/config/SwaggerConfig.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import springfox.documentation.builders.ApiInfoBuilder;
6 | import springfox.documentation.service.ApiInfo;
7 | import springfox.documentation.service.Contact;
8 | import springfox.documentation.spi.DocumentationType;
9 | import springfox.documentation.spring.web.plugins.Docket;
10 |
11 | @Configuration
12 | public class SwaggerConfig {
13 | // 接口版本号
14 | private final String version = "1.0";
15 | // 接口大标题
16 | private final String title = "SpringBoot示例工程";
17 | // 具体的描述
18 | private final String description = "API文档自动生成示例";
19 | // 服务说明url
20 | private final String termsOfServiceUrl = "http://www.kingeid.com";
21 | // licence
22 | private final String license = "MIT";
23 | // licnce url
24 | private final String licenseUrl = "https://mit-license.org/";
25 | // 接口作者联系方式
26 | private final Contact contact = new Contact("calebman", "https://github.com/calebman", "chenjianhui0428@gmail.com");
27 |
28 | @Bean
29 | public Docket buildDocket() {
30 | return new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInf())
31 | .select().build();
32 | }
33 |
34 | private ApiInfo buildApiInf() {
35 | return new ApiInfoBuilder().title(title).termsOfServiceUrl(termsOfServiceUrl).description(description)
36 | .version(version).license(license).licenseUrl(licenseUrl).contact(contact).build();
37 |
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/constants/ErrCodeEnum.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.constants;
2 |
3 | public enum ErrCodeEnum {
4 | JWT_AUTH_FAIL("J0000001", "JWT校验失败"),
5 | JWT_ILL_ERROR("J0000002", "JWT参数非法"),
6 | SYS_ERROR("S0000001", "系统异常"),
7 | LOGIN_FAIL("U0000001", "用户名或密码不正确"),
8 | NO_USER("U000002", "用户不存在"),
9 | ;
10 |
11 | ErrCodeEnum(String code, String msg){
12 | this.code = code;
13 | this.msg = msg;
14 | }
15 |
16 | public String code;
17 | public String msg;
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/constants/JWTConsts.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.constants;
2 |
3 | public class JWTConsts {
4 | /**
5 | * demo claim key值
6 | */
7 | public static final String NAME = "user_name";
8 |
9 | public static final String PASSWORD = "password";
10 |
11 | public static final String JWT_HEADER_KEY = "JWT-Token";
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/constants/ReqResConsts.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.constants;
2 |
3 | public class ReqResConsts {
4 | public static final String SUCCESS_CODE = "000000";
5 | public static final String SUCCESS_MSG = "成功";
6 |
7 | public static final String FAIL_CODE = "999999";
8 | public static final String FAIL_MSG = "失败";
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/controller/DocController.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.controller;
2 |
3 |
4 | import com.spring.demo.springbootexample.beans.Product;
5 | import com.spring.demo.springbootexample.config.Config;
6 | import com.spring.demo.springbootexample.result.ERRORDetail;
7 | import com.spring.demo.springbootexample.result.Pagination;
8 | import com.spring.demo.springbootexample.result.WebResult;
9 | import io.swagger.annotations.Api;
10 | import io.swagger.annotations.ApiImplicitParam;
11 | import io.swagger.annotations.ApiImplicitParams;
12 | import io.swagger.annotations.ApiOperation;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.stereotype.Controller;
17 | import org.springframework.web.bind.annotation.*;
18 |
19 | import java.util.Map;
20 | import java.util.concurrent.ConcurrentHashMap;
21 |
22 | @Controller
23 | @RequestMapping("/v1/product")
24 | @Api(value = "DocController", tags = {"restful api示例"})
25 | public class DocController {
26 |
27 | @Autowired
28 | Config config;
29 |
30 | static Map products = new ConcurrentHashMap();
31 |
32 | Logger logger = LoggerFactory.getLogger(DocController.class);
33 |
34 | @RequestMapping(value = "", method = RequestMethod.POST)
35 | @ResponseBody
36 | @ApiOperation(value = "添加产品", httpMethod = "POST", produces = "application/json")
37 | public WebResult add(@ModelAttribute Product product) {
38 | logger.debug("添加产品接收信息=>{}", product);
39 | if (product == null) {
40 | logger.debug("产品信息不能为空");
41 | return WebResult.error(ERRORDetail.RC_0101001);
42 | }
43 | if (product.getName() == null || "".equals(product.getName())) {
44 | logger.debug("产品名称不能为空");
45 | return WebResult.error(ERRORDetail.RC_0101002);
46 | }
47 | products.put(products.size(), product);
48 | return WebResult.success();
49 | }
50 |
51 | @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
52 | @ResponseBody
53 | @ApiOperation(value = "移除指定产品", httpMethod = "DELETE", produces = "application/json")
54 | @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "产品ID", required = true, paramType = "path")})
55 | public WebResult delete(@PathVariable("id") Integer id) {
56 | logger.debug("移除指定产品接收产品id=>%d", id);
57 | if (id == null || "".equals(id)) {
58 | logger.debug("产品id不能为空");
59 | return WebResult.error(ERRORDetail.RC_0101001);
60 | }
61 | products.remove(id);
62 | return WebResult.success();
63 | }
64 |
65 | @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
66 | @ResponseBody
67 | @ApiOperation(value = "修改指定产品", httpMethod = "PUT", produces = "application/json")
68 | @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "产品ID", required = true, paramType = "path")})
69 | public WebResult update(@PathVariable("id") Integer id, @ModelAttribute Product product) {
70 | logger.debug("修改指定产品接收产品id与产品信息=>%d,{}", id, product);
71 | if (id == null || "".equals(id)) {
72 | logger.debug("产品id不能为空");
73 | return WebResult.error(ERRORDetail.RC_0101001);
74 | }
75 | products.put(id, product);
76 | return WebResult.success();
77 | }
78 |
79 | @RequestMapping(value = "", method = RequestMethod.GET)
80 | @ResponseBody
81 | @ApiOperation(value = "获取以及筛选产品", httpMethod = "GET", produces = "application/json")
82 | @ApiImplicitParams({@ApiImplicitParam(name = "name", value = "根据产品名称筛选", required = false, paramType = "query"),
83 | @ApiImplicitParam(name = "type", value = "根据产品类型筛选", required = false, paramType = "query", dataType = "int")})
84 | public WebResult searchAll(@ModelAttribute Pagination pagination, String name, String type) {
85 | logger.debug("获取以及筛选产品接收分页、产品名称、产品类型=>{},{},{}", pagination, name, type);
86 | if (!pagination.isRequire()) {
87 | logger.debug("分页参数不能为空");
88 | return WebResult.error(ERRORDetail.RC_0101003);
89 | }
90 | return WebResult.success(products);
91 | }
92 |
93 | @RequestMapping(value = "/{id}", method = RequestMethod.GET)
94 | @ResponseBody
95 | @ApiOperation(value = "获取指定产品", httpMethod = "GET", produces = "application/json")
96 | @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "产品ID", required = true, paramType = "path")})
97 | public WebResult search(@PathVariable("id") Integer id) {
98 | logger.debug("获取指定产品接收产品id=>%d", id);
99 | if (id == null || "".equals(id)) {
100 | logger.debug("产品id不能为空");
101 | return WebResult.error(ERRORDetail.RC_0101001);
102 | }
103 | return WebResult.success(products.get(id));
104 | }
105 |
106 | @RequestMapping("/logger")
107 | @ResponseBody
108 | public WebResult logger() {
109 | logger.trace("日志输出 {},{}", "trace", config.getMidpHost());
110 | logger.debug("日志输出 {},{}", "debug", config.getMidpHost());
111 | logger.info("日志输出 {},{}", "info", config.getMidpHost());
112 | logger.warn("日志输出 {},{}", "warn", config.getMidpHost());
113 | logger.error("日志输出 {},{}", "error", config.getMidpHost());
114 | return WebResult.success();
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/controller/JwtTestController.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.controller;
2 |
3 | import com.spring.demo.springbootexample.jwt.JsonWebTokenUtility;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.stereotype.Controller;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.ResponseBody;
8 |
9 | import javax.servlet.http.HttpServletRequest;
10 | import javax.servlet.http.HttpServletResponse;
11 |
12 | @Controller
13 | public class JwtTestController {
14 |
15 | @Autowired
16 | private JsonWebTokenUtility jwt;
17 |
18 | @RequestMapping(value = "/testJwt")
19 | @ResponseBody
20 | public String login(HttpServletResponse response){
21 | jwt.addToken(response,"ohaha~");
22 |
23 | System.out.println("login controller");
24 | return "ohaha";
25 | }
26 |
27 | @RequestMapping(value = "/testJwtdecrypt")
28 | @ResponseBody
29 | public String dec(HttpServletRequest request){
30 |
31 | jwt.getAuthentication(request);
32 | return "ohaha";
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/controller/TestController.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.controller;
2 |
3 | import com.spring.demo.springbootexample.annotation.ignoreJWT;
4 | import com.spring.demo.springbootexample.beans.entity.JWTUser;
5 | import com.spring.demo.springbootexample.beans.msg.Response;
6 | import com.spring.demo.springbootexample.utils.JWTUtils;
7 | import lombok.extern.slf4j.Slf4j;
8 | import org.springframework.beans.factory.annotation.Value;
9 | import org.springframework.web.bind.annotation.PostMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import java.util.UUID;
13 |
14 | @RestController
15 | @Slf4j
16 | public class TestController {
17 |
18 | @Value("${jwt.key}")
19 | private String JWTKey;
20 | @Value("${jwt.expiredTime}")
21 | private long expiredTime;
22 |
23 | @ignoreJWT
24 | @PostMapping("/login")
25 | public Response JWTLogin(String userName, String password){
26 | log.info("用户登陆:{}",userName);
27 | Response response = new Response();
28 | if ("ohaha".equals(userName) && "123456".equals(password)){
29 | JWTUser user = JWTUser.builder().userName(userName).password(password).build();
30 | String JWTToken = JWTUtils.createJWT(user, UUID.randomUUID().toString(), user.getUserName(), JWTKey, expiredTime);
31 | response.setData(JWTToken);
32 | }else {
33 | response.fail("登陆失败");
34 | }
35 | return response;
36 | }
37 | @PostMapping("/test")
38 | public Response test(){
39 | log.info("test controller");
40 | Response response = new Response();
41 | response.success();
42 | return response;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/controller/global/GlobalBasicErrorHandler.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.controller.global;
2 |
3 | import com.google.common.collect.Maps;
4 | import com.spring.demo.springbootexample.constants.ErrCodeEnum;
5 | import org.springframework.boot.autoconfigure.web.ServerProperties;
6 | import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
7 | import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
8 | import org.springframework.http.HttpStatus;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 | import org.springframework.web.servlet.ModelAndView;
12 |
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpServletResponse;
15 | import java.util.Map;
16 |
17 | @RestController
18 | @RequestMapping("${server.error.path:${error.path:/error}}")
19 | public class GlobalBasicErrorHandler extends BasicErrorController {
20 |
21 | public GlobalBasicErrorHandler(ServerProperties serverProperties) {
22 | super(new DefaultErrorAttributes(), serverProperties.getError());
23 | }
24 | @Override
25 | protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map model) {
26 | return super.resolveErrorView(request,response,status,model);
27 | }
28 | @Override
29 | protected Map getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
30 |
31 | Map respMap = Maps.newHashMap();
32 | respMap.put("code",ErrCodeEnum.SYS_ERROR.code);
33 | respMap.put("msg",ErrCodeEnum.SYS_ERROR.msg);
34 | return respMap;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/controller/global/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.controller.global;
2 |
3 |
4 | import com.spring.demo.springbootexample.beans.msg.Response;
5 | import com.spring.demo.springbootexample.constants.ErrCodeEnum;
6 | import com.spring.demo.springbootexample.exception.JWTExpiredException;
7 | import com.spring.demo.springbootexample.exception.JWTIllegalException;
8 | import lombok.extern.slf4j.Slf4j;
9 | import org.springframework.web.bind.annotation.ControllerAdvice;
10 | import org.springframework.web.bind.annotation.ExceptionHandler;
11 | import org.springframework.web.bind.annotation.ResponseBody;
12 |
13 | import javax.servlet.http.HttpServletResponse;
14 |
15 | /**
16 | * 全局异常处理
17 | */
18 | @ControllerAdvice
19 | @ResponseBody
20 | @Slf4j
21 | public class GlobalExceptionHandler {
22 |
23 | @ExceptionHandler(Exception.class)
24 | public Response defaultExceptionHandler(HttpServletResponse response, Exception exception){
25 | response.setStatus(500);
26 | log.error("exception 异常");
27 | Response res = new Response();
28 | return res.msg(ErrCodeEnum.SYS_ERROR.code, ErrCodeEnum.SYS_ERROR.msg);
29 | }
30 |
31 | @ExceptionHandler(JWTIllegalException.class)
32 | public Response JWTIllegalExceptionHandler(HttpServletResponse response, Exception exception){
33 | response.setStatus(500);
34 | log.error("demo 非法 异常");
35 | Response res = new Response();
36 | return res.msg(ErrCodeEnum.JWT_ILL_ERROR.code, ErrCodeEnum.JWT_ILL_ERROR.msg);
37 | }
38 | @ExceptionHandler(JWTExpiredException.class)
39 | public Response JWTExpiredExceptionHandler(HttpServletResponse response, Exception exception){
40 | response.setStatus(500);
41 | log.error("demo 超时 异常");
42 | Response res = new Response();
43 | return res.msg(ErrCodeEnum.JWT_AUTH_FAIL.code, ErrCodeEnum.JWT_AUTH_FAIL.msg);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/exception/JWTExpiredException.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.exception;
2 |
3 | /**
4 | * JWT过期异常
5 | */
6 | public class JWTExpiredException extends Exception{
7 | public JWTExpiredException(String msg){
8 | super(msg);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/exception/JWTIllegalException.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.exception;
2 |
3 | /**
4 | * JWT非法异常
5 | */
6 | public class JWTIllegalException extends Exception{
7 | public JWTIllegalException(String msg){
8 | super(msg);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/intercepter/JWTAuthInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.intercepter;
2 |
3 |
4 | import com.spring.demo.springbootexample.annotation.ignoreJWT;
5 | import com.spring.demo.springbootexample.beans.entity.JWTUser;
6 | import com.spring.demo.springbootexample.constants.JWTConsts;
7 | import com.spring.demo.springbootexample.exception.JWTIllegalException;
8 | import com.spring.demo.springbootexample.utils.JWTUtils;
9 | import io.jsonwebtoken.ExpiredJwtException;
10 | import lombok.extern.slf4j.Slf4j;
11 | import org.springframework.beans.factory.annotation.Value;
12 | import org.springframework.util.StringUtils;
13 | import org.springframework.web.method.HandlerMethod;
14 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
15 |
16 | import javax.servlet.http.HttpServletRequest;
17 | import javax.servlet.http.HttpServletResponse;
18 |
19 | @Slf4j
20 | public class JWTAuthInterceptor extends HandlerInterceptorAdapter {
21 | @Value("${jwt.key}")
22 | private String JWTKey;
23 |
24 | public JWTAuthInterceptor() {
25 | super();
26 | }
27 |
28 | @Override
29 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
30 | log.info("开始校验JWT");
31 | HandlerMethod handlerMethod = (HandlerMethod) handler;
32 | ignoreJWT ignoreAnnotation = handlerMethod.getBeanType().getAnnotation(ignoreJWT.class);
33 | if (ignoreAnnotation == null){
34 | ignoreAnnotation = handlerMethod.getMethodAnnotation(ignoreJWT.class);
35 | }
36 | if (ignoreAnnotation != null){
37 | log.info("当前方法不进行jwt校验");
38 | return super.preHandle(request, response, handler);
39 | }
40 | String jwtToken = request.getHeader(JWTConsts.JWT_HEADER_KEY);
41 | if (!StringUtils.hasText(jwtToken)){
42 | log.error("no JWT Token");
43 | throw new JWTIllegalException("no jwt token");
44 | }
45 | try{
46 | JWTUser userInfo = JWTUtils.parseJWTInfo(jwtToken, JWTKey);
47 | log.info("用户信息:{}", userInfo);
48 | request.setAttribute("userInfo", userInfo);
49 | }catch (ExpiredJwtException e){
50 | log.error("JWT Token 失效");
51 | }catch (Exception e){
52 | throw new RuntimeException("JWT 异常");
53 | }
54 | return super.preHandle(request, response, handler);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/jwt/JsonWebTokenUtility.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.jwt;
2 |
3 |
4 | import com.spring.demo.springbootexample.config.Config;
5 | import io.jsonwebtoken.Jwts;
6 | import io.jsonwebtoken.SignatureAlgorithm;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
9 | import org.springframework.security.core.Authentication;
10 | import org.springframework.stereotype.Component;
11 |
12 | import javax.servlet.http.HttpServletRequest;
13 | import javax.servlet.http.HttpServletResponse;
14 | import java.util.Date;
15 |
16 | import static java.util.Collections.emptyList;
17 |
18 | @Component
19 | public class JsonWebTokenUtility {
20 |
21 | @Autowired
22 | Config config;
23 |
24 | String HEADER_NAME = "Authorization";
25 |
26 | public void addToken(HttpServletResponse res, String userId){
27 |
28 |
29 | String JWT = Jwts.builder()
30 | .setSubject(userId)
31 | .setExpiration(new Date(System.currentTimeMillis() + config.getExpirationtime()))
32 | .signWith(SignatureAlgorithm.HS512, config.getSecret())
33 | .compact();
34 | res.addHeader(HEADER_NAME, JWT);
35 | }
36 |
37 | public Authentication getAuthentication(HttpServletRequest request) {
38 | String token = request.getHeader(HEADER_NAME);
39 | if (token != null) {
40 | // parse the token.
41 | String user = Jwts.parser()
42 | .setSigningKey(config.getSecret())
43 | .parseClaimsJws(token)
44 | .getBody()
45 | .getSubject();
46 |
47 | return user != null ?
48 | new UsernamePasswordAuthenticationToken(user, null, emptyList()) :
49 | null;
50 | }
51 | return null;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/result/ERRORDetail.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.result;
2 |
3 |
4 | /**
5 | * 平台错误描述定义,可根据索引查询具体错误描述。
6 | */
7 | public enum ERRORDetail {
8 |
9 | RC_0000000("0000000", "成功"),
10 |
11 | //参数错误【参数为空】
12 | RC_0101001("0101001", "id为空"),
13 | RC_0101002("0101002", "name为空"),
14 | RC_0101003("0101003", "分页参数为空"),
15 |
16 | //参数错误【格式错误】
17 | RC_0102001("0102001", "biz_sequence_id格式错误"),
18 |
19 | //参数错误【参数无效】
20 | RC_0103001("0103001", "id_type无效"),
21 |
22 | //请求错误
23 | RC_0201004("0201004", "version错误"),
24 | RC_0201005("0201005", "请求地址错误"),
25 |
26 | //认证错误【状态错误】
27 | RC_0301001("0301001", "证书已经注销或未注册"),
28 |
29 | //认证错误【验证失败】
30 | RC_0302001("0302001", "eID签名错误"),
31 |
32 | //操作失败
33 | RC_0303001("0303001", "sms用户处理失败"),
34 | RC_0303002("0303002", "用户拒绝处理"),
35 |
36 | //系统错误
37 | RC_0401001("0401001", "服务异常"),
38 | RC_0401002("0401002", "无法返回"),
39 | RC_0401003("0401003", "返回错误"),
40 | RC_0401006("0401006", "应用服务器缓存异常"),
41 | RC_0401007("0401007", "应用服务器超时"),
42 |
43 | RC_6666666("0500000","异常,没有找到合适的结果描述");
44 |
45 | private String index;
46 | private String meaning;
47 |
48 | ERRORDetail(String index, String meaning) {
49 | this.index = index;
50 | this.setMeaning(meaning);
51 | }
52 |
53 | public String getIndex() {
54 | return this.index;
55 | }
56 |
57 | public void setIndex(String index) {
58 | this.index = index;
59 | }
60 |
61 | public String getMeaning() {
62 | return meaning;
63 | }
64 |
65 | public void setMeaning(String meaning) {
66 | this.meaning = meaning;
67 | }
68 |
69 | public String getResultCode(){
70 | return this.index.substring(0,2);
71 | }
72 |
73 | public String getDetails(){
74 | return this.index+"("+this.meaning+")";
75 | }
76 |
77 | public static ERRORDetail getEnum(String index) {
78 | for (ERRORDetail st : ERRORDetail.values()) {
79 | if (st.index.equals(index)) {
80 | return st;
81 | }
82 | }
83 | return RC_6666666;
84 | }
85 |
86 |
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/result/Pagination.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.result;
2 |
3 | import io.swagger.annotations.ApiModel;
4 | import io.swagger.annotations.ApiModelProperty;
5 |
6 | @ApiModel(value = "分页信息")
7 | public class Pagination {
8 |
9 | @ApiModelProperty(required = true, name = "pageCurrent", value = "当前选择页", dataType = "query")
10 | private String pageCurrent;
11 | @ApiModelProperty(required = true, name = "pageSize", value = "每页条数", dataType = "query")
12 | private String pageSize;
13 |
14 | public String getPageCurrent() {
15 | return pageCurrent;
16 | }
17 |
18 | public void setPageCurrent(String pageCurrent) {
19 | this.pageCurrent = pageCurrent;
20 | }
21 |
22 | public String getPageSize() {
23 | return pageSize;
24 | }
25 |
26 | public void setPageSize(String pageSize) {
27 | this.pageSize = pageSize;
28 | }
29 |
30 | public boolean isRequire() {
31 | if (this == null) {
32 | return false;
33 | }
34 | if (this.getPageCurrent() == null || "".equals(this.getPageCurrent())) {
35 | return false;
36 | }
37 | return this.getPageSize() != null && !"".equals(this.getPageSize());
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/result/WebResult.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.result;
2 |
3 | public class WebResult {
4 |
5 | private String errCode;
6 | private String errMsg;
7 | private Object data;
8 |
9 | public String getErrCode() {
10 | return errCode;
11 | }
12 |
13 | public void setErrCode(String errCode) {
14 | this.errCode = errCode;
15 | }
16 |
17 | public String getErrMsg() {
18 | return errMsg;
19 | }
20 |
21 | public void setErrMsg(String errMsg) {
22 | this.errMsg = errMsg;
23 | }
24 |
25 | public Object getData() {
26 | return data;
27 | }
28 |
29 | public void setData(Object data) {
30 | this.data = data;
31 | }
32 |
33 | public static WebResult error(ERRORDetail errorDetail) {
34 | WebResult result = new WebResult();
35 | result.setErrCode(errorDetail.getResultCode());
36 | result.setErrMsg(errorDetail.getDetails());
37 | result.setData(null);
38 | return result;
39 | }
40 |
41 | public static WebResult success() {
42 | return success(null);
43 | }
44 |
45 | public static WebResult success(Object data) {
46 | WebResult result = new WebResult();
47 | result.setErrCode(ERRORDetail.RC_0000000.getResultCode());
48 | result.setErrMsg(ERRORDetail.RC_0000000.getDetails());
49 | result.setData(data);
50 | return result;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/security/WebSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.security;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
9 |
10 | @Configuration
11 | @EnableWebSecurity
12 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
13 |
14 | @Override
15 | protected void configure(HttpSecurity http) throws Exception {
16 | http
17 | .authorizeRequests()
18 | .antMatchers("/testJwt","/testJwtdecrypt").permitAll()
19 | .anyRequest().authenticated()
20 | .and()
21 | .formLogin()
22 | .loginPage("/login")
23 | .permitAll()
24 | .and()
25 | .logout()
26 | .permitAll();
27 | }
28 |
29 | @Autowired
30 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
31 | auth
32 | .inMemoryAuthentication()
33 | .withUser("user").password("password").roles("USER");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/demo/springbootexample/utils/JWTUtils.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample.utils;
2 |
3 |
4 | import com.spring.demo.springbootexample.beans.entity.JWTUser;
5 | import com.spring.demo.springbootexample.constants.JWTConsts;
6 | import io.jsonwebtoken.*;
7 | import org.apache.tomcat.util.codec.binary.Base64;
8 |
9 | import javax.crypto.SecretKey;
10 | import javax.crypto.spec.SecretKeySpec;
11 | import java.util.Date;
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | public class JWTUtils {
16 |
17 | public static String createJWT(JWTUser user, String jwtId, String subject, String secret, long ttlMillis){
18 | Map claims = new HashMap<>();
19 | claims.put(JWTConsts.NAME, user.getUserName());
20 | claims.put(JWTConsts.PASSWORD, user.getPassword());
21 | return createJWT(claims, jwtId, subject, secret, ttlMillis);
22 | }
23 |
24 | public static String createJWT(Map claims, String jwtId, String subject,String secret, long ttlMillis){
25 | // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
26 | SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
27 | long nowMillis = System.currentTimeMillis();
28 | Date now = new Date(nowMillis);
29 | // 生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
30 | SecretKey key = getKey(secret);
31 | // 下面就是在为payload添加各种标准声明和私有声明了
32 | // 这里其实就是new一个JwtBuilder,设置jwt的body
33 | JwtBuilder builder = Jwts.builder()
34 | // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
35 | .setClaims(claims)
36 | // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
37 | .setId(jwtId)
38 | // iat: jwt的签发时间
39 | .setIssuedAt(now)
40 | // sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
41 | .setSubject(subject)
42 | // 设置签名使用的签名算法和签名使用的秘钥
43 | .signWith(signatureAlgorithm, key);
44 | if (ttlMillis >= 0) {
45 | long expMillis = nowMillis + ttlMillis;
46 | Date exp = new Date(expMillis);
47 | // 设置过期时间
48 | builder.setExpiration(exp);
49 | }
50 | // 就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
51 | return builder.compact();
52 | }
53 | public static Jws parseJWT(String jwt, String secret) throws Exception {
54 | // 签名秘钥,和生成的签名的秘钥一模一样
55 | SecretKey key = getKey(secret);
56 | // 得到DefaultJwtParser
57 | Jws jwsClaims = Jwts.parser()
58 | //设置签名的秘钥
59 | .setSigningKey(key)
60 | //设置需要解析的jwt
61 | .parseClaimsJws(jwt);
62 | return jwsClaims;
63 | }
64 |
65 | public static JWTUser parseJWTInfo(String jwt, String secret) throws Exception {
66 |
67 | Claims claims = parseJWT(jwt,secret).getBody();
68 | JWTUser jwtUser = JWTUser.builder().userName(claims.get(JWTConsts.NAME,String.class)).build();
69 | return jwtUser;
70 | }
71 |
72 | public static SecretKey getKey(String secret){
73 |
74 | byte[] encodedKey = Base64.decodeBase64(secret);
75 | // 根据给定的字节数组使用AES加密算法构造一个密钥,使用 encodedKey中的始于且包含 0 到前 leng 个字节这是当然是所有。(后面的文章中马上回推出讲解Java加密和解密的一些算法)
76 | return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/resources/application-dev.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8080
3 |
4 | spring:
5 | datasource:
6 | url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
7 | username: kingeid
8 | password: 123456
9 |
10 | #自定义配置
11 | resources:
12 | midpHost: 1.1.1.1
13 |
14 | #jwt配置
15 | jwt:
16 | EXPIRATIONTIME: 1000
17 | SECRET: OHAHAHAHA
18 |
--------------------------------------------------------------------------------
/src/main/resources/application-prod.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 18080
3 |
4 | spring:
5 | datasource:
6 | url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
7 | username: kingeid
8 | password: kingeid
9 |
10 | #自定义配置
11 | resources:
12 | logdir: logs/
13 | midpHost: 1.1.1.1
14 |
15 | #jwt配置
16 | jwt:
17 | EXPIRATIONTIME: 1000*60*60*24*1
18 | SECRET: OHAHAHAHA
19 | HEADER_NAME: Authorization
--------------------------------------------------------------------------------
/src/main/resources/application-test.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9000
3 |
4 | spring:
5 | datasource:
6 | url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
7 | username: kingeid
8 | password: 123456
9 |
10 | #自定义配置
11 | resources:
12 | logdir: logs/
13 | midpHost: 1.1.1.1
--------------------------------------------------------------------------------
/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | datasource:
3 | # 如果存在多个数据源,监控的时候可以通过名字来区分开来
4 | name: mysql
5 | # 使用druid数据源
6 | type: com.alibaba.druid.pool.DruidDataSource
7 | # 扩展插件
8 | # 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall
9 | filters: stat
10 | # 最大连接池数量
11 | maxActive: 20
12 | # 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
13 | initialSize: 1
14 | # 获取连接时最大等待时间,单位毫秒
15 | maxWait: 60000
16 | # 最小连接池数量
17 | minIdle: 1
18 | timeBetweenEvictionRunsMillis: 60000
19 | # 连接保持空闲而不被驱逐的最长时间
20 | minEvictableIdleTimeMillis: 300000
21 | # 用来检测连接是否有效的sql,要求是一个查询语句
22 | # 如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用
23 | validationQuery: select count(1) from 'table'
24 | # 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
25 | testWhileIdle: true
26 | # 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
27 | testOnBorrow: false
28 | # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
29 | testOnReturn: false
30 | # 是否缓存preparedStatement,即PSCache
31 | poolPreparedStatements: false
32 | # 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true
33 | maxOpenPreparedStatements: -1
34 | profiles:
35 | # 默认使用开发环境
36 | active: dev
37 | mybatis:
38 | # mapper.xml包地址
39 | mapper-locations: classpath:mapper/*.xml
40 | # pojo生成包地址
41 | type-aliases-package: com.spring.demo.springbootexample.mybatis.po
42 |
43 | #pagehelper分页插件
44 | pagehelper:
45 | # 数据库的方言
46 | helperDialect: mysql
47 | # 启用合理化,如果pageNum < 1会查询第一页,如果pageNum > pages会查询最后一页
48 | reasonable: true
49 |
50 | resources:
51 | #应用名称
52 | appname: spring-boot-example
53 | #日志打印的基础扫描包
54 | basepackage: com.spring.demo.springbootexample
55 |
56 |
57 | #jwt配置
58 | jwt:
59 | expiredTime: 600000
60 | key: jwtkey
--------------------------------------------------------------------------------
/src/main/resources/generator/generator.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calebman/spring-boot-example/415b7fdff1aea67da8c3033bf5d298a0bf4f3ebb/src/main/resources/generator/generator.properties
--------------------------------------------------------------------------------
/src/main/resources/generator/generatorConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/resources/logback-spring.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | %d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n
37 |
38 |
39 |
40 |
41 |
42 |
47 | ${logdir}/${appname}.log
48 |
49 |
50 |
51 | ${logdir}/${appname}.%d{yyyy-MM-dd}.log
52 | ${maxdays}
53 | ${maxsize}
54 |
55 |
56 |
57 | UTF-8
58 | %d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/test/java/com/spring/demo/springbootexample/SpringBootExampleApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.spring.demo.springbootexample;
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 | @RunWith(SpringRunner.class)
9 | @SpringBootTest
10 | public class SpringBootExampleApplicationTests {
11 |
12 | @Test
13 | public void contextLoads() {
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------