Explorar el Código

commit message

Yangzw hace 3 meses
commit
4a4eef69c7
Se han modificado 100 ficheros con 1589 adiciones y 0 borrados
  1. 53 0
      .gitignore
  2. BIN
      .image/Java监控.jpg
  3. BIN
      .image/MySQL.jpg
  4. BIN
      .image/OA请假-列表.jpg
  5. BIN
      .image/OA请假-发起.jpg
  6. BIN
      .image/OA请假-详情.jpg
  7. BIN
      .image/Redis.jpg
  8. BIN
      .image/admin-uniapp/01.png
  9. BIN
      .image/admin-uniapp/02.png
  10. BIN
      .image/admin-uniapp/03.png
  11. BIN
      .image/admin-uniapp/04.png
  12. BIN
      .image/admin-uniapp/05.png
  13. BIN
      .image/admin-uniapp/06.png
  14. BIN
      .image/admin-uniapp/07.png
  15. BIN
      .image/admin-uniapp/08.png
  16. BIN
      .image/admin-uniapp/09.png
  17. BIN
      .image/common/feifan-cloud-architecture.png
  18. BIN
      .image/common/feifan-roadmap.png
  19. BIN
      .image/common/mall-feature.png
  20. BIN
      .image/common/mall-preview.png
  21. BIN
      .image/common/project-vs.png
  22. BIN
      .image/common/ruoyi-vue-pro-architecture.png
  23. BIN
      .image/common/ruoyi-vue-pro-biz.png
  24. BIN
      .image/个人中心.jpg
  25. BIN
      .image/代码生成.jpg
  26. BIN
      .image/令牌管理.jpg
  27. BIN
      .image/任务列表-审批.jpg
  28. BIN
      .image/任务列表-已办.jpg
  29. BIN
      .image/任务列表-待办.jpg
  30. BIN
      .image/任务日志.jpg
  31. BIN
      .image/商户信息.jpg
  32. BIN
      .image/在线用户.jpg
  33. BIN
      .image/大屏设计器-列表.jpg
  34. BIN
      .image/大屏设计器-编辑.jpg
  35. BIN
      .image/大屏设计器-预览.jpg
  36. BIN
      .image/字典数据.jpg
  37. BIN
      .image/字典类型.jpg
  38. BIN
      .image/定时任务.jpg
  39. BIN
      .image/岗位管理.jpg
  40. BIN
      .image/应用信息-列表.jpg
  41. BIN
      .image/应用信息-编辑.jpg
  42. BIN
      .image/应用管理.jpg
  43. BIN
      .image/我的流程-列表.jpg
  44. BIN
      .image/我的流程-发起.jpg
  45. BIN
      .image/我的流程-详情.jpg
  46. BIN
      .image/报表设计器-图形报表.jpg
  47. BIN
      .image/报表设计器-打印设计.jpg
  48. BIN
      .image/报表设计器-数据报表.jpg
  49. BIN
      .image/操作日志.jpg
  50. BIN
      .image/支付订单.jpg
  51. BIN
      .image/敏感词.jpg
  52. BIN
      .image/数据库文档.jpg
  53. BIN
      .image/文件管理.jpg
  54. BIN
      .image/文件管理2.jpg
  55. BIN
      .image/文件配置.jpg
  56. BIN
      .image/日志中心.jpg
  57. BIN
      .image/流程模型-列表.jpg
  58. BIN
      .image/流程模型-定义.jpg
  59. BIN
      .image/流程模型-设计.jpg
  60. BIN
      .image/流程表单.jpg
  61. BIN
      .image/生成效果.jpg
  62. BIN
      .image/用户分组.jpg
  63. BIN
      .image/用户管理.jpg
  64. BIN
      .image/登录.jpg
  65. BIN
      .image/登录日志.jpg
  66. BIN
      .image/短信日志.jpg
  67. BIN
      .image/短信模板.jpg
  68. BIN
      .image/短信渠道.jpg
  69. BIN
      .image/租户套餐.png
  70. BIN
      .image/租户管理.jpg
  71. BIN
      .image/系统接口.jpg
  72. BIN
      .image/菜单管理.jpg
  73. BIN
      .image/表单构建.jpg
  74. BIN
      .image/角色管理.jpg
  75. BIN
      .image/访问日志.jpg
  76. BIN
      .image/退款订单.jpg
  77. BIN
      .image/通知公告.jpg
  78. BIN
      .image/部门管理.jpg
  79. BIN
      .image/配置管理.jpg
  80. BIN
      .image/链路追踪.jpg
  81. BIN
      .image/错误日志.jpg
  82. BIN
      .image/错误码管理.jpg
  83. BIN
      .image/首页.jpg
  84. 20 0
      LICENSE
  85. 1 0
      README.md
  86. 685 0
      feifan-dependencies/pom.xml
  87. 65 0
      feifan-example/feifan-sso-demo-by-code/pom.xml
  88. 13 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/SSODemoApplication.java
  89. 157 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/OAuth2Client.java
  90. 73 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/UserClient.java
  91. 28 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/CommonResult.java
  92. 45 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java
  93. 59 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java
  94. 97 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/user/UserInfoRespDTO.java
  95. 35 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/user/UserUpdateReqDTO.java
  96. 63 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/controller/AuthController.java
  97. 40 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/controller/UserController.java
  98. 52 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/framework/config/SecurityConfiguration.java
  99. 37 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/framework/core/LoginUser.java
  100. 66 0
      feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/framework/core/filter/TokenAuthenticationFilter.java

+ 53 - 0
.gitignore

@@ -0,0 +1,53 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+.flattened-pom.xml
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
+
+### JRebel ###
+rebel.xml
+
+application-my.yaml
+
+/feifan-ui-app/unpackage/

BIN
.image/Java监控.jpg


BIN
.image/MySQL.jpg


BIN
.image/OA请假-列表.jpg


BIN
.image/OA请假-发起.jpg


BIN
.image/OA请假-详情.jpg


BIN
.image/Redis.jpg


BIN
.image/admin-uniapp/01.png


BIN
.image/admin-uniapp/02.png


BIN
.image/admin-uniapp/03.png


BIN
.image/admin-uniapp/04.png


BIN
.image/admin-uniapp/05.png


BIN
.image/admin-uniapp/06.png


BIN
.image/admin-uniapp/07.png


BIN
.image/admin-uniapp/08.png


BIN
.image/admin-uniapp/09.png


BIN
.image/common/feifan-cloud-architecture.png


BIN
.image/common/feifan-roadmap.png


BIN
.image/common/mall-feature.png


BIN
.image/common/mall-preview.png


BIN
.image/common/project-vs.png


BIN
.image/common/ruoyi-vue-pro-architecture.png


BIN
.image/common/ruoyi-vue-pro-biz.png


BIN
.image/个人中心.jpg


BIN
.image/代码生成.jpg


BIN
.image/令牌管理.jpg


BIN
.image/任务列表-审批.jpg


BIN
.image/任务列表-已办.jpg


BIN
.image/任务列表-待办.jpg


BIN
.image/任务日志.jpg


BIN
.image/商户信息.jpg


BIN
.image/在线用户.jpg


BIN
.image/大屏设计器-列表.jpg


BIN
.image/大屏设计器-编辑.jpg


BIN
.image/大屏设计器-预览.jpg


BIN
.image/字典数据.jpg


BIN
.image/字典类型.jpg


BIN
.image/定时任务.jpg


BIN
.image/岗位管理.jpg


BIN
.image/应用信息-列表.jpg


BIN
.image/应用信息-编辑.jpg


BIN
.image/应用管理.jpg


BIN
.image/我的流程-列表.jpg


BIN
.image/我的流程-发起.jpg


BIN
.image/我的流程-详情.jpg


BIN
.image/报表设计器-图形报表.jpg


BIN
.image/报表设计器-打印设计.jpg


BIN
.image/报表设计器-数据报表.jpg


BIN
.image/操作日志.jpg


BIN
.image/支付订单.jpg


BIN
.image/敏感词.jpg


BIN
.image/数据库文档.jpg


BIN
.image/文件管理.jpg


BIN
.image/文件管理2.jpg


BIN
.image/文件配置.jpg


BIN
.image/日志中心.jpg


BIN
.image/流程模型-列表.jpg


BIN
.image/流程模型-定义.jpg


BIN
.image/流程模型-设计.jpg


BIN
.image/流程表单.jpg


BIN
.image/生成效果.jpg


BIN
.image/用户分组.jpg


BIN
.image/用户管理.jpg


BIN
.image/登录.jpg


BIN
.image/登录日志.jpg


BIN
.image/短信日志.jpg


BIN
.image/短信模板.jpg


BIN
.image/短信渠道.jpg


BIN
.image/租户套餐.png


BIN
.image/租户管理.jpg


BIN
.image/系统接口.jpg


BIN
.image/菜单管理.jpg


BIN
.image/表单构建.jpg


BIN
.image/角色管理.jpg


BIN
.image/访问日志.jpg


BIN
.image/退款订单.jpg


BIN
.image/通知公告.jpg


BIN
.image/部门管理.jpg


BIN
.image/配置管理.jpg


BIN
.image/链路追踪.jpg


BIN
.image/错误日志.jpg


BIN
.image/错误码管理.jpg


BIN
.image/首页.jpg


+ 20 - 0
LICENSE

@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2021 ruoyi-vue-pro
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 1 - 0
README.md

@@ -0,0 +1 @@
+中星商城系统管理员后台java项目

+ 685 - 0
feifan-dependencies/pom.xml

@@ -0,0 +1,685 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>cn.newfeifan.zx</groupId>
+    <artifactId>feifan-dependencies</artifactId>
+    <version>${revision}</version>
+    <packaging>pom</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>基础 bom 文件,管理整个项目的依赖版本</description>
+    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
+
+    <properties>
+        <revision>2.0.0-jdk8-snapshot</revision>
+        <flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
+        <!-- 统一依赖管理 -->
+        <spring.boot.version>2.7.18</spring.boot.version>
+        <!-- Web 相关 -->
+        <springdoc.version>1.6.15</springdoc.version>
+        <knife4j.version>4.3.0</knife4j.version>
+        <servlet.versoin>2.5</servlet.versoin>
+        <!-- DB 相关 -->
+        <druid.version>1.2.21</druid.version>
+        <mybatis-plus.version>3.5.5</mybatis-plus.version>
+        <mybatis-plus-generator.version>3.5.5</mybatis-plus-generator.version>
+        <dynamic-datasource.version>4.3.0</dynamic-datasource.version>
+        <mybatis-plus-join.version>1.4.10</mybatis-plus-join.version>
+        <redisson.version>3.18.0</redisson.version> <!-- Spring Boot 2.X 最多使用 3.18.0 版本,否则会报 Tuple NoClassDefFoundError -->
+        <dm8.jdbc.version>8.1.3.62</dm8.jdbc.version>
+        <!-- 消息队列 -->
+        <rocketmq-spring.version>2.2.3</rocketmq-spring.version>
+        <!-- 服务保障相关 -->
+        <lock4j.version>2.2.7</lock4j.version>
+        <resilience4j.version>1.7.1</resilience4j.version>
+        <!-- 监控相关 -->
+        <skywalking.version>8.12.0</skywalking.version>
+        <spring-boot-admin.version>2.7.15</spring-boot-admin.version>
+        <opentracing.version>0.33.0</opentracing.version>
+        <!-- Test 测试相关 -->
+        <podam.version>7.2.11.RELEASE</podam.version> <!-- Spring Boot 2.X 最多使用 7.2.11 版本 -->
+        <jedis-mock.version>1.0.13</jedis-mock.version>
+        <mockito-inline.version>4.11.0</mockito-inline.version>
+        <!-- Bpm 工作流相关 -->
+        <flowable.version>6.8.0</flowable.version>
+        <!-- 工具类相关 -->
+        <captcha-plus.version>1.0.10</captcha-plus.version>
+        <jsoup.version>1.17.2</jsoup.version>
+        <lombok.version>1.18.30</lombok.version>
+        <mapstruct.version>1.5.5.Final</mapstruct.version>
+        <hutool.version>5.8.25</hutool.version>
+        <easyexcel.verion>3.3.3</easyexcel.verion>
+        <velocity.version>2.3</velocity.version>
+        <screw.version>1.0.5</screw.version>
+        <fastjson.version>1.2.83</fastjson.version>
+        <guava.version>33.0.0-jre</guava.version>
+        <guice.version>5.1.0</guice.version>
+        <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
+        <commons-net.version>3.10.0</commons-net.version>
+        <jsch.version>0.1.55</jsch.version>
+        <tika-core.version>2.9.1</tika-core.version>
+        <ip2region.version>2.7.0</ip2region.version>
+        <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
+        <!-- 三方云服务相关 -->
+        <okio.version>3.5.0</okio.version>
+        <okhttp3.version>4.11.0</okhttp3.version>
+        <commons-io.version>2.15.1</commons-io.version>
+        <minio.version>8.5.7</minio.version>
+        <aliyun-java-sdk-core.version>4.6.4</aliyun-java-sdk-core.version>
+        <aliyun-java-sdk-dysmsapi.version>2.2.1</aliyun-java-sdk-dysmsapi.version>
+        <tencentcloud-sdk-java.version>3.1.880</tencentcloud-sdk-java.version>
+        <justauth.version>1.0.8</justauth.version>
+        <jimureport.version>1.6.6</jimureport.version>
+        <xercesImpl.version>2.12.2</xercesImpl.version>
+        <weixin-java.version>4.6.0</weixin-java.version>
+        <ureport2.version>2.2.9</ureport2.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- 统一依赖管理 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+
+            <!-- 业务组件 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-biz-operatelog</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.github.mouzt</groupId>
+                <artifactId>bizlog-sdk</artifactId>
+                <version>${bizlog-sdk.version}</version>
+                <exclusions>
+                    <exclusion> <!-- 排除掉springboot依赖使用项目的 -->
+                        <groupId>org.springframework.boot</groupId>
+                        <artifactId>spring-boot-starter</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-biz-dict</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-biz-pay</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-biz-tenant</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-biz-data-permission</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-biz-ip</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-captcha</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-desensitize</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- Spring 核心 -->
+            <dependency>
+                <!-- 用于生成自定义的 Spring @ConfigurationProperties 配置类的说明文件 -->
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-configuration-processor</artifactId>
+                <version>${spring.boot.version}</version>
+            </dependency>
+
+            <!-- Web 相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-web</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-security</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-websocket</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId>
+                <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
+                <version>${knife4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springdoc</groupId>
+                <artifactId>springdoc-openapi-ui</artifactId>
+                <version>${springdoc.version}</version>
+            </dependency>
+
+            <!-- DB 相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-mybatis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <version>${mybatis-plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
+                <version>${mybatis-plus-generator.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
+                <version>${dynamic-datasource.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.yulichang</groupId>
+                <artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
+                <version>${mybatis-plus-join.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-redis</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.redisson</groupId>
+                <artifactId>redisson-spring-boot-starter</artifactId>
+                <version>${redisson.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework.boot</groupId>
+                        <artifactId>spring-boot-starter-actuator</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <dependency>
+                <groupId>com.dameng</groupId>
+                <artifactId>DmJdbcDriver18</artifactId>
+                <version>${dm8.jdbc.version}</version>
+            </dependency>
+
+            <!-- Job 定时任务相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-job</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <!-- 消息队列相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-mq</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.rocketmq</groupId>
+                <artifactId>rocketmq-spring-boot-starter</artifactId>
+                <version>${rocketmq-spring.version}</version>
+            </dependency>
+
+            <!-- 服务保障相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-protection</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>lock4j-redisson-spring-boot-starter</artifactId>
+                <version>${lock4j.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <artifactId>redisson-spring-boot-starter</artifactId>
+                        <groupId>org.redisson</groupId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <dependency>
+                <groupId>io.github.resilience4j</groupId>
+                <artifactId>resilience4j-ratelimiter</artifactId>
+                <version>${resilience4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.github.resilience4j</groupId>
+                <artifactId>resilience4j-spring-boot2</artifactId>
+                <version>${resilience4j.version}</version>
+            </dependency>
+
+            <!-- 监控相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-monitor</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-trace</artifactId>
+                <version>${skywalking.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-logback-1.x</artifactId>
+                <version>${skywalking.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.skywalking</groupId>
+                <artifactId>apm-toolkit-opentracing</artifactId>
+                <version>${skywalking.version}</version>
+                <!--                <exclusions>-->
+                <!--                    <exclusion>-->
+                <!--                        <artifactId>opentracing-api</artifactId>-->
+                <!--                        <groupId>io.opentracing</groupId>-->
+                <!--                    </exclusion>-->
+                <!--                    <exclusion>-->
+                <!--                        <artifactId>opentracing-util</artifactId>-->
+                <!--                        <groupId>io.opentracing</groupId>-->
+                <!--                    </exclusion>-->
+                <!--                </exclusions>-->
+            </dependency>
+            <dependency>
+                <groupId>io.opentracing</groupId>
+                <artifactId>opentracing-api</artifactId>
+                <version>${opentracing.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.opentracing</groupId>
+                <artifactId>opentracing-util</artifactId>
+                <version>${opentracing.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.opentracing</groupId>
+                <artifactId>opentracing-noop</artifactId>
+                <version>${opentracing.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>de.codecentric</groupId>
+                <artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
+                <version>${spring-boot-admin.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>de.codecentric</groupId>
+                        <artifactId>spring-boot-admin-server-cloud</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>de.codecentric</groupId>
+                <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
+                <version>${spring-boot-admin.version}</version>
+            </dependency>
+
+            <!-- Test 测试相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-test</artifactId>
+                <version>${revision}</version>
+                <scope>test</scope>
+            </dependency>
+
+            <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-inline</artifactId>
+                <version>${mockito-inline.version}</version> <!-- 支持 Mockito 的 final 类与 static 方法的 mock -->
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-starter-test</artifactId>
+                <version>${spring.boot.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <artifactId>asm</artifactId>
+                        <groupId>org.ow2.asm</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <groupId>org.mockito</groupId>
+                        <artifactId>mockito-core</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.fppt</groupId> <!-- 单元测试,我们采用内嵌的 Redis 数据库 -->
+                <artifactId>jedis-mock</artifactId>
+                <version>${jedis-mock.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>uk.co.jemos.podam</groupId> <!-- 单元测试,随机生成 POJO 类 -->
+                <artifactId>podam</artifactId>
+                <version>${podam.version}</version>
+            </dependency>
+
+            <!-- 工作流相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-flowable</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.flowable</groupId>
+                <artifactId>flowable-spring-boot-starter-process</artifactId>
+                <version>${flowable.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.flowable</groupId>
+                <artifactId>flowable-spring-boot-starter-actuator</artifactId>
+                <version>${flowable.version}</version>
+            </dependency>
+            <!-- 工作流相关结束 -->
+
+            <!-- 工具类相关 -->
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-common</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-excel</artifactId>
+                <version>${revision}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>${lombok.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
+                <version>${mapstruct.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct-jdk8</artifactId>
+                <version>${mapstruct.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mapstruct</groupId>
+                <artifactId>mapstruct-processor</artifactId>
+                <version>${mapstruct.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.hutool</groupId>
+                <artifactId>hutool-all</artifactId>
+                <version>${hutool.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>${easyexcel.verion}</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-io</groupId>
+                <artifactId>commons-io</artifactId>
+                <version>${commons-io.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.tika</groupId>
+                <artifactId>tika-core</artifactId> <!-- 文件类型的识别 -->
+                <version>${tika-core.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.apache.velocity</groupId>
+                <artifactId>velocity-engine-core</artifactId>
+                <version>${velocity.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>${fastjson.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>cn.smallbun.screw</groupId>
+                <artifactId>screw-core</artifactId> <!-- 实现数据库文档 -->
+                <version>${screw.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.freemarker</groupId>
+                        <artifactId>freemarker</artifactId> <!-- 移除 Freemarker 依赖,采用 Velocity 作为模板引擎 -->
+                    </exclusion>
+                    <exclusion>
+                        <groupId>com.alibaba</groupId>
+                        <artifactId>fastjson</artifactId> <!-- 最新版screw-core1.0.5依赖fastjson1.2.73存在漏洞,移除。 -->
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>${guava.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.google.inject</groupId>
+                <artifactId>guice</artifactId>
+                <version>${guice.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>transmittable-thread-local</artifactId> <!-- 解决 ThreadLocal 父子线程的传值问题 -->
+                <version>${transmittable-thread-local.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>commons-net</groupId>
+                <artifactId>commons-net</artifactId> <!-- 解决 ftp 连接 -->
+                <version>${commons-net.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.jcraft</groupId>
+                <artifactId>jsch</artifactId> <!-- 解决 sftp 连接 -->
+                <version>${jsch.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.xingyuv</groupId>
+                <artifactId>spring-boot-starter-captcha-plus</artifactId>
+                <version>${captcha-plus.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.lionsoul</groupId>
+                <artifactId>ip2region</artifactId>
+                <version>${ip2region.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>org.jsoup</groupId>
+                <artifactId>jsoup</artifactId>
+                <version>${jsoup.version}</version>
+            </dependency>
+
+            <!-- 三方云服务相关 -->
+            <dependency>
+                <groupId>com.squareup.okio</groupId>
+                <artifactId>okio</artifactId>
+                <version>${okio.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>${okhttp3.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>cn.newfeifan.zx</groupId>
+                <artifactId>feifan-spring-boot-starter-file</artifactId>
+                <version>${revision}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.minio</groupId>
+                <artifactId>minio</artifactId>
+                <version>${minio.version}</version>
+            </dependency>
+
+            <!-- SMS SDK begin -->
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>aliyun-java-sdk-core</artifactId>
+                <version>${aliyun-java-sdk-core.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <artifactId>opentracing-api</artifactId>
+                        <groupId>io.opentracing</groupId>
+                    </exclusion>
+                    <exclusion>
+                        <artifactId>opentracing-util</artifactId>
+                        <groupId>io.opentracing</groupId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
+                <version>${aliyun-java-sdk-dysmsapi.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.tencentcloudapi</groupId>
+                <artifactId>tencentcloud-sdk-java-sms</artifactId>
+                <version>${tencentcloud-sdk-java.version}</version>
+            </dependency>
+            <!-- SMS SDK end -->
+
+            <dependency>
+                <groupId>com.xingyuv</groupId>
+                <artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
+                <version>${justauth.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>cn.hutool</groupId>
+                        <artifactId>hutool-core</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
+            <dependency>
+                <groupId>com.github.binarywang</groupId>
+                <artifactId>weixin-java-pay</artifactId>
+                <version>${weixin-java.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.binarywang</groupId>
+                <artifactId>wx-java-mp-spring-boot-starter</artifactId>
+                <version>${weixin-java.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.binarywang</groupId>
+                <artifactId>wx-java-miniapp-spring-boot-starter</artifactId>
+                <version>${weixin-java.version}</version>
+            </dependency>
+
+            <!-- 积木报表-->
+            <dependency>
+                <groupId>org.jeecgframework.jimureport</groupId>
+                <artifactId>jimureport-spring-boot-starter</artifactId>
+                <version>${jimureport.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>com.alibaba</groupId>
+                        <artifactId>druid</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>xerces</groupId>
+                <artifactId>xercesImpl</artifactId>
+                <version>${xercesImpl.version}</version>
+            </dependency>
+
+            <!-- UReport2 报表-->
+            <dependency>
+                <groupId>com.bstek.ureport</groupId>
+                <artifactId>ureport2-console</artifactId>
+                <version>${ureport2.version}</version>
+            </dependency>
+
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <plugins>
+            <!-- 统一 revision 版本 -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>flatten-maven-plugin</artifactId>
+                <version>${flatten-maven-plugin.version}</version>
+                <configuration>
+                    <flattenMode>resolveCiFriendliesOnly</flattenMode>
+                    <updatePomFile>true</updatePomFile>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>flatten</goal>
+                        </goals>
+                        <id>flatten</id>
+                        <phase>process-resources</phase>
+                    </execution>
+                    <execution>
+                        <goals>
+                            <goal>clean</goal>
+                        </goals>
+                        <id>flatten.clean</id>
+                        <phase>clean</phase>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 65 - 0
feifan-example/feifan-sso-demo-by-code/pom.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <!-- 由于方便大家拷贝,使用不使用 feifan 作为 Maven parent -->
+
+    <groupId>cn.newfeifan.zx</groupId>
+    <artifactId>feifan-sso-demo-by-code</artifactId>
+    <version>1.0.0-snapshot</version>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>基于授权码模式,如何实现 SSO 单点登录?</description>
+    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
+
+    <properties>
+        <!-- Maven 相关 -->
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <!-- 统一依赖管理 -->
+        <spring.boot.version>2.7.17</spring.boot.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- 统一依赖管理 -->
+            <dependency>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-dependencies</artifactId>
+                <version>${spring.boot.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <!-- Web 相关 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.22</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 13 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/SSODemoApplication.java

@@ -0,0 +1,13 @@
+package cn.newfeifan.mall.ssodemo;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SSODemoApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(SSODemoApplication.class, args);
+    }
+
+}

+ 157 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/OAuth2Client.java

@@ -0,0 +1,157 @@
+package cn.newfeifan.mall.ssodemo.client;
+
+import cn.newfeifan.mall.ssodemo.client.dto.CommonResult;
+import cn.newfeifan.mall.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
+import cn.newfeifan.mall.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * OAuth 2.0 客户端
+ *
+ * 对应调用 OAuth2OpenController 接口
+ */
+@Component
+public class OAuth2Client {
+
+    private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";
+
+    /**
+     * 租户编号
+     *
+     * 默认使用 1;如果使用别的租户,可以调整
+     */
+    public static final Long TENANT_ID = 1L;
+
+    private static final String CLIENT_ID = "feifan-sso-demo-by-code";
+    private static final String CLIENT_SECRET = "test";
+
+
+//    @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
+    private final RestTemplate restTemplate = new RestTemplate();
+
+    /**
+     * 使用 code 授权码,获得访问令牌
+     *
+     * @param code        授权码
+     * @param redirectUri 重定向 URI
+     * @return 访问令牌
+     */
+    public CommonResult<OAuth2AccessTokenRespDTO> postAccessToken(String code, String redirectUri) {
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        headers.set("tenant-id", TENANT_ID.toString());
+        addClientHeader(headers);
+        // 1.2 构建请求参数
+        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+        body.add("grant_type", "authorization_code");
+        body.add("code", code);
+        body.add("redirect_uri", redirectUri);
+//        body.add("state", ""); // 选填;填了会校验
+
+        // 2. 执行请求
+        ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
+                BASE_URL + "/token",
+                HttpMethod.POST,
+                new HttpEntity<>(body, headers),
+                new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        return exchange.getBody();
+    }
+
+    /**
+     * 校验访问令牌,并返回它的基本信息
+     *
+     * @param token 访问令牌
+     * @return 访问令牌的基本信息
+     */
+    public CommonResult<OAuth2CheckTokenRespDTO> checkToken(String token) {
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        headers.set("tenant-id", TENANT_ID.toString());
+        addClientHeader(headers);
+        // 1.2 构建请求参数
+        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+        body.add("token", token);
+
+        // 2. 执行请求
+        ResponseEntity<CommonResult<OAuth2CheckTokenRespDTO>> exchange = restTemplate.exchange(
+                BASE_URL + "/check-token",
+                HttpMethod.POST,
+                new HttpEntity<>(body, headers),
+                new ParameterizedTypeReference<CommonResult<OAuth2CheckTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        return exchange.getBody();
+    }
+
+    /**
+     * 使用刷新令牌,获得(刷新)访问令牌
+     *
+     * @param refreshToken 刷新令牌
+     * @return 访问令牌
+     */
+    public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(String refreshToken) {
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        headers.set("tenant-id", TENANT_ID.toString());
+        addClientHeader(headers);
+        // 1.2 构建请求参数
+        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+        body.add("grant_type", "refresh_token");
+        body.add("refresh_token", refreshToken);
+
+        // 2. 执行请求
+        ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
+                BASE_URL + "/token",
+                HttpMethod.POST,
+                new HttpEntity<>(body, headers),
+                new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        return exchange.getBody();
+    }
+
+    /**
+     * 删除访问令牌
+     *
+     * @param token 访问令牌
+     * @return 成功
+     */
+    public CommonResult<Boolean> revokeToken(String token) {
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        headers.set("tenant-id", TENANT_ID.toString());
+        addClientHeader(headers);
+        // 1.2 构建请求参数
+        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+        body.add("token", token);
+
+        // 2. 执行请求
+        ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
+                BASE_URL + "/token",
+                HttpMethod.DELETE,
+                new HttpEntity<>(body, headers),
+                new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        return exchange.getBody();
+    }
+
+    private static void addClientHeader(HttpHeaders headers) {
+        // client 拼接,需要 BASE64 编码
+        String client = CLIENT_ID + ":" + CLIENT_SECRET;
+        client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
+        headers.add("Authorization", "Basic " + client);
+    }
+
+}

+ 73 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/UserClient.java

@@ -0,0 +1,73 @@
+package cn.newfeifan.mall.ssodemo.client;
+
+import cn.newfeifan.mall.ssodemo.client.dto.CommonResult;
+import cn.newfeifan.mall.ssodemo.client.dto.user.UserInfoRespDTO;
+import cn.newfeifan.mall.ssodemo.client.dto.user.UserUpdateReqDTO;
+import cn.newfeifan.mall.ssodemo.framework.core.LoginUser;
+import cn.newfeifan.mall.ssodemo.framework.core.util.SecurityUtils;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * 用户 User 信息的客户端
+ *
+ * 对应调用 OAuth2UserController 接口
+ */
+@Component
+public class UserClient {
+
+    private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user";
+
+    //    @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
+    private final RestTemplate restTemplate = new RestTemplate();
+
+    public CommonResult<UserInfoRespDTO> getUser() {
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+        headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
+        addTokenHeader(headers);
+        // 1.2 构建请求参数
+        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
+
+        // 2. 执行请求
+        ResponseEntity<CommonResult<UserInfoRespDTO>> exchange = restTemplate.exchange(
+                BASE_URL + "/get",
+                HttpMethod.GET,
+                new HttpEntity<>(body, headers),
+                new ParameterizedTypeReference<CommonResult<UserInfoRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        return exchange.getBody();
+    }
+
+    public CommonResult<Boolean> updateUser(UserUpdateReqDTO updateReqDTO) {
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
+        addTokenHeader(headers);
+        // 1.2 构建请求参数
+        // 使用 updateReqDTO 即可
+
+        // 2. 执行请求
+        ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
+                BASE_URL + "/update",
+                HttpMethod.PUT,
+                new HttpEntity<>(updateReqDTO, headers),
+                new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        return exchange.getBody();
+    }
+
+
+    private static void addTokenHeader(HttpHeaders headers) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        Assert.notNull(loginUser, "登录用户不能为空");
+        headers.add("Authorization", "Bearer " + loginUser.getAccessToken());
+    }
+}

+ 28 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/CommonResult.java

@@ -0,0 +1,28 @@
+package cn.newfeifan.mall.ssodemo.client.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 通用返回
+ *
+ * @param <T> 数据泛型
+ */
+@Data
+public class CommonResult<T> implements Serializable {
+
+    /**
+     * 错误码
+     */
+    private Integer code;
+    /**
+     * 返回数据
+     */
+    private T data;
+    /**
+     * 错误提示,用户可阅读
+     */
+    private String msg;
+
+}

+ 45 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java

@@ -0,0 +1,45 @@
+package cn.newfeifan.mall.ssodemo.client.dto.oauth2;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 访问令牌 Response DTO
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class OAuth2AccessTokenRespDTO {
+
+    /**
+     * 访问令牌
+     */
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    /**
+     * 刷新令牌
+     */
+    @JsonProperty("refresh_token")
+    private String refreshToken;
+
+    /**
+     * 令牌类型
+     */
+    @JsonProperty("token_type")
+    private String tokenType;
+
+    /**
+     * 过期时间;单位:秒
+     */
+    @JsonProperty("expires_in")
+    private Long expiresIn;
+
+    /**
+     * 授权范围;如果多个授权范围,使用空格分隔
+     */
+    private String scope;
+
+}

+ 59 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java

@@ -0,0 +1,59 @@
+package cn.newfeifan.mall.ssodemo.client.dto.oauth2;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 校验令牌 Response DTO
+ *
+ * @author 非繁源码
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class OAuth2CheckTokenRespDTO {
+
+    /**
+     * 用户编号
+     */
+    @JsonProperty("user_id")
+    private Long userId;
+    /**
+     * 用户类型
+     */
+    @JsonProperty("user_type")
+    private Integer userType;
+    /**
+     * 租户编号
+     */
+    @JsonProperty("tenant_id")
+    private Long tenantId;
+
+    /**
+     * 客户端编号
+     */
+    @JsonProperty("client_id")
+    private String clientId;
+    /**
+     * 授权范围
+     */
+    private List<String> scopes;
+
+    /**
+     * 访问令牌
+     */
+    @JsonProperty("access_token")
+    private String accessToken;
+
+    /**
+     * 过期时间
+     *
+     * 时间戳 / 1000,即单位:秒
+     */
+    private Long exp;
+
+}

+ 97 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/user/UserInfoRespDTO.java

@@ -0,0 +1,97 @@
+package cn.newfeifan.mall.ssodemo.client.dto.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * 获得用户基本信息 Response dto
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserInfoRespDTO {
+
+    /**
+     * 用户编号
+     */
+    private Long id;
+
+    /**
+     * 用户账号
+     */
+    private String username;
+
+    /**
+     * 用户昵称
+     */
+    private String nickname;
+
+    /**
+     * 用户邮箱
+     */
+    private String email;
+    /**
+     * 手机号码
+     */
+    private String mobile;
+
+    /**
+     * 用户性别
+     */
+    private Integer sex;
+
+    /**
+     * 用户头像
+     */
+    private String avatar;
+
+    /**
+     * 所在部门
+     */
+    private Dept dept;
+
+    /**
+     * 所属岗位数组
+     */
+    private List<Post> posts;
+
+    /**
+     * 部门
+     */
+    @Data
+    public static class Dept {
+
+        /**
+         * 部门编号
+         */
+        private Long id;
+
+        /**
+         * 部门名称
+         */
+        private String name;
+
+    }
+
+    /**
+     * 岗位
+     */
+    @Data
+    public static class Post {
+
+        /**
+         * 岗位编号
+         */
+        private Long id;
+
+        /**
+         * 岗位名称
+         */
+        private String name;
+
+    }
+
+}

+ 35 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/client/dto/user/UserUpdateReqDTO.java

@@ -0,0 +1,35 @@
+package cn.newfeifan.mall.ssodemo.client.dto.user;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 更新用户基本信息 Request DTO
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class UserUpdateReqDTO {
+
+    /**
+     * 用户昵称
+     */
+    private String nickname;
+
+    /**
+     * 用户邮箱
+     */
+    private String email;
+
+    /**
+     * 手机号码
+     */
+    private String mobile;
+
+    /**
+     * 用户性别
+     */
+    private Integer sex;
+
+}

+ 63 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/controller/AuthController.java

@@ -0,0 +1,63 @@
+package cn.newfeifan.mall.ssodemo.controller;
+
+import cn.hutool.core.util.StrUtil;
+import cn.newfeifan.mall.ssodemo.client.OAuth2Client;
+import cn.newfeifan.mall.ssodemo.client.dto.CommonResult;
+import cn.newfeifan.mall.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
+import cn.newfeifan.mall.ssodemo.framework.core.util.SecurityUtils;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+@RestController
+@RequestMapping("/auth")
+public class AuthController {
+
+    @Resource
+    private OAuth2Client oauth2Client;
+
+    /**
+     * 使用 code 访问令牌,获得访问令牌
+     *
+     * @param code 授权码
+     * @param redirectUri 重定向 URI
+     * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
+     */
+    @PostMapping("/login-by-code")
+    public CommonResult<OAuth2AccessTokenRespDTO> loginByCode(@RequestParam("code") String code,
+                                                              @RequestParam("redirectUri") String redirectUri) {
+        return oauth2Client.postAccessToken(code, redirectUri);
+    }
+
+    /**
+     * 使用刷新令牌,获得(刷新)访问令牌
+     *
+     * @param refreshToken 刷新令牌
+     * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
+     */
+    @PostMapping("/refresh-token")
+    public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
+        return oauth2Client.refreshToken(refreshToken);
+    }
+
+    /**
+     * 退出登录
+     *
+     * @param request 请求
+     * @return 成功
+     */
+    @PostMapping("/logout")
+    public CommonResult<Boolean> logout(HttpServletRequest request) {
+        String token = SecurityUtils.obtainAuthorization(request, "Authorization");
+        if (StrUtil.isNotBlank(token)) {
+            return oauth2Client.revokeToken(token);
+        }
+        // 返回成功
+        return new CommonResult<>();
+    }
+
+}

+ 40 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/controller/UserController.java

@@ -0,0 +1,40 @@
+package cn.newfeifan.mall.ssodemo.controller;
+
+import cn.newfeifan.mall.ssodemo.client.UserClient;
+import cn.newfeifan.mall.ssodemo.client.dto.CommonResult;
+import cn.newfeifan.mall.ssodemo.client.dto.user.UserInfoRespDTO;
+import cn.newfeifan.mall.ssodemo.client.dto.user.UserUpdateReqDTO;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/user")
+public class UserController {
+
+    @Resource
+    private UserClient userClient;
+
+    /**
+     * 获得当前登录用户的基本信息
+     *
+     * @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
+     */
+    @GetMapping("/get")
+    public CommonResult<UserInfoRespDTO> getUser() {
+        return userClient.getUser();
+    }
+
+    /**
+     * 更新当前登录用户的昵称
+     *
+     * @param nickname 昵称
+     * @return 成功
+     */
+    @PutMapping("/update")
+    public CommonResult<Boolean> updateUser(@RequestParam("nickname") String nickname) {
+        UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null);
+        return userClient.updateUser(updateReqDTO);
+    }
+
+}

+ 52 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/framework/config/SecurityConfiguration.java

@@ -0,0 +1,52 @@
+package cn.newfeifan.mall.ssodemo.framework.config;
+
+import cn.newfeifan.mall.ssodemo.framework.core.filter.TokenAuthenticationFilter;
+import cn.newfeifan.mall.ssodemo.framework.core.handler.AccessDeniedHandlerImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import javax.annotation.Resource;
+
+@Configuration(proxyBeanMethods = false)
+@EnableWebSecurity
+public class SecurityConfiguration{
+
+    @Resource
+    private TokenAuthenticationFilter tokenAuthenticationFilter;
+
+    @Resource
+    private AccessDeniedHandlerImpl accessDeniedHandler;
+    @Resource
+    private AuthenticationEntryPoint authenticationEntryPoint;
+
+    @Bean
+    protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
+        // 设置 URL 安全权限
+        httpSecurity.csrf().disable() // 禁用 CSRF 保护
+                .authorizeRequests()
+                // 1. 静态资源,可匿名访问
+                .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
+                // 2. 登录相关的接口,可匿名访问
+                .antMatchers("/auth/login-by-code").permitAll()
+                .antMatchers("/auth/refresh-token").permitAll()
+                .antMatchers("/auth/logout").permitAll()
+                // last. 兜底规则,必须认证
+                .and().authorizeRequests()
+                .anyRequest().authenticated();
+
+        // 设置处理器
+        httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler)
+                .authenticationEntryPoint(authenticationEntryPoint);
+
+        // 添加 Token Filter
+        httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+        return httpSecurity.build();
+    }
+
+}

+ 37 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/framework/core/LoginUser.java

@@ -0,0 +1,37 @@
+package cn.newfeifan.mall.ssodemo.framework.core;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 登录用户信息
+ *
+ * @author 非繁源码
+ */
+@Data
+public class LoginUser {
+
+    /**
+     * 用户编号
+     */
+    private Long id;
+    /**
+     * 用户类型
+     */
+    private Integer userType;
+    /**
+     * 租户编号
+     */
+    private Long tenantId;
+    /**
+     * 授权范围
+     */
+    private List<String> scopes;
+
+    /**
+     * 访问令牌
+     */
+    private String accessToken;
+
+}

+ 66 - 0
feifan-example/feifan-sso-demo-by-code/src/main/java/cn/newfeifan/mall/ssodemo/framework/core/filter/TokenAuthenticationFilter.java

@@ -0,0 +1,66 @@
+package cn.newfeifan.mall.ssodemo.framework.core.filter;
+
+import cn.newfeifan.mall.ssodemo.client.OAuth2Client;
+import cn.newfeifan.mall.ssodemo.client.dto.CommonResult;
+import cn.newfeifan.mall.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
+import cn.newfeifan.mall.ssodemo.framework.core.LoginUser;
+import cn.newfeifan.mall.ssodemo.framework.core.util.SecurityUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.annotation.Resource;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Token 过滤器,验证 token 的有效性
+ * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
+ *
+ * @author 非繁源码
+ */
+@Component
+public class TokenAuthenticationFilter extends OncePerRequestFilter {
+
+    @Resource
+    private OAuth2Client oauth2Client;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
+                                    FilterChain filterChain) throws ServletException, IOException {
+        // 1. 获得访问令牌
+        String token = SecurityUtils.obtainAuthorization(request, "Authorization");
+        if (StringUtils.hasText(token)) {
+            // 2. 基于 token 构建登录用户
+            LoginUser loginUser = buildLoginUserByToken(token);
+            // 3. 设置当前用户
+            if (loginUser != null) {
+                SecurityUtils.setLoginUser(loginUser, request);
+            }
+        }
+
+        // 继续过滤链
+        filterChain.doFilter(request, response);
+    }
+
+    private LoginUser buildLoginUserByToken(String token) {
+        try {
+            CommonResult<OAuth2CheckTokenRespDTO> accessTokenResult = oauth2Client.checkToken(token);
+            OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData();
+            if (accessToken == null) {
+                return null;
+            }
+            // 构建登录用户
+            return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
+                    .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
+                    .setAccessToken(accessToken.getAccessToken());
+        } catch (Exception exception) {
+            // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
+            return null;
+        }
+    }
+
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio