# mimic-web-service **Repository Path**: zack5761/mimic-web-service ## Basic Information - **Project Name**: mimic-web-service - **Description**: 科研助手后端服务,让我们助力科研!! - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2023-08-30 - **Last Updated**: 2024-03-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, SpringBoot, Postgresql, JWT, Security ## README # mimic-web-service ### Purpose 我们致力于为每个用户提供优质的数据服务,提供科学研究领域的支持和辅助功能。 通过为每个用户建立一个数据库并将用于身份验证和授权的所有用户信息(用户名、密码、客户端 ID 等)存储在相应用户数据库的用户表中来实现多用户。 使用Spring Security 保护用户数据安全和Web程序。使用 Hibernate 连接数据库。所有用户信息需要存储在用户数据库中,将允许每个用户完全数据隔离。 ### Goal * 存档应用程序 SaaS 模型客户端明智的不同数据库。 * 重点关注 Spring Security 和 JWT * 您可以使用单个数据库连接多个模式,例如 MySQL — testdb、testdb2。 * 您可以连接多个数据库,例如 MySQL、PostgreSQL 或 Oracle。 ### What Is Multi-Tenancy? Multi-tenancy 一种架构,其中软件应用程序的单个实例为多个客户提供服务。 每个客户称为Tenancy。 Tenancy可以被赋予定制应用程序的某些部分的能力。 Multi-tenancy应用程序是指Tenancy(即公司中的用户)感觉该应用程序是为他们创建和部署的。 事实上,有很多这样的Tenancy,他们也使用相同的应用程序,但感觉它是专为他们构建的。 动态多租户高级图: ![Dynamic Multi-Tenant High-Level Diagram](img/multi.png "Dynamic Multi-Tenant High-Level Diagram") 过程: * 客户端请求登录系统。 * 系统使用客户端 ID 与主数据库进行检查。 * 如果成功,根据驱动类名将当前数据库设置为上下文。 * 如果失败,用户会收到消息“未经授权”。 * 身份验证成功后,用户将获得 JWT 以供下次执行。 整个过程按照以下工作流程执行: ![The whole process executes in the following workflow](img/sequence.png "The whole process executes in the following workflow") 技术及项目结构: * Java 11. * Spring Boot. * Spring Security. * Spring AOP. * Spring Data JPA. * Hibernate. * JWT. * MySQ & PostgreSQL. * IntelliJ IDEA Ultimate (2020.2.3). ### MySQL Database 现在,创建主数据库和用户数据库。 主数据库: 在master数据库中,我们只有一张表(tbl_tenant_master),所有用户信息都存储在该表中。 MySQL ```sql DROP TABLE IF EXISTS `tbl_tenant_master`; CREATE TABLE `tbl_tenant_master` ( `tenant_client_id` int(10) unsigned NOT NULL, `db_name` varchar(50) NOT NULL, `url` varchar(250) NOT NULL, `user_name` varchar(50) NOT NULL, `password` varchar(100) NOT NULL, `driver_class` varchar(100) NOT NULL, `status` varchar(10) NOT NULL, PRIMARY KEY (`tenant_client_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; INSERT INTO `tbl_tenant_master` (`tenant_client_id`, `db_name`, `url`, `user_name`, `password`, `driver_class`, `status`) VALUES ('100', 'tenant_db', 'jdbc:mysql://localhost:3306/tenant_db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Jakarta&useSSL=false', 'root', 'root', 'com.mysql.cj.jdbc.Driver', 'ACTIVE'), ('200', 'tenant_db_pgs', 'jdbc:postgresql://localhost:5432/tenant_db_pgs', 'hendisantika', 'root', 'org.postgresql.Driver', 'ACTIVE'), ('300', 'tenant_db2', 'jdbc:mysql://localhost:3306/tenant_db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Jakarta&useSSL=false', 'root', 'root', 'com.mysql.cj.jdbc.Driver', 'ACTIVE'); ``` MySQL中的租户数据库(1): 创建用于客户端登录认证的表(tbl_user)。 创建另一个表 (tbl_product) 以使用 JWT 检索数据(用于授权检查)。 MySQL ```sql DROP TABLE IF EXISTS `tbl_product`; CREATE TABLE `tbl_product` ( `product_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `product_name` varchar(50) NOT NULL, `quantity` int(10) unsigned NOT NULL DEFAULT 0, `size` varchar(3) NOT NULL, PRIMARY KEY (`product_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; DROP TABLE IF EXISTS `tbl_user`; CREATE TABLE `tbl_user` ( `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `full_name` varchar(100) NOT NULL, `gender` varchar(10) NOT NULL, `user_name` varchar(50) NOT NULL, `password` varchar(100) NOT NULL, `status` varchar(10) NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; INSERT INTO `tbl_product` (`product_id`, `product_name`, `quantity`, `size`) VALUES ('1', 'Apple MacOS', '5', 'M'); INSERT INTO `tbl_user` (`user_id`, `full_name`, `gender`, `user_name`, `password`, `status`) VALUES ('1', 'Uzumaki Naruto', 'Male', 'naruto', '$2y$12$/WhepH7JVYUCl4ujy6FFguiCi/x2q4dwXISD.WJTXYIN2QAhv6Zky', 'ACTIVE'); -- password=naruto ``` PostgreSQL 中的租户数据库(2): 创建用于客户端登录认证的表(tbl_user)。 创建另一个表 (tbl_product) 以使用 JWT 检索数据(用于授权检查)。 PostgreSQL ```sql DROP TABLE IF EXISTS "public"."tbl_product"; -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. -- Table Definition CREATE TABLE "public"."tbl_product" ( "product_id" int4 NOT NULL, "product_name" varchar(50) NOT NULL, "quantity" int4 NOT NULL DEFAULT 0, "size" varchar(3) NOT NULL, PRIMARY KEY ("product_id") ); DROP TABLE IF EXISTS "public"."tbl_user"; -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. -- Table Definition CREATE TABLE "public"."tbl_user" ( "user_id" int4 NOT NULL, "full_name" varchar(100) NOT NULL, "gender" varchar(10) NOT NULL, "user_name" varchar(50) NOT NULL, "password" varchar(100) NOT NULL, "status" varchar(10) NOT NULL, PRIMARY KEY ("user_id") ); INSERT INTO "public"."tbl_product" ("product_id", "product_name", "quantity", "size") VALUES ('1', 'Apple MacOS', '5', 'M'); INSERT INTO "public"."tbl_user" ("user_id", "full_name", "gender", "user_name", "password", "status") VALUES ('1', 'Uzumaki Naruto', 'Male', 'naruto', '$2y$12$/WhepH7JVYUCl4ujy6FFguiCi/x2q4dwXISD.WJTXYIN2QAhv6Zky', 'ACTIVE'); ``` 数据库创建和表创建完成! ### 配置用户数据库 Hibernate 中的多用户。 Hibernate 中的多用户有以下三种方法: * 单独的架构 - 同一物理数据库实例中的每个用户一个架构。 * 单独的数据库——每个用户一个单独的物理数据库实例。 * 分区(鉴别器)数据 — 每个用户的数据按鉴别器值进行分区。 ### 数据库检查确认 主数据库: tbl_tenant_master ```sql MariaDB [master_db]> select * from tbl_tenant_master; +------------------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------+--------------------------+--------+ | tenant_client_id | db_name | url | user_name | password | driver_class | status | +------------------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------+--------------------------+--------+ | 100 | tenant_db | jdbc:mysql://localhost:3306/tenant_db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Jakarta&useSSL=false | root | root | com.mysql.cj.jdbc.Driver | ACTIVE | | 200 | tenant_db_pgs | jdbc:postgresql://localhost:5432/tenant_db_pgs | hendisantika | root | org.Postgresql.Driver | ACTIVE | | 300 | tenant_db2 | jdbc:mysql://localhost:3306/tenant_db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Jakarta&useSSL=false | root | root | com.mysql.cj.jdbc.Driver | ACTIVE | +------------------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------+----------+--------------------------+--------+ 3 rows in set (0.010 sec) ``` MySQL用户数据库: tbl_user tbl_product ```sql MariaDB [tenant_db]> select * from tbl_user; +---------+----------------+--------+-----------+--------------------------------------------------------------+--------+ | user_id | full_name | gender | user_name | password | status | +---------+----------------+--------+-----------+--------------------------------------------------------------+--------+ | 1 | Uzumaki Naruto | Male | naruto | $2y$12$/WhepH7JVYUCl4ujy6FFguiCi/x2q4dwXISD.WJTXYIN2QAhv6Zky | ACTIVE | +---------+----------------+--------+-----------+--------------------------------------------------------------+--------+ 1 row in set (0.002 sec) MariaDB [tenant_db]> select * from tbl_product; +------------+--------------+----------+------+ | product_id | product_name | quantity | size | +------------+--------------+----------+------+ | 1 | Apple MacOS | 5 | M | +------------+--------------+----------+------+ 1 row in set (0.000 sec) ``` ### PostgreSQL 用户数据库 tbl_user tbl_product ```sql tenant_db_pgs=# select * from tbl_user; user_id | full_name | gender | user_name | password | status ---------+----------------+--------+-----------+--------------------------------------------------------------+-------- 1 | Uzumaki Naruto | Male | naruto | $2y$12$/WhepH7JVYUCl4ujy6FFguiCi/x2q4dwXISD.WJTXYIN2QAhv6Zky | ACTIVE (1 row) tenant_db_pgs=# select * from tbl_product; product_id | product_name | quantity | size ------------+--------------+----------+------ 1 | Apple MacOS | 5 | M (1 row) ``` 现在,使用 Postman 测试一切是否按我们预期运行: 目标MySQL: MySQL 中的用户登录 ![User Login in MySQL](img/login-mysql.png "User Login in MySQL") 获取MySQL中的产品列表 ![Get Product List in MySQL](img/products-mysql.png "Get Product List in MySQL") 目标 PostgreSQL: PostgreSQL 中的用户登录 ![User Login in PostgreSQL](img/login-psql.png "User Login in PostgreSQL") 获取 PostgreSQL 中的产品列表 ![Get Product List in PostgreSQL](img/products-psql.png "Get Product List in PostgreSQL") NOTE: 用户密码加密使用Bcrypt 加密: 1. https://bcrypt-generator.com/