HTTPS接口实现
下面我们来实践使用Java实现一个简单HTTPS接口示例
项目结构:
1. springbootdemo
2. ├─config 配置信息类
3. ├─controller 控制器类
4. ├─entity 实体类
5. ├─enums 枚举类
6. ├─exception 异常类
7. ├─handler 捕获类
8. ├─repository 数据访问类
9. ├─util 工具类
10. ├─SpringbootdemoApplication 项目启动类
11. ├──resources 资源文件目录
12. │ ├─application.yml 全局配置文件
13. │ ├─banner.txt 项目启动banner
14. │ ├─tomcat.keystore SSL证书
15. │ ├─logback.xml 日志配置文件
主要特点:
- Restful风格
- 统一异常处理
- SQL预处理
技术选型:
- 核心框架:Spring Boot 2.1
- 持久层框架:JPA 2.0
- 日志管理:Logback
- 数据库:MySQL 5.7
- 插件:lombok
开发环境:
- SUN JDK1.8
- Maven 3.5.4
新建Spring Boot项目
这里使用的IDE是 IntelliJIDEA2018
引包,配置pom.xml
1. <dependencies>
2. <dependency>
3. <groupId>org.springframework.boot</groupId>
4. <artifactId>spring-boot-starter-data-jpa</artifactId>
5. </dependency>
6. <dependency>
7. <groupId>org.springframework.boot</groupId>
8. <artifactId>spring-boot-starter-data-rest</artifactId>
9. </dependency>
10. <!--引入MySQL驱动包-->
11. <dependency>
12. <groupId>mysql</groupId>
13. <artifactId>mysql-connector-java</artifactId>
14. </dependency>
15. <dependency>
16. <groupId>org.projectlombok</groupId>
17. <artifactId>lombok</artifactId>
18. <optional>true</optional>
19. </dependency>
20. <dependency>
21. <groupId>org.springframework.boot</groupId>
22. <artifactId>spring-boot-starter-test</artifactId>
23. <scope>test</scope>
24. </dependency>
编写Entity
编写Person实体bean,用于ORM对象关系映射,映射数据库表结构
1. /**
2. * Person实体类
3. */
4.
5. @Entity
6. @Data
7. @Table(name = "person")
8. public class Person {
9. //主键自增长
10. @Id
11. @GeneratedValue(strategy = GenerationType.IDENTITY)
12. private Integer id;
13. private String name;
14. private Integer age;
15. }
创建一个接口PersonRepository,后续的控制器直接调用该接口继承自JpaRepository的方法,来实现和数据库交互
1. /**
2. * 继承JpaRepository,实现与数据库交互(JPA支持自动生成一些基本CURD SQL语句)
3. */
4. public interface PersonRepository extends JpaRepository<Person,Integer> {
5.
6. }
统一异常处理
自定义异常
1. /**
2. * 自定义异常类
3. */
4.
5. //RuntimeException继承Exception,spring只对继承RuntimeException的异常进行回滚
6. public class PersonException extends RuntimeException {
7. private Integer code;
8.
9. public PersonException(ResultEnum resultEnum) {
10. super(resultEnum.getMsg());
11. this.code = code;
12. }
13.
14. public Integer getCode() {
15. return code;
16. }
17.
18. public void setCode(Integer code) {
19. this.code = code;
20. }
21. }
捕获异常类
1. @ControllerAdvice
2. @Slf4j
3. public class ExceptionHandle {
4.
5. /**
6. * 捕获异常类
7. * @param e
8. * @return
9. */
10. @ExceptionHandler(value = Exception.class)
11. @ResponseBody
12. public Result handle(Exception e){
13. if (e instanceof PersonException){
14. PersonException personException = (PersonException) e;
15. return ResultUtil.error(personException.getCode(),personException.getMessage());
16. }
17. log.error("【系统错误】",e);
18. return ResultUtil.error(-1,"未知错误");
19. }
20. }
封装异常消息枚举
1. /**
2. * 封装异常消息枚举类
3. */
4.
5. public enum ResultEnum {
6. UNKONW_ERROR(-1, "未知错误"),
7. SUCCESS(0, "成功");
8.
9. private Integer code;
10. private String msg;
11.
12. ResultEnum(Integer code, String msg) {
13. this.code = code;
14. this.msg = msg;
15. }
16.
17. public Integer getCode() {
18. return code;
19. }
20.
21. public String getMsg() {
22. return msg;
23. }
24. }
封装异常对象实体
1. /**
2. * 封装异常对象(Http请求返回的最外层对象)
3. * @param <T>
4. */
5.
6. @Data
7. public class Result<T> {
8. //错误码
9. private Integer code;
10. //提示信息
11. private String msg;
12. private T date;
13. }
异常工具类
1. /**
2. * 异常工具类
3. */
4. public class ResultUtil {
5. public static Result sucess(Object obj) {
6. Result result = new Result();
7. result.setCode(0);
8. result.setMsg("sucess");
9. result.setDate(obj);
10. return result;
11. }
12.
13. public static Result sucess() {
14. Result result = new Result();
15. result.setCode(0);
16. result.setMsg("sucess");
17. return result;
18. }
19.
20. public static Result error(Integer code, String message) {
21. Result result = new Result();
22. result.setCode(code);
23. result.setMsg(message);
24. return result;
25. }
26. }
创建RESTful API
风格设计
请求类型 | 请求路径 | 功能 |
Get | /person | 获取人员列表 |
Post | /person | 创建一个人员 |
创建Controller
1. /**
2. * 控制器,处理Http/https请求(RESTful API)
3. */
4.
5. @RestController
6. public class PersonController {
7. @Autowired
8. PersonRepository personRepository;
9.
10. /**
11. * 查询所有人员列表(Get方式)
12. * @return
13. */
14.
15. @GetMapping(value = "/person")
16. private List<Person> personlist() {
17. return personRepository.findAll();
18. }
19.
20. /**
21. * 添加人员 (Post方式)
22. * @param person
23. * @return
24. */
25. @PostMapping(value = "/person")
26. public Result personAdd(HttpServletRequest request,@RequestBody Person person) {
27. return ResultUtil.sucess(personRepository.save(person));
28. }
29. }
使用SSL-HTTPS
Spring Boot中使用HTTPS步骤:
- 要有一个SSL证书,证书怎么获取呢?买(通过证书授权机构购买)或者自己生成(通过keytool生成)
- 启用HTTPS
- 将HTTP重定向到HTTPS(可选)
获取SSL证书
有两种方式可以获取到SSL证书:
- 自己通过keytool生成;
- 通过证书授权机构购买;
这里作为演示,采用keytool生成,实际项目中大部分采用的都是购买的方式。 那么怎么使用keytool生成呢? Keytool是Java提供的证书生成工具,如果配置了JAVA_HOME的,直接就可以在控制台进行生成了,这里演示使用的是Mac的终端窗口
1. 192:~ apple$ keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.keystore
2. 输入密钥库口令:
3. 再次输入新口令:
4. 您的名字与姓氏是什么?
5. [Unknown]: zuozewei
6. 您的组织单位名称是什么?
7. [Unknown]: 7DGroup
8. 您的组织名称是什么?
9. [Unknown]: 7D
10. 您所在的城市或区域名称是什么?
11. [Unknown]: Beijing
12. 您所在的省/市/自治区名称是什么?
13. [Unknown]: Beijing
14. 该单位的双字母国家/地区代码是什么?
15. [Unknown]: CN
16. CN=zuozewei, OU=7DGroup, O=7D, L=Beijing, ST=Beijing, C=CN是否正确?
17. [否]: y
18.
19. 输入 <tomcat> 的密钥口令
20. (如果和密钥库口令相同, 按回车):
21. 再次输入新口令:
查看生成的SSL证书信息
1. apple$ keytool -list -keystore tomcat.keystore
2. 输入密钥库口令:
3. 密钥库类型: jks
4. 密钥库提供方: SUN
5.
6. 您的密钥库包含 1 个条目
7.
8. tomcat, 2018-11-29, PrivateKeyEntry,
9. 证书指纹 (SHA1): 2B:C5:FB:77:2C:5E:DC:5B:C5:E9:9F:06:27:7F:2E:A4:E4:9E:DF:8C
这里解释下命令的各个参数的含义:
- genkey :生成key;
- alias :key的别名;
- dname:指定证书拥有者信息
- storetype :密钥库的类型为JCEKS。常用的有JKS(默认),JCEKS(推荐),PKCS12,BKS,UBER。每个密钥库只可以是其中一种类型。
- keyalg :DSA或RSA算法(当使用-genkeypair参数),DES或DESede或AES算法(当使用-genseckey参数);
- keysize :密钥的长度为512至1024之间(64的倍数)
- keystore :证书库的名称
- validity : 指定创建的证书有效期多少天
dname的值详解:
- CN(Common Name名字与姓氏)
- OU(Organization Unit组织单位名称)
- O(Organization组织名称)
- L(Locality城市或区域名称)
- ST(State州或省份名称)
- C(Country国家名称)
这时候在当前目录下就会看到一个文件 tomcat.keystore
,到这里SSL证书就有了。
启用HTTPS
默认情况下Spring Boot内嵌的Tomcat服务器会在8080端口启动HTTP服务,Spring Boot允许在全局配置文件中配置HTTP或HTTPS,但是不可同时配置,如果两个都启动,至少有一个要以编程的方式配置,Spring Boot官方文档建议在application配置文件中配置HTTPS,因为HTTPS比HTTP更复杂一些
在 application.yml
中配置HTTPS,配置信息如下:
1. server:
2. port: 443
3. servlet:
4. context-path: /springboot
5. ssl:
6. key-store: classpath:tomcat.keystore
7. key-store-type: jks
8. key-alias: tomcat
9. key-store-password: zuozewei
10. key-store-provider: SUN
11. key-password: zuozewei
12. enabled: true
注意:请将在上一步生成的证书放到 src/main/resources
目录下。
将HTTP请求重定向到HTTPS
由于不能同时在application.l中同时配置两个connector,所以要以编程的方式配置HTTP Connector,然后重定向到HTTPS Connector
编写TomcatHttp配置类
1. @Configuration
2. public class TomcatHttpConfig {
3.
4. /**
5. * 配置内置的Servlet容器工厂为Tomcat
6. * @return
7. */
8. @Bean
9. public ServletWebServerFactory servletContainer() {
10. TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
11. @Override
12. protected void postProcessContext(Context context) {
13. SecurityConstraint securityConstraint = new SecurityConstraint();
14. securityConstraint.setUserConstraint("CONFIDENTIAL");
15. SecurityCollection collection = new SecurityCollection();
16. collection.addPattern("/*");
17. securityConstraint.addCollection(collection);
18. context.addConstraint(securityConstraint);
19. }
20. };
21. //添加配置信息,主要是Http的配置信息
22. tomcat.addAdditionalTomcatConnectors(redirectConnector());
23. return tomcat;
24. }
25.
26. /**
27. * 配置一个Http连接信息
28. * @return
29. */
30. private Connector redirectConnector() {
31. Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
32. connector.setScheme("http");
33. connector.setPort(8088);
34. connector.setSecure(false);
35. connector.setRedirectPort(443);
36. return connector;
37. }
38. }
自定义启动标志
只需要在 src/main/resources
路径下新建一个banner.txt文件,banner.txt中填写好需要打印的字符串内容即可。 一般情况下,我们会借助第三方工具帮忙转化内容,如:
网站:http://www.network-science.de/ascii/ 将文字转化成字符串,
网站:http://www.degraeve.com/img2txt.php 可以将图片转化成字符串。
配置日志配置文件
只需要在 src/main/resources
路径下新建一个 logback.xml
配置文件
1. <?xml version="1.0" encoding="UTF-8"?>
2. <configuration>
3. <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
4. <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n"/>
5. <property name="LOG_PATH" value="${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}"/>
6. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
7. <file>${LOG_PATH}/${LOG_FILE}</file>
8. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
9. <fileNamePattern>${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern>
10. </rollingPolicy>
11. <encoder charset="UTF-8">
12. <pattern>${FILE_LOG_PATTERN}</pattern>
13. </encoder>
14. </appender>
15. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
16. <encoder>
17. <pattern>${FILE_LOG_PATTERN}</pattern>
18. </encoder>
19. </appender>
20. <appender name="CRAWLER_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
21. <file>${LOG_PATH}/event.log</file>
22. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
23. <fileNamePattern>${LOG_PATH}/event.%d{yyyy-MM-dd}.log</fileNamePattern>
24. <maxHistory>30</maxHistory>
25. </rollingPolicy>
26. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
27. <pattern>%msg%n</pattern>
28. </encoder>
29. </appender>
30. <logger name="com.business.intelligence.util.CrawlerLogger" level="INFO" additivity="false">
31. <appender-ref ref="CRAWLER_LOG"/>
32. </logger>
33. <root level="INFO">
34. <appender-ref ref="STDOUT"/>
35. <appender-ref ref="FILE"/>
36. </root>
37. </configuration>
配置数据库配置
手动先创建 db_person
数据库
1. spring:
2. profiles:
3. active: a
4. datasource:
5. driver-class-name: com.mysql.jdbc.Driver
6. #手动创建db_person数据库
7. url: jdbc:mysql://39.105.21.2:3306/db_person?useUnicode=true&characterEncoding=utf-8&useSSL=false
8. username: zuozewei
9. password: zuozewei
10. jpa:
11. hibernate:
12. ddl-auto: update
13. show-sql: true
启动并测试
启动项目
通过浏览器输入:http://127.0.0.1:8088/springboot/person
我们可以看到浏览器自动重定向到 https://127.0.0.1/springboot/person
点击浏览器上方的证书,我们可以看到使用的SSL证书信息