diff --git a/dev-protocol-springcloud/SpringCloud项目介绍.md b/dev-protocol-springcloud/SpringCloud项目介绍.md
index 8f585e7..486c1de 100644
--- a/dev-protocol-springcloud/SpringCloud项目介绍.md
+++ b/dev-protocol-springcloud/SpringCloud项目介绍.md
@@ -1,4 +1,4 @@
-# 项目大致说明
+# 项目大致说明 - SpringCloud 组件
- 微服务注册与配置中心 - (Alibaba Nacos)
- [dev-protocol-springcloud-nacos](dev-protocol-springcloud-nacos)
- 微服务应用监控 - (SpringBoot Admin)
@@ -8,7 +8,7 @@
- 微服务网关 - (Gateway)
- [dev-protocol-springcloud-gateway](dev-protocol-springcloud-gateway)
- 分布式链路、日志追踪 - (Sleuth + Zipkin)
- - todo
+ - [dev-protocol-springcloud-sleuth-zipkin](dev-protocol-springcloud-sleuth-zipkin)
- 微服务容错 - (SpringCloud Netflix Hystrix)
- todo
- 消息驱动微服务 - (SpringCloud Stream)
@@ -18,4 +18,17 @@
- 网关动态限流 - (SpringCloud Alibaba Sentinel)
- todo
- 微服务工程部署与整体可用性验证
- - todo
\ No newline at end of file
+ - todo
+
+
+## 通用工程模块
+- 授权、鉴权中心微服务
+ - [dev-protocol-springcloud-project-authority-center](dev-protocol-springcloud-project-authority-center)
+
+- 用户账户微服务
+
+- 商品微服务
+
+- 订单微服务
+
+- 物流微服务
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/pic/授权鉴权中心微服务功能逻辑架构.png b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/pic/授权鉴权中心微服务功能逻辑架构.png
new file mode 100644
index 0000000..d3f1ab1
Binary files /dev/null and b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/pic/授权鉴权中心微服务功能逻辑架构.png differ
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/pom.xml
new file mode 100644
index 0000000..f8c7596
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/pom.xml
@@ -0,0 +1,98 @@
+
+
+ 4.0.0
+
+ org.example
+ dev-protocol
+ 1.0-SNAPSHOT
+ ../../pom.xml
+
+
+ dev-protocol-springcloud-project-authority-center
+ 1.0-SNAPSHOT
+ jar
+
+
+ dev-protocol-springcloud-project-authority-center
+ 授权中心
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.12
+ runtime
+
+
+
+ org.example
+ dev-protocol-springcloud-project-mvc-config
+ 1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+
+
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+
+ org.freemarker
+ freemarker
+ 2.3.30
+
+
+ cn.smallbun.screw
+ screw-core
+ 1.0.3
+
+
+
+
+
+ ${artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/AuthorityCenterApplication.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/AuthorityCenterApplication.java
new file mode 100644
index 0000000..99faaed
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/AuthorityCenterApplication.java
@@ -0,0 +1,19 @@
+package org.example;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+
+/**
+ *
授权中心启动入口
+ * */
+@EnableJpaAuditing // 允许 Jpa 自动审计
+@EnableDiscoveryClient
+@SpringBootApplication
+public class AuthorityCenterApplication {
+ public static void main(String[] args) {
+
+ SpringApplication.run(AuthorityCenterApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/constant/AuthorityConstant.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/constant/AuthorityConstant.java
new file mode 100644
index 0000000..ea0006d
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/constant/AuthorityConstant.java
@@ -0,0 +1,13 @@
+package org.example.constant;
+
+/**
+ * 授权需要使用的一些常量信息
+ * */
+public final class AuthorityConstant {
+
+ /** RSA 私钥, 除了授权中心以外, 不暴露给任何客户端 */
+ public static final String PRIVATE_KEY = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKzKEUVNF+VVgyar2SAcm9xGDCL4yjMLEXXsy0BNM6rZTh2metCIqA1vbKvFvuruTLcTHYMtO0+urup2ScRhiZtkHMI9Vy/MGbiG0o5T2YrDUYZD4jPPRgvZ2L95uI+nFkEm1WZ7nuO37HvIE3+WDVvJnI84omFsLwN3bLCnStw5AgMBAAECgYACCZePWdAVL+HQlProXIJO1XXFwPN2MtCnzB0cIkk5Kc6zjxLIZf0M2dCGrGONG8BJVEj4Zn6BkXlEqTv+LXVCZfOLNJuXTOEdBTWQj1EFk2wuXIqrcZgFqT56ChSscMQTiEe/O7ydyQ2qD/ZbNDOcJMf6nto883ZDLtVtOTdzxQJBANwaMS8O0/X0/gkdzrY3dKjOJFmXOIybdURAR6Mum/PGkX6j9xAUO1clErKTEY1jkuqohLmnXw+pKTQTW/Gt290CQQDI+HFel+S64xZ9SGyISK+gXl1gK1mpMT2YaQjmzwotNljn7U3g0nChbltNANYsRcE5X8/kVoX7AihO+8RZp1oNAkEAgS66SVVZoJVfeHhPN/GKff0nppGz9grUI+/aW/NiQwz7nimcO4q0XWx78eWRuruDokiwRcrvZ2Cwt0jZgRq63QJBAIVli3LbZcK7K1lbclb/0Dulh1tnSutoONdqmLMDqGCcW2UO+guKA6LTqpyxOnhGkNwxgb+xwtr68qCCszFDSR0CQQDC52dm0bei9PCi0pebOhtQVYdPx+zfZE4p+aRCV7pYjm8HgMMJslKX8sgLEOg91gO/925QRb0uN5H5oDjOHFVh";
+
+ /** 默认的 Token 超时时间, 一天 */
+ public static final Integer DEFAULT_EXPIRE_DAY = 1;
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/controller/AuthorityController.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/controller/AuthorityController.java
new file mode 100644
index 0000000..5a3ca42
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/controller/AuthorityController.java
@@ -0,0 +1,59 @@
+package org.example.controller;
+
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.example.annotation.IgnoreResponseAdvice;
+import org.example.service.IJWTService;
+import org.example.vo.JwtToken;
+import org.example.vo.UsernameAndPassword;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 对外暴露的授权服务接口
+ * */
+@Slf4j
+@RestController
+@RequestMapping("/authority")
+public class AuthorityController {
+
+ private final IJWTService ijwtService;
+
+ public AuthorityController(IJWTService ijwtService) {
+ this.ijwtService = ijwtService;
+ }
+
+ /**
+ * 从授权中心获取 Token (其实就是登录功能), 且返回信息中没有统一响应的包装
+ * */
+ @IgnoreResponseAdvice
+ @PostMapping("/token")
+ public JwtToken token(@RequestBody UsernameAndPassword usernameAndPassword)
+ throws Exception {
+
+ log.info("request to get token with param: [{}]",
+ JSON.toJSONString(usernameAndPassword));
+ return new JwtToken(ijwtService.generateToken(
+ usernameAndPassword.getUsername(),
+ usernameAndPassword.getPassword()
+ ));
+ }
+
+ /**
+ * 注册用户并返回当前注册用户的 Token, 即通过授权中心创建用户
+ * */
+ @IgnoreResponseAdvice
+ @PostMapping("/register")
+ public JwtToken register(@RequestBody UsernameAndPassword usernameAndPassword)
+ throws Exception {
+
+ log.info("register user with param: [{}]", JSON.toJSONString(
+ usernameAndPassword
+ ));
+ return new JwtToken(ijwtService.registerUserAndGenerateToken(
+ usernameAndPassword
+ ));
+ }
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/dao/UserDao.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/dao/UserDao.java
new file mode 100644
index 0000000..ec60e18
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/dao/UserDao.java
@@ -0,0 +1,22 @@
+package org.example.dao;
+
+import org.example.entity.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * User Dao 接口定义
+ * */
+public interface UserDao extends JpaRepository {
+
+ /**
+ * 根据用户名查询 User 对象
+ * select * from t_dev_protocol_cloud_user where username = ?
+ * */
+ User findByUsername(String username);
+
+ /**
+ * 根据用户名和密码查询实体对象
+ * select * from t_dev_protocol_cloud_user where username = ? and password = ?
+ * */
+ User findByUsernameAndPassword(String username, String password);
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/entity/User.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/entity/User.java
new file mode 100644
index 0000000..c376f76
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/entity/User.java
@@ -0,0 +1,58 @@
+package org.example.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityListeners;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 用户表实体类定义
+ * */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Entity
+@EntityListeners(AuditingEntityListener.class)
+@Table(name = "t_dev_protocol_cloud_user")
+public class User implements Serializable {
+
+ /** 自增主键 */
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id", nullable = false)
+ private Long id;
+
+ /** 用户名 */
+ @Column(name = "username", nullable = false)
+ private String username;
+
+ /** MD5 密码 */
+ @Column(name = "password", nullable = false)
+ private String password;
+
+ /** 额外的信息, json 字符串存储 */
+ @Column(name = "extra_info", nullable = false)
+ private String extraInfo;
+
+ /** 创建时间 */
+ @CreatedDate
+ @Column(name = "create_time", nullable = false)
+ private Date createTime;
+
+ /** 更新时间 */
+ @LastModifiedDate
+ @Column(name = "update_time", nullable = false)
+ private Date updateTime;
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/service/IJWTService.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/service/IJWTService.java
new file mode 100644
index 0000000..e3f41fe
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/service/IJWTService.java
@@ -0,0 +1,26 @@
+package org.example.service;
+
+
+import org.example.vo.UsernameAndPassword;
+
+/**
+ * JWT 相关服务接口定义
+ * */
+public interface IJWTService {
+
+ /**
+ * 生成 JWT Token, 使用默认的超时时间
+ * */
+ String generateToken(String username, String password) throws Exception;
+
+ /**
+ * 生成指定超时时间的 Token, 单位是天
+ * */
+ String generateToken(String username, String password, int expire) throws Exception;
+
+ /**
+ * 注册用户并生成 Token 返回
+ * */
+ String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword)
+ throws Exception;
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/service/impl/JWTServiceImpl.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/service/impl/JWTServiceImpl.java
new file mode 100644
index 0000000..059ce1e
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/java/org/example/service/impl/JWTServiceImpl.java
@@ -0,0 +1,123 @@
+package org.example.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+import lombok.extern.slf4j.Slf4j;
+import org.example.constant.AuthorityConstant;
+import org.example.constant.CommonConstant;
+import org.example.dao.UserDao;
+import org.example.entity.User;
+import org.example.service.IJWTService;
+import org.example.vo.LoginUserInfo;
+import org.example.vo.UsernameAndPassword;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import sun.misc.BASE64Decoder;
+
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * JWT 相关服务接口实现
+ * */
+@Slf4j
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class JWTServiceImpl implements IJWTService {
+
+ private final UserDao ecommerceUserDao;
+
+ public JWTServiceImpl(UserDao ecommerceUserDao) {
+ this.ecommerceUserDao = ecommerceUserDao;
+ }
+
+ @Override
+ public String generateToken(String username, String password) throws Exception {
+
+ return generateToken(username, password, 0);
+ }
+
+ @Override
+ public String generateToken(String username, String password, int expire)
+ throws Exception {
+
+ // 首先需要验证用户是否能够通过授权校验, 即输入的用户名和密码能否匹配数据表记录
+ User ecommerceUser = ecommerceUserDao.findByUsernameAndPassword(
+ username, password
+ );
+ if (null == ecommerceUser) {
+ log.error("can not find user: [{}], [{}]", username, password);
+ return null;
+ }
+
+ // Token 中塞入对象, 即 JWT 中存储的信息, 后端拿到这些信息就可以知道是哪个用户在操作
+ LoginUserInfo loginUserInfo = new LoginUserInfo(
+ ecommerceUser.getId(), ecommerceUser.getUsername()
+ );
+
+ if (expire <= 0) {
+ expire = AuthorityConstant.DEFAULT_EXPIRE_DAY;
+ }
+
+ // 计算超时时间
+ ZonedDateTime zdt = LocalDate.now().plus(expire, ChronoUnit.DAYS)
+ .atStartOfDay(ZoneId.systemDefault());
+ Date expireDate = Date.from(zdt.toInstant());
+
+ return Jwts.builder()
+ // jwt payload --> KV
+ .claim(CommonConstant.JWT_USER_INFO_KEY, JSON.toJSONString(loginUserInfo))
+ // jwt id
+ .setId(UUID.randomUUID().toString())
+ // jwt 过期时间
+ .setExpiration(expireDate)
+ // jwt 签名 --> 加密
+// .signWith(getPrivateKey(), SignatureAlgorithm.RS256)
+ .compact();
+ }
+
+ @Override
+ public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword)
+ throws Exception {
+
+ // 先去校验用户名是否存在, 如果存在, 不能重复注册
+ User oldUser = ecommerceUserDao.findByUsername(
+ usernameAndPassword.getUsername());
+ if (null != oldUser) {
+ log.error("username is registered: [{}]", oldUser.getUsername());
+ return null;
+ }
+
+ User ecommerceUser = new User();
+ ecommerceUser.setUsername(usernameAndPassword.getUsername());
+ ecommerceUser.setPassword(usernameAndPassword.getPassword()); // MD5 编码以后
+ ecommerceUser.setExtraInfo("{}");
+
+ // 注册一个新用户, 写一条记录到数据表中
+ ecommerceUser = ecommerceUserDao.save(ecommerceUser);
+ log.info("register user success: [{}], [{}]", ecommerceUser.getUsername(),
+ ecommerceUser.getId());
+
+ // 生成 token 并返回
+ return generateToken(ecommerceUser.getUsername(), ecommerceUser.getPassword());
+ }
+
+ /**
+ * 根据本地存储的私钥获取到 PrivateKey 对象
+ * */
+ private PrivateKey getPrivateKey() throws Exception {
+
+ PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
+ new BASE64Decoder().decodeBuffer(AuthorityConstant.PRIVATE_KEY));
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(priPKCS8);
+ }
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/bootstrap.yml b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..ebf7d22
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/bootstrap.yml
@@ -0,0 +1,64 @@
+server:
+ port: 7000
+ servlet:
+ context-path: /dev-protocol-springcloud-project-authority-center
+
+spring:
+ application:
+ name: dev-protocol-springcloud-project-authority-center
+ cloud:
+ nacos:
+ discovery:
+ enabled: true # 如果不想使用 Nacos 进行服务注册和发现, 设置为 false 即可
+ server-addr: 127.0.0.1:8848 # Nacos 服务器地址
+ # server-addr: 127.0.0.1:8848,127.0.0.1:8849,127.0.0.1:8850 # Nacos 服务器地址
+ namespace: 1ccc74ae-9398-4dbe-b9d7-4f9addf9f40c
+ metadata:
+ management:
+ context-path: ${server.servlet.context-path}/actuator
+ jpa:
+ show-sql: true
+ hibernate:
+ ddl-auto: none
+ properties:
+ hibernate.show_sql: true
+ hibernate.format_sql: true
+ hibernate:
+ dialect: org.hibernate.dialect.MySQLDialect
+ open-in-view: false
+ datasource:
+ # 数据源
+ url: jdbc:mysql://127.0.0.1:3306/dev_protocol_springcloud_project?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
+ username: root
+ password: root
+ type: com.zaxxer.hikari.HikariDataSource
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ # 连接池
+ hikari:
+ maximum-pool-size: 8
+ minimum-idle: 4
+ idle-timeout: 30000
+ connection-timeout: 30000
+ max-lifetime: 45000
+ auto-commit: true
+ pool-name: devProtocolSpringcloudHikariCP
+# kafka:
+# bootstrap-servers: 127.0.0.1:9092
+# producer:
+# retries: 3
+# consumer:
+# auto-offset-reset: latest
+# zipkin:
+# sender:
+# type: kafka # 默认是 web
+# base-url: http://127.0.0.1:9411/
+
+# 暴露端点
+management:
+ endpoints:
+ web:
+ exposure:
+ include: '*'
+ endpoint:
+ health:
+ show-details: always
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/http/authority.http b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/http/authority.http
new file mode 100644
index 0000000..cea788e
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/http/authority.http
@@ -0,0 +1,18 @@
+### 获取 Token -- 登录功能实现
+POST http://127.0.0.1:7000/ecommerce-authority-center/authority/token
+Content-Type: application/json
+
+{
+ "username": "Qinyi02@imooc.com",
+ "password": "25d55ad283aa400af464c76d713c07ad"
+}
+
+
+### 注册用户并返回 Token -- 注册功能实现
+POST http://127.0.0.1:7000/ecommerce-authority-center/authority/register
+Content-Type: application/json
+
+{
+ "username": "ImoocQinyiImooc@imooc.com",
+ "password": "25d55ad283aa400af464c76d713c07ad"
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/sql/t_ecommerce_user.sql b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/sql/t_ecommerce_user.sql
new file mode 100644
index 0000000..e698bc7
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/main/resources/sql/t_ecommerce_user.sql
@@ -0,0 +1,11 @@
+-- 创建 t_ecommerce_user 数据表
+CREATE TABLE IF NOT EXISTS `dev_protocol_springcloud_project`.`t_dev_protocol_cloud_user` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
+ `username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名',
+ `password` varchar(256) NOT NULL DEFAULT '' COMMENT 'MD5 加密之后的密码',
+ `extra_info` varchar(1024) NOT NULL DEFAULT '' COMMENT '额外的信息',
+ `create_time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00' COMMENT '创建时间',
+ `update_time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00' COMMENT '更新时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/AuthorityCenterApplicationTests.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/AuthorityCenterApplicationTests.java
new file mode 100644
index 0000000..03945be
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/AuthorityCenterApplicationTests.java
@@ -0,0 +1,21 @@
+package org.example;
+
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * 授权中心测试入口
+ * 验证授权中心环境可用性
+ * */
+@SpringBootTest
+@RunWith(SpringRunner.class)
+public class AuthorityCenterApplicationTests {
+
+ @Test
+ public void contextLoad() {
+
+ }
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/dao/UserDaoTest.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/dao/UserDaoTest.java
new file mode 100644
index 0000000..06d0d66
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/dao/UserDaoTest.java
@@ -0,0 +1,35 @@
+package org.example.dao;
+
+import cn.hutool.crypto.digest.MD5;
+import com.alibaba.fastjson.JSON;
+import lombok.extern.slf4j.Slf4j;
+import org.example.entity.User;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+
+/**
+ * User 相关的测试
+ * */
+@Slf4j
+@SpringBootTest
+@RunWith(SpringRunner.class)
+public class UserDaoTest {
+
+ @Autowired
+ private UserDao ecommerceUserDao;
+
+ @Test
+ public void createUserRecord() {
+
+ User ecommerceUser = new User();
+ ecommerceUser.setUsername("q@bbbbbbyyyyyy.com");
+ ecommerceUser.setPassword(MD5.create().digestHex("12345678"));
+ ecommerceUser.setExtraInfo("{}");
+ log.info("save user: [{}]",
+ JSON.toJSON(ecommerceUserDao.save(ecommerceUser)));
+ }
+}
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/service/RSATest.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/service/RSATest.java
new file mode 100644
index 0000000..84bf968
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/src/test/java/org/example/service/RSATest.java
@@ -0,0 +1,50 @@
+package org.example.service;
+
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.asymmetric.RSA;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Test;
+
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+
+/**
+ * RSA 非对称加密算法: 生成公钥和私钥
+ * */
+@Slf4j
+public class RSATest {
+
+ // https://doc.hutool.cn/pages/jwt/#jwt%E8%A7%A3%E6%9E%90
+ @Test
+ public void generateKeyBytes() throws Exception {
+
+// KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+// keyPairGenerator.initialize(2048);
+
+ // 生成公钥和私钥对
+// KeyPair keyPair = keyPairGenerator.generateKeyPair();
+// // 获取公钥和私钥对象
+// RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+// RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+//
+// log.info("private key: [{}]", Base64.encode(privateKey.getEncoded()));
+// log.info("public key: [{}]", Base64.encode(publicKey.getEncoded()));
+
+ RSA rsa = new RSA();
+
+ //获得私钥
+ rsa.getPrivateKey();
+ log.info("private key: [{}]", rsa.getPrivateKeyBase64());
+ // MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKzKEUVNF+VVgyar2SAcm9xGDCL4yjMLEXXsy0BNM6rZTh2metCIqA1vbKvFvuruTLcTHYMtO0+urup2ScRhiZtkHMI9Vy/MGbiG0o5T2YrDUYZD4jPPRgvZ2L95uI+nFkEm1WZ7nuO37HvIE3+WDVvJnI84omFsLwN3bLCnStw5AgMBAAECgYACCZePWdAVL+HQlProXIJO1XXFwPN2MtCnzB0cIkk5Kc6zjxLIZf0M2dCGrGONG8BJVEj4Zn6BkXlEqTv+LXVCZfOLNJuXTOEdBTWQj1EFk2wuXIqrcZgFqT56ChSscMQTiEe/O7ydyQ2qD/ZbNDOcJMf6nto883ZDLtVtOTdzxQJBANwaMS8O0/X0/gkdzrY3dKjOJFmXOIybdURAR6Mum/PGkX6j9xAUO1clErKTEY1jkuqohLmnXw+pKTQTW/Gt290CQQDI+HFel+S64xZ9SGyISK+gXl1gK1mpMT2YaQjmzwotNljn7U3g0nChbltNANYsRcE5X8/kVoX7AihO+8RZp1oNAkEAgS66SVVZoJVfeHhPN/GKff0nppGz9grUI+/aW/NiQwz7nimcO4q0XWx78eWRuruDokiwRcrvZ2Cwt0jZgRq63QJBAIVli3LbZcK7K1lbclb/0Dulh1tnSutoONdqmLMDqGCcW2UO+guKA6LTqpyxOnhGkNwxgb+xwtr68qCCszFDSR0CQQDC52dm0bei9PCi0pebOhtQVYdPx+zfZE4p+aRCV7pYjm8HgMMJslKX8sgLEOg91gO/925QRb0uN5H5oDjOHFVh
+ //获得公钥
+ rsa.getPublicKey();
+ log.info("public key: [{}]", rsa.getPublicKeyBase64());
+ // MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsyhFFTRflVYMmq9kgHJvcRgwi+MozCxF17MtATTOq2U4dpnrQiKgNb2yrxb7q7ky3Ex2DLTtPrq7qdknEYYmbZBzCPVcvzBm4htKOU9mKw1GGQ+Izz0YL2di/ebiPpxZBJtVme57jt+x7yBN/lg1byZyPOKJhbC8Dd2ywp0rcOQIDAQAB
+
+ // 自助生成密钥对
+ KeyPair pair = SecureUtil.generateKeyPair("RSA");
+ pair.getPrivate();
+ pair.getPublic();
+ // 自助生成的密钥对是byte[]形式,我们可以使用Base64.encode方法转为Base64,便于存储为文本。 当然,如果使用RSA对象,也可以使用encryptStr和decryptStr加密解密为字符串。
+ }
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md
new file mode 100644
index 0000000..7c3e020
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-authority-center/电商-授权鉴权服务.md
@@ -0,0 +1,34 @@
+## 授权、鉴权中心微服务功能设计
+- 什么是 JWT
+ - JSON Web Token (WT)是一个开放标准,它定义了一种紧凑的、自包含的方式,用于作为 JSON对象在各方之间安全地传输信息
+ - 哪些场景下可以考虑使用 JWT?
+ - 用户授权 信息交换
+---
+- JWT 的结构(组成部分)
+ - JWT由三个部分组成:Header、Payload、Signature,且用圆点连接 xxxxx.yyyyy.ZZZZZ
+ - Header:由两部分(Token 类型、加密算法名称)组成,并使用 Base64 编码 {'alg':"HS256",'type':"JWT"}
+ - Payload:KV 形式的数据,即你想传递的数据(授权的话就是 Token 信息 )
+ - Signature:为了得到签名部分,你必须有编码过的 Header、编码过的 payload、一个秘钥, 签名算法是 Header 中指定的那个,然对它们签名即可
+ - HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
+---
+- 授权、鉴权中心微服务功能逻辑架构
+ - ![授权鉴权中心微服务功能逻辑架构.png](pic/授权鉴权中心微服务功能逻辑架构.png)
+ - 鉴权功能不走HTTP可以更快来进行完成开发, 因为微服务很多模块都要依赖鉴权服务
+---
+## 搭建授权、鉴权中心微服务
+- [FIX] Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
+ - 加入配置:
+ - spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
+- [FIX] Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time
+ - 在配置文件中 spring.datasource.url 加 serverTimezone=GMT%2B8
+## 数据表及 ORM 过程
+-
+## 生成 RSA256 公钥和私钥对
+- [RSATest.java]
+ - 一般是放在服务器上, 用的时候拉取下来使用
+
+## 基于 JWT + RSA256 的授权
+
+## 验证服务可用性
+
+## 授权、鉴权中心微服务总结
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/pom.xml
new file mode 100644
index 0000000..a3c6db4
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+
+ org.example
+ dev-protocol
+ 1.0-SNAPSHOT
+ ../../pom.xml
+
+
+ dev-protocol-springcloud-project-common
+ 1.0-SNAPSHOT
+ jar
+
+ dev-protocol-springcloud-project-common
+ 通用模块
+
+
+ 8
+ 8
+ UTF-8
+ 0.9.1
+
+
+
+
+ io.jsonwebtoken
+ jjwt
+ ${jjwt.version}
+
+
+ org.projectlombok
+ lombok
+
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+
+
+ cn.hutool
+ hutool-all
+ 5.7.22
+
+
+
+ com.alibaba.fastjson2
+ fastjson2
+ 2.0.18
+
+
+
+
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/constant/CommonConstant.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/constant/CommonConstant.java
new file mode 100644
index 0000000..d65c0c2
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/constant/CommonConstant.java
@@ -0,0 +1,19 @@
+package org.example.constant;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:18:06
+ * @Description 通用模块常量定义
+ */
+public final class CommonConstant {
+
+ /** RSA 公钥 */
+ public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsyhFFTRflVYMmq9kgHJvcRgwi+MozCxF17MtATTOq2U4dpnrQiKgNb2yrxb7q7ky3Ex2DLTtPrq7qdknEYYmbZBzCPVcvzBm4htKOU9mKw1GGQ+Izz0YL2di/ebiPpxZBJtVme57jt+x7yBN/lg1byZyPOKJhbC8Dd2ywp0rcOQIDAQAB";
+
+ /** JWT 中存储用户信息的 key */
+ public static final String JWT_USER_INFO_KEY = "dev-protocol-user";
+
+ /** 授权中心的 service-id */
+ public static final String AUTHORITY_CENTER_SERVICE_ID = "dev-protocol-springcloud-project-authority-center";
+
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/utils/TokenParseUtils.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/utils/TokenParseUtils.java
new file mode 100644
index 0000000..289a345
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/utils/TokenParseUtils.java
@@ -0,0 +1,64 @@
+package org.example.utils;
+
+import com.alibaba.fastjson2.JSON;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.Jwts;
+import org.example.constant.CommonConstant;
+import org.example.vo.LoginUserInfo;
+import sun.misc.BASE64Decoder;
+
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Calendar;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:17:42
+ * @Description JWT Token 解析工具类
+ */
+public class TokenParseUtils {
+
+ /**
+ * 从 JWT Token 中解析 LoginUserInfo 对象
+ */
+ public static LoginUserInfo parseUserInfoFromToken(String token) throws Exception {
+ if (null == token) {
+ return null;
+ }
+
+ Jws claimsJws = parseToken(token, getPublicKey());
+ Claims body = claimsJws.getBody();
+
+ // 如果 Token 已经过期了, 返回 null
+ if (body.getExpiration().before(Calendar.getInstance().getTime())) {
+ return null;
+ }
+
+ // 返回 Token 中保存的用户信息
+ return JSON.parseObject(
+ body.get(CommonConstant.JWT_USER_INFO_KEY).toString(),
+ LoginUserInfo.class
+ );
+ }
+
+ /**
+ * 通过公钥去解析 JWT Token
+ */
+ private static Jws parseToken(String token, PublicKey publicKey) {
+ return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
+ }
+
+ /**
+ * 根据本地存储的公钥获取到 PublicKey 对象
+ */
+ private static PublicKey getPublicKey() throws Exception {
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(
+ new BASE64Decoder().decodeBuffer(CommonConstant.PUBLIC_KEY)
+ );
+ return KeyFactory.getInstance("RSA").generatePublic(keySpec);
+ }
+
+
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/CommonResponse.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/CommonResponse.java
new file mode 100644
index 0000000..07977c9
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/CommonResponse.java
@@ -0,0 +1,39 @@
+package org.example.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:17:02
+ * @Description 通用响应对象定义
+ * {
+ * "code": 0,
+ * "message": "",
+ * "data": {}
+ * }
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class CommonResponse implements Serializable {
+
+ /** 错误码 */
+ private Integer code;
+
+ /** 错误消息 */
+ private String message;
+
+ /** 泛型响应数据 */
+ private T Data;
+
+ public CommonResponse(Integer code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/JwtToken.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/JwtToken.java
new file mode 100644
index 0000000..478719a
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/JwtToken.java
@@ -0,0 +1,22 @@
+package org.example.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:17:16
+ * @Description 授权中心鉴权之后给客户端的 Token
+ */
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+public class JwtToken {
+
+ /** JWT */
+ private String token;
+
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/LoginUserInfo.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/LoginUserInfo.java
new file mode 100644
index 0000000..22320fb
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/LoginUserInfo.java
@@ -0,0 +1,25 @@
+package org.example.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:17:24
+ * @Description 登录用户信息
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class LoginUserInfo {
+
+ /** 用户 id */
+ private Long id;
+
+ /** 用户名 */
+ private String username;
+
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/UsernameAndPassword.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/UsernameAndPassword.java
new file mode 100644
index 0000000..05171dd
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-common/src/main/java/org/example/vo/UsernameAndPassword.java
@@ -0,0 +1,21 @@
+package org.example.vo;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:17:33
+ * @Description 用户名和密码
+ */
+@Getter
+@Setter
+public class UsernameAndPassword {
+
+ /** 用户名 */
+ private String username;
+
+ /** 密码 */
+ private String password;
+
+}
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml
new file mode 100644
index 0000000..225a19c
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+
+ org.example
+ dev-protocol
+ 1.0-SNAPSHOT
+ ../../pom.xml
+
+
+ dev-protocol-springcloud-project-mvc-config
+
+
+ dev-protocol-springcloud-project-mvc-config
+ 通用配置模块
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.example
+ dev-protocol-springcloud-project-common
+ 1.0-SNAPSHOT
+
+
+
+
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/src/main/java/org/example/Main.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/src/main/java/org/example/Main.java
new file mode 100644
index 0000000..407f157
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/src/main/java/org/example/Main.java
@@ -0,0 +1,7 @@
+package org.example;
+
+public class Main {
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+}
\ No newline at end of file
diff --git a/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/src/main/java/org/example/advice/CommonResponseDataAdvice.java b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/src/main/java/org/example/advice/CommonResponseDataAdvice.java
new file mode 100644
index 0000000..2ccb898
--- /dev/null
+++ b/dev-protocol-springcloud/dev-protocol-springcloud-project-mvc-config/src/main/java/org/example/advice/CommonResponseDataAdvice.java
@@ -0,0 +1,72 @@
+package org.example.advice;
+
+
+import org.example.annotation.IgnoreResponseAdvice;
+import org.example.vo.CommonResponse;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+/**
+ * @author q
+ * @createTime 2024-08-19 18:11:45
+ * @Description 实现统一响应
+ * ResponseBodyAdvice接口是在Controller执行return之后,在response返回给浏览器或者APP客户端之前,执行的对response的一些处理。可以实现对response数据的一些统一封装或者加密等操作
+ */
+//声明该类要处理的包路径
+@RestControllerAdvice("org.example")
+public class CommonResponseDataAdvice implements ResponseBodyAdvice