Ver Fonte

Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/db

# Conflicts:
#	sql/mysql/ruoyi-vue-pro.sql
YunaiV há 1 ano atrás
pai
commit
c0722b3a19
66 ficheiros alterados com 94 adições e 4147 exclusões
  1. 16 33
      README.md
  2. 1 3
      pom.xml
  3. 10 1509
      sql/mysql/ruoyi-vue-pro.sql
  4. 0 22
      yudao-example/pom.xml
  5. 0 65
      yudao-example/yudao-sso-demo-by-code/pom.xml
  6. 0 13
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java
  7. 0 157
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java
  8. 0 73
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java
  9. 0 28
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java
  10. 0 45
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java
  11. 0 59
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java
  12. 0 97
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java
  13. 0 35
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java
  14. 0 63
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java
  15. 0 40
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java
  16. 0 58
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java
  17. 0 37
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java
  18. 0 66
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java
  19. 0 44
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java
  20. 0 36
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java
  21. 0 103
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java
  22. 0 33
      yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java
  23. 0 2
      yudao-example/yudao-sso-demo-by-code/src/main/resources/application.yaml
  24. 0 61
      yudao-example/yudao-sso-demo-by-code/src/main/resources/static/callback.html
  25. 0 159
      yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html
  26. 0 65
      yudao-example/yudao-sso-demo-by-password/pom.xml
  27. 0 13
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java
  28. 0 127
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java
  29. 0 73
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java
  30. 0 28
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java
  31. 0 45
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java
  32. 0 59
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java
  33. 0 97
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java
  34. 0 35
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java
  35. 0 50
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java
  36. 0 40
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java
  37. 0 58
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java
  38. 0 37
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java
  39. 0 66
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java
  40. 0 44
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java
  41. 0 36
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java
  42. 0 103
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java
  43. 0 33
      yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java
  44. 0 2
      yudao-example/yudao-sso-demo-by-password/src/main/resources/application.yaml
  45. 0 154
      yudao-example/yudao-sso-demo-by-password/src/main/resources/static/index.html
  46. 0 74
      yudao-example/yudao-sso-demo-by-password/src/main/resources/static/login.html
  47. 10 10
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
  48. 2 1
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java
  49. 2 1
      yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java
  50. 3 2
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
  51. 4 3
      yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
  52. 1 1
      yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java
  53. 1 1
      yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java
  54. 2 1
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java
  55. 3 0
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
  56. 7 7
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java
  57. 6 7
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java
  58. 6 7
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java
  59. 6 7
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java
  60. 7 7
      yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java
  61. 0 6
      yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java
  62. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java
  63. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseDbAndRedisIntegrationTest.java
  64. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseRedisIntegrationTest.java
  65. 2 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java
  66. 2 1
      yudao-server/src/test/java/cn/iocoder/yudao/ProjectReactor.java

+ 16 - 33
README.md

@@ -21,6 +21,18 @@
 * 启动文档:<https://doc.iocoder.cn/quick-start/>
 * 视频教程:<https://doc.iocoder.cn/video/>
 
+## 🐰 版本说明
+
+| 版本                                                                  | JDK 8 + Spring Boot 2.7                                                   | JDK 17/21 + Spring Boot 3.2                                                           |
+|---------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
+| 【完整版】[ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk17/) 分支 |
+| 【精简版】[yudao-boot-mini](https://gitee.com/yudaocode/yudao-boot-mini) | [`master`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master/) 分支   | [`master-jdk17`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk17/) 分支   |
+
+* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能
+* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP 等功能
+
+可参考 [《迁移文档》](https://doc.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】
+
 ## 🐯 平台简介
 
 **芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
@@ -31,7 +43,7 @@
 
 ![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
 
-* Java 后端:`master` 分支为 JDK 8 + Spring Boot 2.7.18,`master-jdk21` 分支为 JDK21 + Spring Boot 3.2.0
+* Java 后端:`master` 分支为 JDK 8 + Spring Boot 2.7,`master-jdk17` 分支为 JDK 17/21 + Spring Boot 3.2
 * 管理后台的电脑端:Vue3 提供 `element-plus`、`vben(ant-design-vue)` 两个版本,Vue2 提供 `element-ui` 版本
 * 管理后台的移动端:采用 `uni-app` 方案,一份代码多终端适配,同时支持 APP、小程序、H5!
 * 后端采用 Spring Boot 多模块架构、MySQL + MyBatis Plus、Redis + Redisson
@@ -72,28 +84,6 @@
 | [yudao-ui-admin-uniapp](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-admin-uniapp/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-admin-uniapp) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-admin-uniapp.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-admin-uniapp) | 基于 Vue2 + element-ui 实现的管理后台           |
 | [yudao-ui-go-view](https://gitee.com/yudaocode/yudao-ui-go-view)           | [![Gitee star](https://gitee.com/yudaocode/yudao-ui-go-view/badge/star.svg?theme=white)](https://gitee.com/yudaocode/yudao-ui-go-view) [![GitHub stars](https://img.shields.io/github/stars/yudaocode/yudao-ui-go-view.svg?style=social&label=Stars)](https://github.com/yudaocode/yudao-ui-go-view)                     | 基于 Vue3 + naive-ui 实现的大屏报表             |
 
-## 🐰 分支说明
-
-### ⬅️ 完整版
-
-【完整版】包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能
-
-* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master` 分支
-* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/zhijiantianya/ruoyi-vue-pro> 的 `master-jdk21` 分支
-
-两个分支的功能是一致的,可以放心使用!
-
-### ➡️️ 精简版
-
-【精简版】只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM 等功能
-
-* JDK 8 + Spring Boot 2.7.18 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master` 分支
-* JDK 21 + Spring Boot 3.2.0 版本:<https://gitee.com/yudaocode/yudao-boot-mini> 的 `master-jdk21` 分支
-
-如果你想把【完整版】的功能,迁移到【精简版】,可以参考 [《迁移功能到精简版》](https://doc.iocoder.cn/migrate-module/) 文档。
-
-如果你想把【完整版】的功能,迁移到【精简版】,可以参考 [《迁移功能到精简版》](https://doc.iocoder.cn/migrate-module/) 文档。
-
 ## 😎 开源协议
 
 **为什么推荐使用本项目?**
@@ -120,16 +110,9 @@
 
 ![功能分层](/.image/common/ruoyi-vue-pro-biz.png)
 
-* 系统功能
-* 基础设施
-* 工作流程
-* 支付系统
-* 会员中心
-* 数据报表
-* 商城系统
-* 微信公众号
-* ERP 系统
-* CRM 系统
+* 通用模块(必选):系统功能、基础设施
+* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
+* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号
 
 > 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
 >

+ 1 - 3
pom.xml

@@ -23,8 +23,6 @@
 <!--        <module>yudao-module-mall</module>-->
 <!--        <module>yudao-module-crm</module>-->
 <!--        <module>yudao-module-erp</module>-->
-        <!-- 示例项目 -->
-<!--        <module>yudao-example</module>-->
     </modules>
 
     <name>${project.artifactId}</name>
@@ -34,7 +32,7 @@
     <properties>
         <revision>2.0.1-snapshot</revision>
         <!-- Maven 相关 -->
-        <java.version>21</java.version>
+        <java.version>17</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>
         <maven.compiler.target>${java.version}</maven.compiler.target>
         <maven-surefire-plugin.version>3.2.2</maven-surefire-plugin.version>

Diff do ficheiro suprimidas por serem muito extensas
+ 10 - 1509
sql/mysql/ruoyi-vue-pro.sql


+ 0 - 22
yudao-example/pom.xml

@@ -1,22 +0,0 @@
-<?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>
-
-    <!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
-
-    <groupId>cn.iocoder.boot</groupId>
-    <artifactId>yudao-example</artifactId>
-    <version>1.0.0-snapshot</version>
-    <packaging>pom</packaging>
-    <modules>
-        <module>yudao-sso-demo-by-code</module>
-        <module>yudao-sso-demo-by-password</module>
-    </modules>
-
-    <name>${project.artifactId}</name>
-    <description>提供各种示例,例如说:SSO 单点登录</description>
-    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
-
-</project>

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

@@ -1,65 +0,0 @@
-<?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>
-
-    <!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
-
-    <groupId>cn.iocoder.boot</groupId>
-    <artifactId>yudao-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>21</maven.compiler.source>
-        <maven.compiler.target>21</maven.compiler.target>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <!-- 统一依赖管理 -->
-        <spring.boot.version>3.2.0</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>

+ 0 - 13
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java

@@ -1,13 +0,0 @@
-package cn.iocoder.yudao.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);
-    }
-
-}

+ 0 - 157
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java

@@ -1,157 +0,0 @@
-package cn.iocoder.yudao.ssodemo.client;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
-import cn.iocoder.yudao.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 = "yudao-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);
-    }
-
-}

+ 0 - 73
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java

@@ -1,73 +0,0 @@
-package cn.iocoder.yudao.ssodemo.client;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
-import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
-import cn.iocoder.yudao.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());
-    }
-}

+ 0 - 28
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

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

@@ -1,45 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

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

@@ -1,59 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

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

@@ -1,97 +0,0 @@
-package cn.iocoder.yudao.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;
-
-    }
-
-}

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

@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

+ 0 - 63
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java

@@ -1,63 +0,0 @@
-package cn.iocoder.yudao.ssodemo.controller;
-
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
-import cn.iocoder.yudao.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 jakarta.annotation.Resource;
-import jakarta.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<>();
-    }
-
-}

+ 0 - 40
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java

@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.ssodemo.controller;
-
-import cn.iocoder.yudao.ssodemo.client.UserClient;
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.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);
-    }
-
-}

+ 0 - 58
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java

@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.config;
-
-import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
-import cn.iocoder.yudao.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.Customizer;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.SecurityFilterChain;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-
-import jakarta.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
-                // 开启跨域
-                .cors(Customizer.withDefaults())
-                // CSRF 禁用,因为不使用 Session
-                .csrf(AbstractHttpConfigurer::disable)
-                // 一堆自定义的 Spring Security 处理器
-                .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
-                        .accessDeniedHandler(accessDeniedHandler));
-
-        // 设置每个请求的权限
-        httpSecurity.authorizeHttpRequests(c -> c
-                        // 1. 静态资源,可匿名访问
-                        .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
-                        // 2. 登录相关的接口,可匿名访问
-                        .requestMatchers("/auth/login-by-code").permitAll()
-                        .requestMatchers("/auth/refresh-token").permitAll()
-                        .requestMatchers("/auth/logout").permitAll())
-                // 3. 兜底规则,必须认证
-                .authorizeHttpRequests(c -> c.anyRequest().authenticated());
-
-        // 添加 Token Filter
-        httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
-        return httpSecurity.build();
-    }
-
-}

+ 0 - 37
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

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

@@ -1,66 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.filter;
-
-import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
-import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
-import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import jakarta.annotation.Resource;
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.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;
-        }
-    }
-
-}

+ 0 - 44
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java

@@ -1,44 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.handler;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
-import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.access.AccessDeniedException;
-import org.springframework.security.web.access.AccessDeniedHandler;
-import org.springframework.security.web.access.ExceptionTranslationFilter;
-import org.springframework.stereotype.Component;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
- *
- * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
- *
- * @author 芋道源码
- */
-@Component
-@SuppressWarnings("JavadocReference")
-@Slf4j
-public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
-
-    @Override
-    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
-            throws IOException, ServletException {
-        // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
-        log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
-                SecurityUtils.getLoginUserId(), e);
-        // 返回 403
-        CommonResult<Object> result = new CommonResult<>();
-        result.setCode(HttpStatus.FORBIDDEN.value());
-        result.setMsg("没有该操作权限");
-        ServletUtils.writeJSON(response, result);
-    }
-
-}

+ 0 - 36
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java

@@ -1,36 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.handler;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.access.ExceptionTranslationFilter;
-import org.springframework.stereotype.Component;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-/**
- * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
- *
- * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
- */
-@Component
-@Slf4j
-@SuppressWarnings("JavadocReference") // 忽略文档引用报错
-public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
-
-    @Override
-    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
-        log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
-        // 返回 401
-        CommonResult<Object> result = new CommonResult<>();
-        result.setCode(HttpStatus.UNAUTHORIZED.value());
-        result.setMsg("账号未登录");
-        ServletUtils.writeJSON(response, result);
-    }
-
-}

+ 0 - 103
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java

@@ -1,103 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.util;
-
-import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
-import org.springframework.lang.Nullable;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.util.StringUtils;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.Collections;
-
-/**
- * 安全服务工具类
- *
- * @author 芋道源码
- */
-public class SecurityUtils {
-
-    public static final String AUTHORIZATION_BEARER = "Bearer";
-
-    private SecurityUtils() {}
-
-    /**
-     * 从请求中,获得认证 Token
-     *
-     * @param request 请求
-     * @param header 认证 Token 对应的 Header 名字
-     * @return 认证 Token
-     */
-    public static String obtainAuthorization(HttpServletRequest request, String header) {
-        String authorization = request.getHeader(header);
-        if (!StringUtils.hasText(authorization)) {
-            return null;
-        }
-        int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
-        if (index == -1) { // 未找到
-            return null;
-        }
-        return authorization.substring(index + 7).trim();
-    }
-
-    /**
-     * 获得当前认证信息
-     *
-     * @return 认证信息
-     */
-    public static Authentication getAuthentication() {
-        SecurityContext context = SecurityContextHolder.getContext();
-        if (context == null) {
-            return null;
-        }
-        return context.getAuthentication();
-    }
-
-    /**
-     * 获取当前用户
-     *
-     * @return 当前用户
-     */
-    @Nullable
-    public static LoginUser getLoginUser() {
-        Authentication authentication = getAuthentication();
-        if (authentication == null) {
-            return null;
-        }
-        return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
-    }
-
-    /**
-     * 获得当前用户的编号,从上下文中
-     *
-     * @return 用户编号
-     */
-    @Nullable
-    public static Long getLoginUserId() {
-        LoginUser loginUser = getLoginUser();
-        return loginUser != null ? loginUser.getId() : null;
-    }
-
-    /**
-     * 设置当前用户
-     *
-     * @param loginUser 登录用户
-     * @param request 请求
-     */
-    public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
-        // 创建 Authentication,并设置到上下文
-        Authentication authentication = buildAuthentication(loginUser, request);
-        SecurityContextHolder.getContext().setAuthentication(authentication);
-    }
-
-    private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
-        // 创建 UsernamePasswordAuthenticationToken 对象
-        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
-                loginUser, null, Collections.emptyList());
-        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
-        return authenticationToken;
-    }
-
-}

+ 0 - 33
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java

@@ -1,33 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.util;
-
-import cn.hutool.extra.servlet.JakartaServletUtil;
-import cn.hutool.extra.servlet.ServletUtil;
-import cn.hutool.json.JSONUtil;
-import org.springframework.http.MediaType;
-
-import jakarta.servlet.http.HttpServletResponse;
-
-/**
- * 客户端工具类
- *
- * @author 芋道源码
- */
-public class ServletUtils {
-
-    /**
-     * 返回 JSON 字符串
-     *
-     * @param response 响应
-     * @param object 对象,会序列化成 JSON 字符串
-     */
-    @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
-    public static void writeJSON(HttpServletResponse response, Object object) {
-        String content = JSONUtil.toJsonStr(object);
-        JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
-    }
-
-    public static void write(HttpServletResponse response, String text, String contentType) {
-        JakartaServletUtil.write(response, text, contentType);
-    }
-
-}

+ 0 - 2
yudao-example/yudao-sso-demo-by-code/src/main/resources/application.yaml

@@ -1,2 +0,0 @@
-server:
-  port: 18080

+ 0 - 61
yudao-example/yudao-sso-demo-by-code/src/main/resources/static/callback.html

@@ -1,61 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-	<meta charset="UTF-8">
-	<title>SSO 授权后的回调页</title>
-	<!-- jQuery:操作 dom、发起请求等 -->
-	<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
-	<!-- 工具类 -->
-	<script type="application/javascript">
-    (function ($) {
-      /**
-       * 获得 URL 的指定参数的值
-       *
-       * @param name 参数名
-       * @returns 参数值
-       */
-      $.getUrlParam = function (name) {
-        const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
-        const r = window.location.search.substr(1).match(reg);
-        if (r != null) return unescape(r[2]); return null;
-      }
-    })(jQuery);
-	</script>
-
-	<script type="application/javascript">
-    $(function () {
-      // 获得 code 授权码
-      const code = $.getUrlParam('code');
-      if (!code) {
-        alert('获取不到 code 参数,请排查!')
-        return;
-      }
-
-      // 提交
-      const redirectUri = 'http://127.0.0.1:18080/callback.html'; // 需要修改成,你回调的地址,就是在 index.html 拼接的 redirectUri
-      $.ajax({
-        url:  "http://127.0.0.1:18080/auth/login-by-code?code=" + code
-          + '&redirectUri=' + redirectUri,
-        method: 'POST',
-        success: function( result ) {
-          if (result.code !== 0) {
-            alert('获得访问令牌失败,原因:' + result.msg)
-            return;
-          }
-          alert('获得访问令牌成功!点击确认,跳转回首页')
-
-          // 设置到 localStorage 中
-          localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
-          localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
-
-          // 跳转回首页
-          window.location.href = '/index.html';
-        }
-      })
-    })
-	</script>
-</head>
-<body>
-正在使用 code 授权码,进行 accessToken 访问令牌的获取
-</body>
-</html>

+ 0 - 159
yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html

@@ -1,159 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-	<meta charset="UTF-8">
-	<title>首页</title>
-	<!-- jQuery:操作 dom、发起请求等 -->
-	<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
-
-	<script type="application/javascript">
-
-    /**
-     * 跳转单点登录
-     */
-    function ssoLogin() {
-      const clientId = 'yudao-sso-demo-by-code'; // 可以改写成,你的 clientId
-      const redirectUri = encodeURIComponent('http://127.0.0.1:18080/callback.html'); // 注意,需要使用 encodeURIComponent 编码地址
-      const responseType = 'code'; // 1)授权码模式,对应 code;2)简化模式,对应 token
-      window.location.href = 'http://127.0.0.1:1024/sso?client_id=' + clientId
-        + '&redirect_uri=' + redirectUri
-        + '&response_type=' + responseType;
-    }
-
-    /**
-     * 修改昵称
-     */
-    function updateNickname() {
-      const nickname = prompt("请输入新的昵称", "");
-      if (!nickname) {
-        return;
-      }
-      // 更新用户的昵称
-      const accessToken = localStorage.getItem('ACCESS-TOKEN');
-      $.ajax({
-        url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
-        method: 'PUT',
-        headers: {
-          'Authorization': 'Bearer ' + accessToken
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('更新昵称失败,原因:' + result.msg)
-            return;
-          }
-          alert('更新昵称成功!');
-          $('#nicknameSpan').html(nickname);
-        }
-      });
-    }
-
-    /**
-     * 刷新令牌
-     */
-    function refreshToken() {
-      const refreshToken = localStorage.getItem('REFRESH-TOKEN');
-      if (!refreshToken) {
-        alert("获取不到刷新令牌");
-        return;
-      }
-      $.ajax({
-        url: "http://127.0.0.1:18080/auth/refresh-token?refreshToken=" + refreshToken,
-        method: 'POST',
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('刷新访问令牌失败,原因:' + result.msg)
-            return;
-          }
-          alert('更新访问令牌成功!');
-          $('#accessTokenSpan').html(result.data.access_token);
-
-          // 设置到 localStorage 中
-          localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
-          localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
-        }
-      });
-    }
-
-    /**
-     * 登出,删除访问令牌
-     */
-    function logout() {
-      const accessToken = localStorage.getItem('ACCESS-TOKEN');
-      if (!accessToken) {
-        location.reload();
-        return;
-      }
-      $.ajax({
-        url: "http://127.0.0.1:18080/auth/logout",
-        method: 'POST',
-        headers: {
-          'Authorization': 'Bearer ' + accessToken
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('退出登录失败,原因:' + result.msg)
-            return;
-          }
-          alert('退出登录成功!');
-          // 删除 localStorage 中
-          localStorage.removeItem('ACCESS-TOKEN');
-          localStorage.removeItem('REFRESH-TOKEN');
-
-          location.reload();
-        }
-      });
-    }
-
-    $(function () {
-      const accessToken = localStorage.getItem('ACCESS-TOKEN');
-      // 情况一:未登录
-      if (!accessToken) {
-        $('#noLoginDiv').css("display", "block");
-        return;
-      }
-
-      // 情况二:已登录
-      $('#yesLoginDiv').css("display", "block");
-      $('#accessTokenSpan').html(accessToken);
-      // 获得登录用户的信息
-      $.ajax({
-        url: "http://127.0.0.1:18080/user/get",
-        method: 'GET',
-        headers: {
-          'Authorization': 'Bearer ' + accessToken
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('获得个人信息失败,原因:' + result.msg)
-            return;
-          }
-          $('#nicknameSpan').html(result.data.nickname);
-        }
-      });
-    })
-	</script>
-</head>
-<body>
-<!-- 情况一:未登录:1)跳转 ruoyi-vue-pro 的 SSO 登录页 -->
-<div id="noLoginDiv" style="display: none">
-	您未登录,点击 <a href="#" onclick="ssoLogin()">跳转 </a> SSO 单点登录
-</div>
-
-<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
-<div id="yesLoginDiv" style="display: none">
-	您已登录!<button onclick="logout()">退出登录</button> <br />
-	昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
-	访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
-</div>
-</body>
-<style>
-    body { /** 页面居中 */
-        border-radius: 20px;
-        height: 350px;
-        position: absolute;
-        left: 50%;
-        top: 50%;
-        transform: translate(-50%,-50%);
-    }
-</style>
-</html>

+ 0 - 65
yudao-example/yudao-sso-demo-by-password/pom.xml

@@ -1,65 +0,0 @@
-<?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>
-
-    <!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
-
-    <groupId>cn.iocoder.boot</groupId>
-    <artifactId>yudao-sso-demo-by-password</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>21</maven.compiler.source>
-        <maven.compiler.target>21</maven.compiler.target>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <!-- 统一依赖管理 -->
-        <spring.boot.version>3.2.0</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>

+ 0 - 13
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java

@@ -1,13 +0,0 @@
-package cn.iocoder.yudao.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);
-    }
-
-}

+ 0 - 127
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java

@@ -1,127 +0,0 @@
-package cn.iocoder.yudao.ssodemo.client;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
-import cn.iocoder.yudao.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 = "yudao-sso-demo-by-password";
-    private static final String CLIENT_SECRET = "test";
-
-
-//    @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
-    private final RestTemplate restTemplate = new RestTemplate();
-
-    /**
-     * 校验访问令牌,并返回它的基本信息
-     *
-     * @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);
-    }
-
-}

+ 0 - 73
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java

@@ -1,73 +0,0 @@
-package cn.iocoder.yudao.ssodemo.client;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
-import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
-import cn.iocoder.yudao.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());
-    }
-}

+ 0 - 28
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

+ 0 - 45
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java

@@ -1,45 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

+ 0 - 59
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java

@@ -1,59 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

+ 0 - 97
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java

@@ -1,97 +0,0 @@
-package cn.iocoder.yudao.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;
-
-    }
-
-}

+ 0 - 35
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java

@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

+ 0 - 50
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java

@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.ssodemo.controller;
-
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
-import cn.iocoder.yudao.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 jakarta.annotation.Resource;
-import jakarta.servlet.http.HttpServletRequest;
-
-@RestController
-@RequestMapping("/auth")
-public class AuthController {
-
-    @Resource
-    private OAuth2Client oauth2Client;
-
-    /**
-     * 使用刷新令牌,获得(刷新)访问令牌
-     *
-     * @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<>();
-    }
-
-}

+ 0 - 40
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java

@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.ssodemo.controller;
-
-import cn.iocoder.yudao.ssodemo.client.UserClient;
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
-import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
-import org.springframework.web.bind.annotation.*;
-
-import jakarta.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);
-    }
-
-}

+ 0 - 58
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java

@@ -1,58 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.config;
-
-import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
-import cn.iocoder.yudao.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.Customizer;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.SecurityFilterChain;
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-
-import jakarta.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
-                // 开启跨域
-                .cors(Customizer.withDefaults())
-                // CSRF 禁用,因为不使用 Session
-                .csrf(AbstractHttpConfigurer::disable)
-                // 一堆自定义的 Spring Security 处理器
-                .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
-                        .accessDeniedHandler(accessDeniedHandler));
-
-        // 设置每个请求的权限
-        httpSecurity.authorizeHttpRequests(c -> c
-                // 1. 静态资源,可匿名访问
-                .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
-                // 2. 登录相关的接口,可匿名访问
-                .requestMatchers("/auth/login-by-code").permitAll()
-                .requestMatchers("/auth/refresh-token").permitAll()
-                .requestMatchers("/auth/logout").permitAll())
-                // 3. 兜底规则,必须认证
-                .authorizeHttpRequests(c -> c.anyRequest().authenticated());
-
-        // 添加 Token Filter
-        httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
-        return httpSecurity.build();
-    }
-
-}

+ 0 - 37
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java

@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.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;
-
-}

+ 0 - 66
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java

@@ -1,66 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.filter;
-
-import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
-import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
-import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-import org.springframework.web.filter.OncePerRequestFilter;
-
-import jakarta.annotation.Resource;
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.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;
-        }
-    }
-
-}

+ 0 - 44
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java

@@ -1,44 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.handler;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
-import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.access.AccessDeniedException;
-import org.springframework.security.web.access.AccessDeniedHandler;
-import org.springframework.security.web.access.ExceptionTranslationFilter;
-import org.springframework.stereotype.Component;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
- *
- * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
- *
- * @author 芋道源码
- */
-@Component
-@SuppressWarnings("JavadocReference")
-@Slf4j
-public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
-
-    @Override
-    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
-            throws IOException, ServletException {
-        // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
-        log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
-                SecurityUtils.getLoginUserId(), e);
-        // 返回 403
-        CommonResult<Object> result = new CommonResult<>();
-        result.setCode(HttpStatus.FORBIDDEN.value());
-        result.setMsg("没有该操作权限");
-        ServletUtils.writeJSON(response, result);
-    }
-
-}

+ 0 - 36
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java

@@ -1,36 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.handler;
-
-import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
-import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.AuthenticationEntryPoint;
-import org.springframework.security.web.access.ExceptionTranslationFilter;
-import org.springframework.stereotype.Component;
-
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-/**
- * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
- *
- * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
- */
-@Component
-@Slf4j
-@SuppressWarnings("JavadocReference") // 忽略文档引用报错
-public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
-
-    @Override
-    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
-        log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
-        // 返回 401
-        CommonResult<Object> result = new CommonResult<>();
-        result.setCode(HttpStatus.UNAUTHORIZED.value());
-        result.setMsg("账号未登录");
-        ServletUtils.writeJSON(response, result);
-    }
-
-}

+ 0 - 103
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java

@@ -1,103 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.util;
-
-import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
-import org.springframework.lang.Nullable;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
-import org.springframework.util.StringUtils;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.Collections;
-
-/**
- * 安全服务工具类
- *
- * @author 芋道源码
- */
-public class SecurityUtils {
-
-    public static final String AUTHORIZATION_BEARER = "Bearer";
-
-    private SecurityUtils() {}
-
-    /**
-     * 从请求中,获得认证 Token
-     *
-     * @param request 请求
-     * @param header 认证 Token 对应的 Header 名字
-     * @return 认证 Token
-     */
-    public static String obtainAuthorization(HttpServletRequest request, String header) {
-        String authorization = request.getHeader(header);
-        if (!StringUtils.hasText(authorization)) {
-            return null;
-        }
-        int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
-        if (index == -1) { // 未找到
-            return null;
-        }
-        return authorization.substring(index + 7).trim();
-    }
-
-    /**
-     * 获得当前认证信息
-     *
-     * @return 认证信息
-     */
-    public static Authentication getAuthentication() {
-        SecurityContext context = SecurityContextHolder.getContext();
-        if (context == null) {
-            return null;
-        }
-        return context.getAuthentication();
-    }
-
-    /**
-     * 获取当前用户
-     *
-     * @return 当前用户
-     */
-    @Nullable
-    public static LoginUser getLoginUser() {
-        Authentication authentication = getAuthentication();
-        if (authentication == null) {
-            return null;
-        }
-        return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
-    }
-
-    /**
-     * 获得当前用户的编号,从上下文中
-     *
-     * @return 用户编号
-     */
-    @Nullable
-    public static Long getLoginUserId() {
-        LoginUser loginUser = getLoginUser();
-        return loginUser != null ? loginUser.getId() : null;
-    }
-
-    /**
-     * 设置当前用户
-     *
-     * @param loginUser 登录用户
-     * @param request 请求
-     */
-    public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
-        // 创建 Authentication,并设置到上下文
-        Authentication authentication = buildAuthentication(loginUser, request);
-        SecurityContextHolder.getContext().setAuthentication(authentication);
-    }
-
-    private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
-        // 创建 UsernamePasswordAuthenticationToken 对象
-        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
-                loginUser, null, Collections.emptyList());
-        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
-        return authenticationToken;
-    }
-
-}

+ 0 - 33
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java

@@ -1,33 +0,0 @@
-package cn.iocoder.yudao.ssodemo.framework.core.util;
-
-import cn.hutool.extra.servlet.JakartaServletUtil;
-import cn.hutool.extra.servlet.ServletUtil;
-import cn.hutool.json.JSONUtil;
-import org.springframework.http.MediaType;
-
-import jakarta.servlet.http.HttpServletResponse;
-
-/**
- * 客户端工具类
- *
- * @author 芋道源码
- */
-public class ServletUtils {
-
-    /**
-     * 返回 JSON 字符串
-     *
-     * @param response 响应
-     * @param object 对象,会序列化成 JSON 字符串
-     */
-    @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
-    public static void writeJSON(HttpServletResponse response, Object object) {
-        String content = JSONUtil.toJsonStr(object);
-        JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
-    }
-
-    public static void write(HttpServletResponse response, String text, String contentType) {
-        JakartaServletUtil.write(response, text, contentType);
-    }
-
-}

+ 0 - 2
yudao-example/yudao-sso-demo-by-password/src/main/resources/application.yaml

@@ -1,2 +0,0 @@
-server:
-  port: 18080

+ 0 - 154
yudao-example/yudao-sso-demo-by-password/src/main/resources/static/index.html

@@ -1,154 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-	<meta charset="UTF-8">
-	<title>首页</title>
-	<!-- jQuery:操作 dom、发起请求等 -->
-	<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
-
-	<script type="application/javascript">
-
-    /**
-     * 跳转单点登录
-     */
-    function passwordLogin() {
-      window.location.href = '/login.html'
-    }
-
-    /**
-     * 修改昵称
-     */
-    function updateNickname() {
-      const nickname = prompt("请输入新的昵称", "");
-      if (!nickname) {
-        return;
-      }
-      // 更新用户的昵称
-      const accessToken = localStorage.getItem('ACCESS-TOKEN');
-      $.ajax({
-        url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
-        method: 'PUT',
-        headers: {
-          'Authorization': 'Bearer ' + accessToken
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('更新昵称失败,原因:' + result.msg)
-            return;
-          }
-          alert('更新昵称成功!');
-          $('#nicknameSpan').html(nickname);
-        }
-      });
-    }
-
-    /**
-     * 刷新令牌
-     */
-    function refreshToken() {
-      const refreshToken = localStorage.getItem('REFRESH-TOKEN');
-      if (!refreshToken) {
-        alert("获取不到刷新令牌");
-        return;
-      }
-      $.ajax({
-        url: "http://127.0.0.1:18080/auth/refresh-token?refreshToken=" + refreshToken,
-        method: 'POST',
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('刷新访问令牌失败,原因:' + result.msg)
-            return;
-          }
-          alert('更新访问令牌成功!');
-          $('#accessTokenSpan').html(result.data.access_token);
-
-          // 设置到 localStorage 中
-          localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
-          localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
-        }
-      });
-    }
-
-    /**
-     * 登出,删除访问令牌
-     */
-    function logout() {
-      const accessToken = localStorage.getItem('ACCESS-TOKEN');
-      if (!accessToken) {
-        location.reload();
-        return;
-      }
-      $.ajax({
-        url: "http://127.0.0.1:18080/auth/logout",
-        method: 'POST',
-        headers: {
-          'Authorization': 'Bearer ' + accessToken
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('退出登录失败,原因:' + result.msg)
-            return;
-          }
-          alert('退出登录成功!');
-          // 删除 localStorage 中
-          localStorage.removeItem('ACCESS-TOKEN');
-          localStorage.removeItem('REFRESH-TOKEN');
-
-          location.reload();
-        }
-      });
-    }
-
-    $(function () {
-      const accessToken = localStorage.getItem('ACCESS-TOKEN');
-      // 情况一:未登录
-      if (!accessToken) {
-        $('#noLoginDiv').css("display", "block");
-        return;
-      }
-
-      // 情况二:已登录
-      $('#yesLoginDiv').css("display", "block");
-      $('#accessTokenSpan').html(accessToken);
-      // 获得登录用户的信息
-      $.ajax({
-        url: "http://127.0.0.1:18080/user/get",
-        method: 'GET',
-        headers: {
-          'Authorization': 'Bearer ' + accessToken
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('获得个人信息失败,原因:' + result.msg)
-            return;
-          }
-          $('#nicknameSpan').html(result.data.nickname);
-        }
-      });
-    })
-	</script>
-</head>
-<body>
-<!-- 情况一:未登录:1)跳转 ruoyi-vue-pro 的 SSO 登录页 -->
-<div id="noLoginDiv" style="display: none">
-	您未登录,点击 <a href="#" onclick="passwordLogin()">跳转 </a> 账号密码登录
-</div>
-
-<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
-<div id="yesLoginDiv" style="display: none">
-	您已登录!<button onclick="logout()">退出登录</button> <br />
-	昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
-	访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
-</div>
-</body>
-<style>
-    body { /** 页面居中 */
-        border-radius: 20px;
-        height: 350px;
-        position: absolute;
-        left: 50%;
-        top: 50%;
-        transform: translate(-50%,-50%);
-    }
-</style>
-</html>

+ 0 - 74
yudao-example/yudao-sso-demo-by-password/src/main/resources/static/login.html

@@ -1,74 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-	<meta charset="UTF-8">
-	<title>登录</title>
-	<!-- jQuery:操作 dom、发起请求等 -->
-	<script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
-
-	<script type="application/javascript">
-
-    /**
-     * 账号密码登录
-     */
-    function login() {
-      const clientId = 'yudao-sso-demo-by-password'; // 可以改写成,你的 clientId
-      const clientSecret = 'test'; // 可以改写成,你的 clientSecret
-      const grantType = 'password'; // 密码模式
-
-      // 账号 + 密码
-      const username = $('#username').val();
-      const password = $('#password').val();
-      if (username.length === 0 || password.length === 0) {
-        alert('账号或密码未输入');
-        return;
-      }
-
-      // 发起请求
-      $.ajax({
-        url: "http://127.0.0.1:48080/admin-api/system/oauth2/token?"
-          // 客户端
-          + "client_id=" + clientId
-          + "&client_secret=" + clientSecret
-          // 密码模式的参数
-          + "&grant_type=" + grantType
-          + "&username=" + username
-          + "&password=" + password
-          + '&scope=user.read user.write',
-        method: 'POST',
-        headers: {
-          'tenant-id': '1', // 多租户编号,写死
-        },
-        success: function (result) {
-          if (result.code !== 0) {
-            alert('登录失败,原因:' + result.msg)
-            return;
-          }
-          // 设置到 localStorage 中
-          localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
-          localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
-
-          // 提示登录成功
-          alert('登录成功!点击确认,跳转回首页');
-          window.location.href = '/index.html';
-        }
-      });
-    }
-	</script>
-</head>
-<body>
-账号:<input id="username" value="admin" /> <br />
-密码:<input id="password" value="admin123" > <br />
-<button style="float: right; margin-top: 5px;" onclick="login()">登录</button>
-</body>
-<style>
-    body { /** 页面居中 */
-        border-radius: 20px;
-        height: 350px;
-        position: absolute;
-        left: 50%;
-        top: 50%;
-        transform: translate(-50%,-50%);
-    }
-</style>
-</html>

+ 10 - 10
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java

@@ -227,27 +227,27 @@ public class LocalDateTimeUtils {
         // 2. 循环,生成时间范围
         List<LocalDateTime[]> timeRanges = new ArrayList<>();
         switch (intervalEnum) {
-            case DateIntervalEnum.DAY:
+            case DAY:
                 while (startTime.isBefore(endTime)) {
                     timeRanges.add(new LocalDateTime[]{startTime, startTime.plusDays(1).minusNanos(1)});
                     startTime = startTime.plusDays(1);
                 }
                 break;
-            case DateIntervalEnum.WEEK:
+            case WEEK:
                 while (startTime.isBefore(endTime)) {
                     LocalDateTime endOfWeek = startTime.with(DayOfWeek.SUNDAY).plusDays(1).minusNanos(1);
                     timeRanges.add(new LocalDateTime[]{startTime, endOfWeek});
                     startTime = endOfWeek.plusNanos(1);
                 }
                 break;
-            case DateIntervalEnum.MONTH:
+            case MONTH:
                 while (startTime.isBefore(endTime)) {
                     LocalDateTime endOfMonth = startTime.with(TemporalAdjusters.lastDayOfMonth()).plusDays(1).minusNanos(1);
                     timeRanges.add(new LocalDateTime[]{startTime, endOfMonth});
                     startTime = endOfMonth.plusNanos(1);
                 }
                 break;
-            case DateIntervalEnum.QUARTER:
+            case QUARTER:
                 while (startTime.isBefore(endTime)) {
                     int quarterOfYear = getQuarterOfYear(startTime);
                     LocalDateTime quarterEnd = quarterOfYear == 4
@@ -257,7 +257,7 @@ public class LocalDateTimeUtils {
                     startTime = quarterEnd.plusNanos(1);
                 }
                 break;
-            case DateIntervalEnum.YEAR:
+            case YEAR:
                 while (startTime.isBefore(endTime)) {
                     LocalDateTime endOfYear = startTime.with(TemporalAdjusters.lastDayOfYear()).plusDays(1).minusNanos(1);
                     timeRanges.add(new LocalDateTime[]{startTime, endOfYear});
@@ -290,16 +290,16 @@ public class LocalDateTimeUtils {
 
         // 2. 循环,生成时间范围
         switch (intervalEnum) {
-            case DateIntervalEnum.DAY:
+            case DAY:
                 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN);
-            case DateIntervalEnum.WEEK:
+            case WEEK:
                 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_DATE_PATTERN)
                         + StrUtil.format("(第 {} 周)", LocalDateTimeUtil.weekOfYear(startTime));
-            case DateIntervalEnum.MONTH:
+            case MONTH:
                 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_MONTH_PATTERN);
-            case DateIntervalEnum.QUARTER:
+            case QUARTER:
                 return StrUtil.format("{}-Q{}", startTime.getYear(), getQuarterOfYear(startTime));
-            case DateIntervalEnum.YEAR:
+            case YEAR:
                 return LocalDateTimeUtil.format(startTime, DatePattern.NORM_YEAR_PATTERN);
             default:
                 throw new IllegalArgumentException("Invalid interval: " + interval);

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.mybatis.config;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler;
 import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
 import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
 import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
 import com.baomidou.mybatisplus.extension.incrementer.*;
@@ -20,7 +21,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
  *
  * @author 芋道源码
  */
-@AutoConfiguration
+@AutoConfiguration(before = MybatisPlusAutoConfiguration.class) // 目的:先于 MyBatis Plus 自动配置,避免 @MapperScan 可能扫描不到 Mapper 打印 warn 日志
 @MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class,
         lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载,目前仅用于单元测试
 public class YudaoMybatisAutoConfiguration {

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.redis.config;
 import cn.hutool.core.util.ReflectUtil;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.redisson.spring.starter.RedissonAutoConfigurationV2;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.context.annotation.Bean;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
@@ -12,7 +13,7 @@ import org.springframework.data.redis.serializer.RedisSerializer;
 /**
  * Redis 配置类
  */
-@AutoConfiguration
+@AutoConfiguration(before = RedissonAutoConfigurationV2.class) // 目的:使用自己定义的 RedisTemplate Bean
 public class YudaoRedisAutoConfiguration {
 
     /**

+ 3 - 2
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java

@@ -10,8 +10,10 @@ import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService
 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
 import cn.iocoder.yudao.module.system.api.oauth2.OAuth2TokenApi;
 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
+import jakarta.annotation.Resource;
 import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureOrder;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -20,8 +22,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.access.AccessDeniedHandler;
 
-import jakarta.annotation.Resource;
-
 /**
  * Spring Security 自动配置类,主要用于相关组件的配置
  *
@@ -31,6 +31,7 @@ import jakarta.annotation.Resource;
  * @author 芋道源码
  */
 @AutoConfiguration
+@AutoConfigureOrder(-1) // 目的:先于 Spring Security 自动配置,避免一键改包后,org.* 基础包无法生效
 @EnableConfigurationProperties(SecurityProperties.class)
 public class YudaoSecurityAutoConfiguration {
 

+ 4 - 3
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java

@@ -5,14 +5,16 @@ import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter
 import cn.iocoder.yudao.framework.web.config.WebProperties;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
+import jakarta.annotation.Resource;
+import jakarta.annotation.security.PermitAll;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.AutoConfigureOrder;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.Customizer;
 import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
@@ -27,8 +29,6 @@ import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
 import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
 
-import jakarta.annotation.Resource;
-import jakarta.annotation.security.PermitAll;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -39,6 +39,7 @@ import java.util.Set;
  * @author 芋道源码
  */
 @AutoConfiguration
+@AutoConfigureOrder(-1) // 目的:先于 Spring Security 自动配置,避免一键改包后,org.* 基础包无法生效
 @EnableMethodSecurity(securedEnabled = true)
 public class YudaoWebSecurityConfigurerAdapter {
 

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseDbAndRedisUnitTest.java

@@ -43,7 +43,7 @@ public class BaseDbAndRedisUnitTest {
             RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
             YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
             RedisAutoConfiguration.class, // Spring Redis 自动配置类
-            RedissonAutoConfiguration.class, // Redisson 自动配置类
+            RedissonAutoConfiguration.class, // Redisson 自动配置类
     })
     public static class Application {
     }

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/ut/BaseRedisUnitTest.java

@@ -24,7 +24,7 @@ public class BaseRedisUnitTest {
             RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
             RedisAutoConfiguration.class, // Spring Redis 自动配置类
             YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
-            RedissonAutoConfiguration.class, // Redisson 自动配置类
+            RedissonAutoConfiguration.class, // Redisson 自动配置类
     })
     public static class Application {
     }

+ 2 - 1
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java

@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
 import org.springframework.http.HttpHeaders;
 
 import java.util.HashMap;
@@ -91,6 +92,7 @@ public class YudaoSwaggerAutoConfiguration {
      * 自定义 OpenAPI 处理器
      */
     @Bean
+    @Primary // 目的:以我们创建的 OpenAPIService Bean 为主,避免一键改包后,启动报错!
     public OpenAPIService openApiBuilder(Optional<OpenAPI> openAPI,
                                          SecurityService securityParser,
                                          SpringDocConfigProperties springDocConfigProperties,
@@ -98,7 +100,6 @@ public class YudaoSwaggerAutoConfiguration {
                                          Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
                                          Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
                                          Optional<JavadocProvider> javadocProvider) {
-
         return new OpenAPIService(openAPI, securityParser, springDocConfigProperties,
                 propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
     }

+ 3 - 0
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -122,7 +123,9 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
      * @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}
      */
     @Bean
+    @ConditionalOnMissingBean
     public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
         return restTemplateBuilder.build();
     }
+
 }

+ 7 - 7
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java

@@ -1,20 +1,20 @@
 package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo01;
 
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
-import java.util.*;
-import java.time.LocalDateTime;
-import java.time.LocalDateTime;
+
 import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.*;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 
 /**
  * 示例联系人 DO
  *
  * @author 芋道源码
  */
-@TableName("infra_demo01_contact")
-@KeySequence("infra_demo01_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("yudao_demo01_contact")
+@KeySequence("yudao_demo01_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)

+ 6 - 7
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java

@@ -1,19 +1,18 @@
 package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo02;
 
-import lombok.*;
-import java.util.*;
-import java.time.LocalDateTime;
-import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.*;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
 
 /**
  * 示例分类 DO
  *
  * @author 芋道源码
  */
-@TableName("infra_demo02_category")
-@KeySequence("infra_demo02_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("yudao_demo02_category")
+@KeySequence("yudao_demo02_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)

+ 6 - 7
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java

@@ -1,19 +1,18 @@
 package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
 
-import lombok.*;
-import java.util.*;
-import java.time.LocalDateTime;
-import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.*;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
 
 /**
  * 学生课程 DO
  *
  * @author 芋道源码
  */
-@TableName("infra_demo03_course")
-@KeySequence("infra_demo03_course_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("yudao_demo03_course")
+@KeySequence("yudao_demo03_course_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)

+ 6 - 7
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java

@@ -1,19 +1,18 @@
 package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
 
-import lombok.*;
-import java.util.*;
-import java.time.LocalDateTime;
-import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.*;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
 
 /**
  * 学生班级 DO
  *
  * @author 芋道源码
  */
-@TableName("infra_demo03_grade")
-@KeySequence("infra_demo03_grade_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("yudao_demo03_grade")
+@KeySequence("yudao_demo03_grade_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)

+ 7 - 7
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java

@@ -1,20 +1,20 @@
 package cn.iocoder.yudao.module.infra.dal.dataobject.demo.demo03;
 
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
-import java.util.*;
-import java.time.LocalDateTime;
-import java.time.LocalDateTime;
+
 import java.time.LocalDateTime;
-import com.baomidou.mybatisplus.annotation.*;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 
 /**
  * 学生 DO
  *
  * @author 芋道源码
  */
-@TableName("infra_demo03_student")
-@KeySequence("infra_demo03_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("yudao_demo03_student")
+@KeySequence("yudao_demo03_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)

+ 0 - 6
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/spu/dto/ProductSpuRespDTO.java

@@ -24,12 +24,6 @@ public class ProductSpuRespDTO {
      * 商品名称
      */
     private String name;
-    /**
-     * 单位
-     *
-     * 对应 product_unit 数据字典
-     */
-    private Integer unit;
 
     /**
      * 商品分类编号

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java

@@ -119,7 +119,7 @@ public class CouponServiceImpl implements CouponService {
         Integer status = LocalDateTimeUtils.beforeNow(coupon.getValidEndTime())
                 ? CouponStatusEnum.EXPIRE.getStatus() // 退还时可能已经过期了
                 : CouponStatusEnum.UNUSED.getStatus();
-        int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.UNUSED.getStatus(),
+        int updateCount = couponMapper.updateByIdAndStatus(id, CouponStatusEnum.USED.getStatus(),
                 new CouponDO().setStatus(status));
         if (updateCount == 0) {
             throw exception(COUPON_STATUS_NOT_USED);

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseDbAndRedisIntegrationTest.java

@@ -30,7 +30,7 @@ public class BaseDbAndRedisIntegrationTest {
             // Redis 配置类
             RedisAutoConfiguration.class, // Spring Redis 自动配置类
             YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
-            RedissonAutoConfiguration.class, // Redisson 自动配置类
+            RedissonAutoConfiguration.class, // Redisson 自动配置类
     })
     public static class Application {
     }

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/test/BaseRedisIntegrationTest.java

@@ -15,7 +15,7 @@ public class BaseRedisIntegrationTest {
             // Redis 配置类
             RedisAutoConfiguration.class, // Spring Redis 自动配置类
             YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
-            RedissonAutoConfiguration.class, // Redisson 自动配置类
+            RedissonAutoConfiguration.class, // Redisson 自动配置类
     })
     public static class Application {
     }

+ 2 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java

@@ -26,7 +26,7 @@ public class SmsCallbackController {
 
     @PostMapping("/aliyun")
     @PermitAll
-    @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
+    @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/zh/sms/developer-reference/configure-delivery-receipts-1 文档")
     public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable {
         String text = ServletUtils.getBody(request);
         smsSendService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text);
@@ -35,7 +35,7 @@ public class SmsCallbackController {
 
     @PostMapping("/tencent")
     @PermitAll
-    @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档")
+    @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/59178 文档")
     public CommonResult<Boolean> receiveTencentSmsStatus(HttpServletRequest request) throws Throwable {
         String text = ServletUtils.getBody(request);
         smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text);

+ 2 - 1
yudao-server/src/test/java/cn/iocoder/yudao/ProjectReactor.java

@@ -34,7 +34,8 @@ public class ProjectReactor {
      * 白名单文件,不进行重写,避免出问题
      */
     private static final Set<String> WHITE_FILE_TYPES = SetUtils.asSet("gif", "jpg", "svg", "png", // 图片
-            "eot", "woff2", "ttf", "woff"); // 字体
+            "eot", "woff2", "ttf", "woff",  // 字体
+            "xdb"); // IP 库
 
     public static void main(String[] args) {
         long start = System.currentTimeMillis();

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff