秒懂HTTPS介面(實現篇)
文章目錄
HTTPS介面實現
在上文秒懂HTTPS介面(原理篇)中我們詳細介紹了HTTPS協議原理,下面我們來實踐使用Java實現一個簡單HTTPS介面示例
專案結構:
springbootdemo ├─config 配置資訊類 ├─controller 控制器類 ├─entity 實體類 ├─enums 列舉類 ├─exception 異常類 ├─handler 捕獲類 ├─repository 資料訪問類 ├─util 工具類 ├─SpringbootdemoApplication 專案啟動類 ├──resources 資原始檔目錄 │ ├─application.yml 全域性配置檔案 │ ├─banner.txt 專案啟動banner │ ├─tomcat.keystore SSL證書 │ ├─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是IntelliJ IDEA 2018
引包,配置pom.xml
<dependencies>
<dependency>
< groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<!--引入MySQL驅動包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
編寫Entity
編寫Person實體bean,用於ORM物件關係對映,對映資料庫表結構
/**
* Person實體類
*/
@Entity
@Data
@Table(name = "person")
public class Person {
//主鍵自增長
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer age;
}
建立一個介面PersonRepository,後續的控制器直接呼叫該介面繼承自JpaRepository的方法,來實現和資料庫互動
/**
* 繼承JpaRepository,實現與資料庫互動(JPA支援自動生成一些基本CURD SQL語句)
*/
public interface PersonRepository extends JpaRepository<Person,Integer> {
}
統一異常處理
自定義異常
/**
* 自定義異常類
*/
//RuntimeException繼承Exception,spring只對繼承RuntimeException的異常進行回滾
public class PersonException extends RuntimeException {
private Integer code;
public PersonException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
捕獲異常類
@ControllerAdvice
@Slf4j
public class ExceptionHandle {
/**
* 捕獲異常類
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result handle(Exception e){
if (e instanceof PersonException){
PersonException personException = (PersonException) e;
return ResultUtil.error(personException.getCode(),personException.getMessage());
}
log.error("【系統錯誤】",e);
return ResultUtil.error(-1,"未知錯誤");
}
}
封裝異常訊息列舉
/**
* 封裝異常訊息列舉類
*/
public enum ResultEnum {
UNKONW_ERROR(-1, "未知錯誤"),
SUCCESS(0, "成功");
private Integer code;
private String msg;
ResultEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
封裝異常物件實體
/**
* 封裝異常物件(Http請求返回的最外層物件)
* @param <T>
*/
@Data
public class Result<T> {
//錯誤碼
private Integer code;
//提示資訊
private String msg;
private T date;
}
異常工具類
/**
* 異常工具類
*/
public class ResultUtil {
public static Result sucess(Object obj) {
Result result = new Result();
result.setCode(0);
result.setMsg("sucess");
result.setDate(obj);
return result;
}
public static Result sucess() {
Result result = new Result();
result.setCode(0);
result.setMsg("sucess");
return result;
}
public static Result error(Integer code, String message) {
Result result = new Result();
result.setCode(code);
result.setMsg(message);
return result;
}
}
建立RESTful API
風格設計
請求型別 | 請求路徑 | 功能 |
---|---|---|
Get | /person | 獲取人員列表 |
Post | /person | 建立一個人員 |
建立Controller
/**
* 控制器,處理Http/https請求(RESTful API)
*/
@RestController
public class PersonController {
@Autowired
PersonRepository personRepository;
/**
* 查詢所有人員列表(Get方式)
* @return
*/
@GetMapping(value = "/person")
private List<Person> personlist() {
return personRepository.findAll();
}
/**
* 新增人員 (Post方式)
* @param person
* @return
*/
@PostMapping(value = "/person")
public Result personAdd(HttpServletRequest request,@RequestBody Person person) {
return ResultUtil.sucess(personRepository.save(person));
}
}
使用SSL-HTTPS
Spring Boot中使用HTTPS步驟:
- 要有一個SSL證書,證書怎麼獲取呢?買(通過證書授權機構購買)或者自己生成(通過keytool生成)
- 啟用HTTPS
- 將HTTP重定向到HTTPS(可選)
獲取SSL證書
有兩種方式可以獲取到SSL證書:
- 自己通過keytool生成;
- 通過證書授權機構購買;
這裡作為演示,採用keytool生成,實際專案中大部分採用的都是購買的方式。
那麼怎麼使用keytool生成呢?
Keytool是Java提供的證書生成工具,如果配置了JAVA_HOME的,直接就可以在控制檯進行生成了,這裡演示使用的是Mac的終端視窗
192:~ apple$ keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.keystore
輸入金鑰庫口令:
再次輸入新口令:
您的名字與姓氏是什麼?
[Unknown]: zuozewei
您的組織單位名稱是什麼?
[Unknown]: 7DGroup
您的組織名稱是什麼?
[Unknown]: 7D
您所在的城市或區域名稱是什麼?
[Unknown]: Beijing
您所在的省/市/自治區名稱是什麼?
[Unknown]: Beijing
該單位的雙字母國家/地區程式碼是什麼?
[Unknown]: CN
CN=zuozewei, OU=7DGroup, O=7D, L=Beijing, ST=Beijing, C=CN是否正確?
[否]: y
輸入 <tomcat> 的金鑰口令
(如果和金鑰庫口令相同, 按回車):
再次輸入新口令:
檢視生成的SSL證書資訊
apple$ keytool -list -keystore tomcat.keystore
輸入金鑰庫口令:
金鑰庫型別: jks
金鑰庫提供方: SUN
您的金鑰庫包含 1 個條目
tomcat, 2018-11-29, PrivateKeyEntry,
證書指紋 (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,配置資訊如下:
server:
port: 443
servlet:
context-path: /springboot
ssl:
key-store: classpath:tomcat.keystore
key-store-type: jks
key-alias: tomcat
key-store-password: zuozewei
key-store-provider: SUN
key-password: zuozewei
enabled: true
注意:請將在上一步生成的證書放到src/main/resources
目錄下。
將HTTP請求重定向到HTTPS
由於不能同時在application.l中同時配置兩個connector,所以要以程式設計的方式配置HTTP Connector,然後重定向到HTTPS Connector
編寫TomcatHttp配置類
@Configuration
public class TomcatHttpConfig {
/**
* 配置內建的Servlet容器工廠為Tomcat
* @return
*/
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
//新增配置資訊,主要是Http的配置資訊
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
/**
* 配置一個Http連線資訊
* @return
*/
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8088);
connector.setSecure(false);
connector.setRedirectPort(443);
return connector;
}
}
自定義啟動標誌
只需要在src/main/resources
路徑下新建一個banner.txt檔案,banner.txt中填寫好需要列印的字串內容即可。
一般情況下,我們會藉助第三方工具幫忙轉化內容,如:
網站:http://www.network-science.de/ascii/ 將文字轉化成字串,
網站:http://www.degraeve.com/img2txt.php 可以將圖片轉化成字串。
配置日誌配置檔案
只需要在src/main/resources
路徑下新建一個logback.xml
配置檔案
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n"/>
<property name="LOG_PATH" value="${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}</fileNamePattern>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="CRAWLER_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/event.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/event.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%msg%n</pattern>
</encoder>
</appender>
<logger name="com.business.intelligence.util.CrawlerLogger" level="INFO" additivity="false">
<appender-ref ref="CRAWLER_LOG"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
配置資料庫配置
手動先建立db_person
資料庫
spring:
profiles:
active: a
datasource:
driver-class-name: com.mysql.jdbc.Driver
#手動建立db_person資料庫
url: jdbc:mysql://39.105.21.2:3306/db_person?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: zuozewei
password: zuozewei
jpa:
hibernate:
ddl-auto: update
show-sql: true
啟動並測試
啟動專案
通過瀏覽器輸入:http://127.0.0.1:8088/springboot/person
我們可以看到瀏覽器自動重定向到 https://127.0.0.1/springboot/person
點選瀏覽器上方的證書,我們可以看到使用的SSL證書資訊
完整的專案結構
秒懂HTTPS介面系列原始碼:
https://github.com/zuozewei/Springboot-https-demo