秒懂HTTPS接口(实现篇)

举报
zuozewei 发表于 2021/07/22 22:02:59 2021/07/22
【摘要】 我们来实践使用 Java 实现一个简单 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步骤:

  1. 要有一个SSL证书,证书怎么获取呢?买(通过证书授权机构购买)或者自己生成(通过keytool生成)
  2. 启用HTTPS
  3. 将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接口系列源码:

相关系列:

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。