Commit 16b8f3ba9484cb936664c84bfbc6caae42d525f8

Authored by YunaiV
1 parent 1d00261c

【移除】移除 yudao-example 项目,统一到 https://gitee.com/yudaocode/yudao-demo

Showing 44 changed files with 0 additions and 2537 deletions
... ... @@ -23,8 +23,6 @@
23 23 <!-- <module>yudao-module-mall</module>-->
24 24 <!-- <module>yudao-module-crm</module>-->
25 25 <!-- <module>yudao-module-erp</module>-->
26   - <!-- 示例项目 -->
27   -<!-- <module>yudao-example</module>-->
28 26 </modules>
29 27  
30 28 <name>${project.artifactId}</name>
... ...
yudao-example/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <modelVersion>4.0.0</modelVersion>
6   -
7   - <!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
8   -
9   - <groupId>cn.iocoder.boot</groupId>
10   - <artifactId>yudao-example</artifactId>
11   - <version>1.0.0-snapshot</version>
12   - <packaging>pom</packaging>
13   - <modules>
14   - <module>yudao-sso-demo-by-code</module>
15   - <module>yudao-sso-demo-by-password</module>
16   - </modules>
17   -
18   - <name>${project.artifactId}</name>
19   - <description>提供各种示例,例如说:SSO 单点登录</description>
20   - <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
21   -
22   -</project>
yudao-example/yudao-sso-demo-by-code/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <modelVersion>4.0.0</modelVersion>
6   -
7   - <!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
8   -
9   - <groupId>cn.iocoder.boot</groupId>
10   - <artifactId>yudao-sso-demo-by-code</artifactId>
11   - <version>1.0.0-snapshot</version>
12   - <packaging>jar</packaging>
13   -
14   - <name>${project.artifactId}</name>
15   - <description>基于授权码模式,如何实现 SSO 单点登录?</description>
16   - <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
17   -
18   - <properties>
19   - <!-- Maven 相关 -->
20   - <maven.compiler.source>21</maven.compiler.source>
21   - <maven.compiler.target>21</maven.compiler.target>
22   - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23   - <!-- 统一依赖管理 -->
24   - <spring.boot.version>3.2.0</spring.boot.version>
25   - </properties>
26   -
27   - <dependencyManagement>
28   - <dependencies>
29   - <!-- 统一依赖管理 -->
30   - <dependency>
31   - <groupId>org.springframework.boot</groupId>
32   - <artifactId>spring-boot-dependencies</artifactId>
33   - <version>${spring.boot.version}</version>
34   - <type>pom</type>
35   - <scope>import</scope>
36   - </dependency>
37   - </dependencies>
38   - </dependencyManagement>
39   -
40   - <dependencies>
41   - <!-- Web 相关 -->
42   - <dependency>
43   - <groupId>org.springframework.boot</groupId>
44   - <artifactId>spring-boot-starter-web</artifactId>
45   - </dependency>
46   -
47   - <dependency>
48   - <groupId>org.springframework.boot</groupId>
49   - <artifactId>spring-boot-starter-security</artifactId>
50   - </dependency>
51   -
52   - <dependency>
53   - <groupId>cn.hutool</groupId>
54   - <artifactId>hutool-all</artifactId>
55   - <version>5.8.22</version>
56   - </dependency>
57   -
58   - <dependency>
59   - <groupId>org.projectlombok</groupId>
60   - <artifactId>lombok</artifactId>
61   - <optional>true</optional>
62   - </dependency>
63   - </dependencies>
64   -
65   -</project>
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java deleted
1   -package cn.iocoder.yudao.ssodemo;
2   -
3   -import org.springframework.boot.SpringApplication;
4   -import org.springframework.boot.autoconfigure.SpringBootApplication;
5   -
6   -@SpringBootApplication
7   -public class SSODemoApplication {
8   -
9   - public static void main(String[] args) {
10   - SpringApplication.run(SSODemoApplication.class, args);
11   - }
12   -
13   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java deleted
1   -package cn.iocoder.yudao.ssodemo.client;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
5   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
6   -import org.springframework.core.ParameterizedTypeReference;
7   -import org.springframework.http.*;
8   -import org.springframework.stereotype.Component;
9   -import org.springframework.util.Assert;
10   -import org.springframework.util.Base64Utils;
11   -import org.springframework.util.LinkedMultiValueMap;
12   -import org.springframework.util.MultiValueMap;
13   -import org.springframework.web.client.RestTemplate;
14   -
15   -import java.nio.charset.StandardCharsets;
16   -
17   -/**
18   - * OAuth 2.0 客户端
19   - *
20   - * 对应调用 OAuth2OpenController 接口
21   - */
22   -@Component
23   -public class OAuth2Client {
24   -
25   - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";
26   -
27   - /**
28   - * 租户编号
29   - *
30   - * 默认使用 1;如果使用别的租户,可以调整
31   - */
32   - public static final Long TENANT_ID = 1L;
33   -
34   - private static final String CLIENT_ID = "yudao-sso-demo-by-code";
35   - private static final String CLIENT_SECRET = "test";
36   -
37   -
38   -// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
39   - private final RestTemplate restTemplate = new RestTemplate();
40   -
41   - /**
42   - * 使用 code 授权码,获得访问令牌
43   - *
44   - * @param code 授权码
45   - * @param redirectUri 重定向 URI
46   - * @return 访问令牌
47   - */
48   - public CommonResult<OAuth2AccessTokenRespDTO> postAccessToken(String code, String redirectUri) {
49   - // 1.1 构建请求头
50   - HttpHeaders headers = new HttpHeaders();
51   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
52   - headers.set("tenant-id", TENANT_ID.toString());
53   - addClientHeader(headers);
54   - // 1.2 构建请求参数
55   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
56   - body.add("grant_type", "authorization_code");
57   - body.add("code", code);
58   - body.add("redirect_uri", redirectUri);
59   -// body.add("state", ""); // 选填;填了会校验
60   -
61   - // 2. 执行请求
62   - ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
63   - BASE_URL + "/token",
64   - HttpMethod.POST,
65   - new HttpEntity<>(body, headers),
66   - new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
67   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
68   - return exchange.getBody();
69   - }
70   -
71   - /**
72   - * 校验访问令牌,并返回它的基本信息
73   - *
74   - * @param token 访问令牌
75   - * @return 访问令牌的基本信息
76   - */
77   - public CommonResult<OAuth2CheckTokenRespDTO> checkToken(String token) {
78   - // 1.1 构建请求头
79   - HttpHeaders headers = new HttpHeaders();
80   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
81   - headers.set("tenant-id", TENANT_ID.toString());
82   - addClientHeader(headers);
83   - // 1.2 构建请求参数
84   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
85   - body.add("token", token);
86   -
87   - // 2. 执行请求
88   - ResponseEntity<CommonResult<OAuth2CheckTokenRespDTO>> exchange = restTemplate.exchange(
89   - BASE_URL + "/check-token",
90   - HttpMethod.POST,
91   - new HttpEntity<>(body, headers),
92   - new ParameterizedTypeReference<CommonResult<OAuth2CheckTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
93   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
94   - return exchange.getBody();
95   - }
96   -
97   - /**
98   - * 使用刷新令牌,获得(刷新)访问令牌
99   - *
100   - * @param refreshToken 刷新令牌
101   - * @return 访问令牌
102   - */
103   - public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(String refreshToken) {
104   - // 1.1 构建请求头
105   - HttpHeaders headers = new HttpHeaders();
106   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
107   - headers.set("tenant-id", TENANT_ID.toString());
108   - addClientHeader(headers);
109   - // 1.2 构建请求参数
110   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
111   - body.add("grant_type", "refresh_token");
112   - body.add("refresh_token", refreshToken);
113   -
114   - // 2. 执行请求
115   - ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
116   - BASE_URL + "/token",
117   - HttpMethod.POST,
118   - new HttpEntity<>(body, headers),
119   - new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
120   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
121   - return exchange.getBody();
122   - }
123   -
124   - /**
125   - * 删除访问令牌
126   - *
127   - * @param token 访问令牌
128   - * @return 成功
129   - */
130   - public CommonResult<Boolean> revokeToken(String token) {
131   - // 1.1 构建请求头
132   - HttpHeaders headers = new HttpHeaders();
133   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
134   - headers.set("tenant-id", TENANT_ID.toString());
135   - addClientHeader(headers);
136   - // 1.2 构建请求参数
137   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
138   - body.add("token", token);
139   -
140   - // 2. 执行请求
141   - ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
142   - BASE_URL + "/token",
143   - HttpMethod.DELETE,
144   - new HttpEntity<>(body, headers),
145   - new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
146   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
147   - return exchange.getBody();
148   - }
149   -
150   - private static void addClientHeader(HttpHeaders headers) {
151   - // client 拼接,需要 BASE64 编码
152   - String client = CLIENT_ID + ":" + CLIENT_SECRET;
153   - client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
154   - headers.add("Authorization", "Basic " + client);
155   - }
156   -
157   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java deleted
1   -package cn.iocoder.yudao.ssodemo.client;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
5   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
6   -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
7   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
8   -import org.springframework.core.ParameterizedTypeReference;
9   -import org.springframework.http.*;
10   -import org.springframework.stereotype.Component;
11   -import org.springframework.util.Assert;
12   -import org.springframework.util.LinkedMultiValueMap;
13   -import org.springframework.util.MultiValueMap;
14   -import org.springframework.web.client.RestTemplate;
15   -
16   -/**
17   - * 用户 User 信息的客户端
18   - *
19   - * 对应调用 OAuth2UserController 接口
20   - */
21   -@Component
22   -public class UserClient {
23   -
24   - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user";
25   -
26   - // @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
27   - private final RestTemplate restTemplate = new RestTemplate();
28   -
29   - public CommonResult<UserInfoRespDTO> getUser() {
30   - // 1.1 构建请求头
31   - HttpHeaders headers = new HttpHeaders();
32   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
33   - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
34   - addTokenHeader(headers);
35   - // 1.2 构建请求参数
36   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
37   -
38   - // 2. 执行请求
39   - ResponseEntity<CommonResult<UserInfoRespDTO>> exchange = restTemplate.exchange(
40   - BASE_URL + "/get",
41   - HttpMethod.GET,
42   - new HttpEntity<>(body, headers),
43   - new ParameterizedTypeReference<CommonResult<UserInfoRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
44   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
45   - return exchange.getBody();
46   - }
47   -
48   - public CommonResult<Boolean> updateUser(UserUpdateReqDTO updateReqDTO) {
49   - // 1.1 构建请求头
50   - HttpHeaders headers = new HttpHeaders();
51   - headers.setContentType(MediaType.APPLICATION_JSON);
52   - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
53   - addTokenHeader(headers);
54   - // 1.2 构建请求参数
55   - // 使用 updateReqDTO 即可
56   -
57   - // 2. 执行请求
58   - ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
59   - BASE_URL + "/update",
60   - HttpMethod.PUT,
61   - new HttpEntity<>(updateReqDTO, headers),
62   - new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
63   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
64   - return exchange.getBody();
65   - }
66   -
67   -
68   - private static void addTokenHeader(HttpHeaders headers) {
69   - LoginUser loginUser = SecurityUtils.getLoginUser();
70   - Assert.notNull(loginUser, "登录用户不能为空");
71   - headers.add("Authorization", "Bearer " + loginUser.getAccessToken());
72   - }
73   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto;
2   -
3   -import lombok.Data;
4   -
5   -import java.io.Serializable;
6   -
7   -/**
8   - * 通用返回
9   - *
10   - * @param <T> 数据泛型
11   - */
12   -@Data
13   -public class CommonResult<T> implements Serializable {
14   -
15   - /**
16   - * 错误码
17   - */
18   - private Integer code;
19   - /**
20   - * 返回数据
21   - */
22   - private T data;
23   - /**
24   - * 错误提示,用户可阅读
25   - */
26   - private String msg;
27   -
28   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
2   -
3   -import com.fasterxml.jackson.annotation.JsonProperty;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Data;
6   -import lombok.NoArgsConstructor;
7   -
8   -/**
9   - * 访问令牌 Response DTO
10   - */
11   -@Data
12   -@NoArgsConstructor
13   -@AllArgsConstructor
14   -public class OAuth2AccessTokenRespDTO {
15   -
16   - /**
17   - * 访问令牌
18   - */
19   - @JsonProperty("access_token")
20   - private String accessToken;
21   -
22   - /**
23   - * 刷新令牌
24   - */
25   - @JsonProperty("refresh_token")
26   - private String refreshToken;
27   -
28   - /**
29   - * 令牌类型
30   - */
31   - @JsonProperty("token_type")
32   - private String tokenType;
33   -
34   - /**
35   - * 过期时间;单位:秒
36   - */
37   - @JsonProperty("expires_in")
38   - private Long expiresIn;
39   -
40   - /**
41   - * 授权范围;如果多个授权范围,使用空格分隔
42   - */
43   - private String scope;
44   -
45   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
2   -
3   -import com.fasterxml.jackson.annotation.JsonProperty;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Data;
6   -import lombok.NoArgsConstructor;
7   -
8   -import java.util.List;
9   -
10   -/**
11   - * 校验令牌 Response DTO
12   - *
13   - * @author 芋道源码
14   - */
15   -@Data
16   -@NoArgsConstructor
17   -@AllArgsConstructor
18   -public class OAuth2CheckTokenRespDTO {
19   -
20   - /**
21   - * 用户编号
22   - */
23   - @JsonProperty("user_id")
24   - private Long userId;
25   - /**
26   - * 用户类型
27   - */
28   - @JsonProperty("user_type")
29   - private Integer userType;
30   - /**
31   - * 租户编号
32   - */
33   - @JsonProperty("tenant_id")
34   - private Long tenantId;
35   -
36   - /**
37   - * 客户端编号
38   - */
39   - @JsonProperty("client_id")
40   - private String clientId;
41   - /**
42   - * 授权范围
43   - */
44   - private List<String> scopes;
45   -
46   - /**
47   - * 访问令牌
48   - */
49   - @JsonProperty("access_token")
50   - private String accessToken;
51   -
52   - /**
53   - * 过期时间
54   - *
55   - * 时间戳 / 1000,即单位:秒
56   - */
57   - private Long exp;
58   -
59   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.user;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
6   -
7   -import java.util.List;
8   -
9   -/**
10   - * 获得用户基本信息 Response dto
11   - */
12   -@Data
13   -@NoArgsConstructor
14   -@AllArgsConstructor
15   -public class UserInfoRespDTO {
16   -
17   - /**
18   - * 用户编号
19   - */
20   - private Long id;
21   -
22   - /**
23   - * 用户账号
24   - */
25   - private String username;
26   -
27   - /**
28   - * 用户昵称
29   - */
30   - private String nickname;
31   -
32   - /**
33   - * 用户邮箱
34   - */
35   - private String email;
36   - /**
37   - * 手机号码
38   - */
39   - private String mobile;
40   -
41   - /**
42   - * 用户性别
43   - */
44   - private Integer sex;
45   -
46   - /**
47   - * 用户头像
48   - */
49   - private String avatar;
50   -
51   - /**
52   - * 所在部门
53   - */
54   - private Dept dept;
55   -
56   - /**
57   - * 所属岗位数组
58   - */
59   - private List<Post> posts;
60   -
61   - /**
62   - * 部门
63   - */
64   - @Data
65   - public static class Dept {
66   -
67   - /**
68   - * 部门编号
69   - */
70   - private Long id;
71   -
72   - /**
73   - * 部门名称
74   - */
75   - private String name;
76   -
77   - }
78   -
79   - /**
80   - * 岗位
81   - */
82   - @Data
83   - public static class Post {
84   -
85   - /**
86   - * 岗位编号
87   - */
88   - private Long id;
89   -
90   - /**
91   - * 岗位名称
92   - */
93   - private String name;
94   -
95   - }
96   -
97   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.user;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
6   -
7   -/**
8   - * 更新用户基本信息 Request DTO
9   - */
10   -@Data
11   -@NoArgsConstructor
12   -@AllArgsConstructor
13   -public class UserUpdateReqDTO {
14   -
15   - /**
16   - * 用户昵称
17   - */
18   - private String nickname;
19   -
20   - /**
21   - * 用户邮箱
22   - */
23   - private String email;
24   -
25   - /**
26   - * 手机号码
27   - */
28   - private String mobile;
29   -
30   - /**
31   - * 用户性别
32   - */
33   - private Integer sex;
34   -
35   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java deleted
1   -package cn.iocoder.yudao.ssodemo.controller;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
5   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
6   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
7   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
8   -import org.springframework.web.bind.annotation.PostMapping;
9   -import org.springframework.web.bind.annotation.RequestMapping;
10   -import org.springframework.web.bind.annotation.RequestParam;
11   -import org.springframework.web.bind.annotation.RestController;
12   -
13   -import jakarta.annotation.Resource;
14   -import jakarta.servlet.http.HttpServletRequest;
15   -
16   -@RestController
17   -@RequestMapping("/auth")
18   -public class AuthController {
19   -
20   - @Resource
21   - private OAuth2Client oauth2Client;
22   -
23   - /**
24   - * 使用 code 访问令牌,获得访问令牌
25   - *
26   - * @param code 授权码
27   - * @param redirectUri 重定向 URI
28   - * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
29   - */
30   - @PostMapping("/login-by-code")
31   - public CommonResult<OAuth2AccessTokenRespDTO> loginByCode(@RequestParam("code") String code,
32   - @RequestParam("redirectUri") String redirectUri) {
33   - return oauth2Client.postAccessToken(code, redirectUri);
34   - }
35   -
36   - /**
37   - * 使用刷新令牌,获得(刷新)访问令牌
38   - *
39   - * @param refreshToken 刷新令牌
40   - * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
41   - */
42   - @PostMapping("/refresh-token")
43   - public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
44   - return oauth2Client.refreshToken(refreshToken);
45   - }
46   -
47   - /**
48   - * 退出登录
49   - *
50   - * @param request 请求
51   - * @return 成功
52   - */
53   - @PostMapping("/logout")
54   - public CommonResult<Boolean> logout(HttpServletRequest request) {
55   - String token = SecurityUtils.obtainAuthorization(request, "Authorization");
56   - if (StrUtil.isNotBlank(token)) {
57   - return oauth2Client.revokeToken(token);
58   - }
59   - // 返回成功
60   - return new CommonResult<>();
61   - }
62   -
63   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java deleted
1   -package cn.iocoder.yudao.ssodemo.controller;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.UserClient;
4   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
5   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
6   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
7   -import org.springframework.web.bind.annotation.*;
8   -
9   -import jakarta.annotation.Resource;
10   -
11   -@RestController
12   -@RequestMapping("/user")
13   -public class UserController {
14   -
15   - @Resource
16   - private UserClient userClient;
17   -
18   - /**
19   - * 获得当前登录用户的基本信息
20   - *
21   - * @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
22   - */
23   - @GetMapping("/get")
24   - public CommonResult<UserInfoRespDTO> getUser() {
25   - return userClient.getUser();
26   - }
27   -
28   - /**
29   - * 更新当前登录用户的昵称
30   - *
31   - * @param nickname 昵称
32   - * @return 成功
33   - */
34   - @PutMapping("/update")
35   - public CommonResult<Boolean> updateUser(@RequestParam("nickname") String nickname) {
36   - UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null);
37   - return userClient.updateUser(updateReqDTO);
38   - }
39   -
40   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.config;
2   -
3   -import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
4   -import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl;
5   -import org.springframework.context.annotation.Bean;
6   -import org.springframework.context.annotation.Configuration;
7   -import org.springframework.http.HttpMethod;
8   -import org.springframework.security.config.Customizer;
9   -import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10   -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11   -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
12   -import org.springframework.security.web.AuthenticationEntryPoint;
13   -import org.springframework.security.web.SecurityFilterChain;
14   -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
15   -
16   -import jakarta.annotation.Resource;
17   -
18   -@Configuration(proxyBeanMethods = false)
19   -@EnableWebSecurity
20   -public class SecurityConfiguration{
21   -
22   - @Resource
23   - private TokenAuthenticationFilter tokenAuthenticationFilter;
24   -
25   - @Resource
26   - private AccessDeniedHandlerImpl accessDeniedHandler;
27   - @Resource
28   - private AuthenticationEntryPoint authenticationEntryPoint;
29   -
30   - @Bean
31   - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
32   - // 设置 URL 安全权限
33   - httpSecurity
34   - // 开启跨域
35   - .cors(Customizer.withDefaults())
36   - // CSRF 禁用,因为不使用 Session
37   - .csrf(AbstractHttpConfigurer::disable)
38   - // 一堆自定义的 Spring Security 处理器
39   - .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
40   - .accessDeniedHandler(accessDeniedHandler));
41   -
42   - // 设置每个请求的权限
43   - httpSecurity.authorizeHttpRequests(c -> c
44   - // 1. 静态资源,可匿名访问
45   - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
46   - // 2. 登录相关的接口,可匿名访问
47   - .requestMatchers("/auth/login-by-code").permitAll()
48   - .requestMatchers("/auth/refresh-token").permitAll()
49   - .requestMatchers("/auth/logout").permitAll())
50   - // 3. 兜底规则,必须认证
51   - .authorizeHttpRequests(c -> c.anyRequest().authenticated());
52   -
53   - // 添加 Token Filter
54   - httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
55   - return httpSecurity.build();
56   - }
57   -
58   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core;
2   -
3   -import lombok.Data;
4   -
5   -import java.util.List;
6   -
7   -/**
8   - * 登录用户信息
9   - *
10   - * @author 芋道源码
11   - */
12   -@Data
13   -public class LoginUser {
14   -
15   - /**
16   - * 用户编号
17   - */
18   - private Long id;
19   - /**
20   - * 用户类型
21   - */
22   - private Integer userType;
23   - /**
24   - * 租户编号
25   - */
26   - private Long tenantId;
27   - /**
28   - * 授权范围
29   - */
30   - private List<String> scopes;
31   -
32   - /**
33   - * 访问令牌
34   - */
35   - private String accessToken;
36   -
37   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.filter;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
4   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
5   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
6   -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
7   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
8   -import org.springframework.stereotype.Component;
9   -import org.springframework.util.StringUtils;
10   -import org.springframework.web.filter.OncePerRequestFilter;
11   -
12   -import jakarta.annotation.Resource;
13   -import jakarta.servlet.FilterChain;
14   -import jakarta.servlet.ServletException;
15   -import jakarta.servlet.http.HttpServletRequest;
16   -import jakarta.servlet.http.HttpServletResponse;
17   -import java.io.IOException;
18   -
19   -/**
20   - * Token 过滤器,验证 token 的有效性
21   - * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
22   - *
23   - * @author 芋道源码
24   - */
25   -@Component
26   -public class TokenAuthenticationFilter extends OncePerRequestFilter {
27   -
28   - @Resource
29   - private OAuth2Client oauth2Client;
30   -
31   - @Override
32   - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
33   - FilterChain filterChain) throws ServletException, IOException {
34   - // 1. 获得访问令牌
35   - String token = SecurityUtils.obtainAuthorization(request, "Authorization");
36   - if (StringUtils.hasText(token)) {
37   - // 2. 基于 token 构建登录用户
38   - LoginUser loginUser = buildLoginUserByToken(token);
39   - // 3. 设置当前用户
40   - if (loginUser != null) {
41   - SecurityUtils.setLoginUser(loginUser, request);
42   - }
43   - }
44   -
45   - // 继续过滤链
46   - filterChain.doFilter(request, response);
47   - }
48   -
49   - private LoginUser buildLoginUserByToken(String token) {
50   - try {
51   - CommonResult<OAuth2CheckTokenRespDTO> accessTokenResult = oauth2Client.checkToken(token);
52   - OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData();
53   - if (accessToken == null) {
54   - return null;
55   - }
56   - // 构建登录用户
57   - return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
58   - .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
59   - .setAccessToken(accessToken.getAccessToken());
60   - } catch (Exception exception) {
61   - // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
62   - return null;
63   - }
64   - }
65   -
66   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.handler;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
5   -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
6   -import lombok.extern.slf4j.Slf4j;
7   -import org.springframework.http.HttpStatus;
8   -import org.springframework.security.access.AccessDeniedException;
9   -import org.springframework.security.web.access.AccessDeniedHandler;
10   -import org.springframework.security.web.access.ExceptionTranslationFilter;
11   -import org.springframework.stereotype.Component;
12   -
13   -import jakarta.servlet.FilterChain;
14   -import jakarta.servlet.ServletException;
15   -import jakarta.servlet.http.HttpServletRequest;
16   -import jakarta.servlet.http.HttpServletResponse;
17   -import java.io.IOException;
18   -
19   -/**
20   - * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
21   - *
22   - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
23   - *
24   - * @author 芋道源码
25   - */
26   -@Component
27   -@SuppressWarnings("JavadocReference")
28   -@Slf4j
29   -public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
30   -
31   - @Override
32   - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
33   - throws IOException, ServletException {
34   - // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
35   - log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
36   - SecurityUtils.getLoginUserId(), e);
37   - // 返回 403
38   - CommonResult<Object> result = new CommonResult<>();
39   - result.setCode(HttpStatus.FORBIDDEN.value());
40   - result.setMsg("没有该操作权限");
41   - ServletUtils.writeJSON(response, result);
42   - }
43   -
44   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.handler;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
5   -import lombok.extern.slf4j.Slf4j;
6   -import org.springframework.http.HttpStatus;
7   -import org.springframework.security.core.AuthenticationException;
8   -import org.springframework.security.web.AuthenticationEntryPoint;
9   -import org.springframework.security.web.access.ExceptionTranslationFilter;
10   -import org.springframework.stereotype.Component;
11   -
12   -import jakarta.servlet.FilterChain;
13   -import jakarta.servlet.http.HttpServletRequest;
14   -import jakarta.servlet.http.HttpServletResponse;
15   -
16   -/**
17   - * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
18   - *
19   - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
20   - */
21   -@Component
22   -@Slf4j
23   -@SuppressWarnings("JavadocReference") // 忽略文档引用报错
24   -public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
25   -
26   - @Override
27   - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
28   - log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
29   - // 返回 401
30   - CommonResult<Object> result = new CommonResult<>();
31   - result.setCode(HttpStatus.UNAUTHORIZED.value());
32   - result.setMsg("账号未登录");
33   - ServletUtils.writeJSON(response, result);
34   - }
35   -
36   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.util;
2   -
3   -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
4   -import org.springframework.lang.Nullable;
5   -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
6   -import org.springframework.security.core.Authentication;
7   -import org.springframework.security.core.context.SecurityContext;
8   -import org.springframework.security.core.context.SecurityContextHolder;
9   -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
10   -import org.springframework.util.StringUtils;
11   -
12   -import jakarta.servlet.http.HttpServletRequest;
13   -import java.util.Collections;
14   -
15   -/**
16   - * 安全服务工具类
17   - *
18   - * @author 芋道源码
19   - */
20   -public class SecurityUtils {
21   -
22   - public static final String AUTHORIZATION_BEARER = "Bearer";
23   -
24   - private SecurityUtils() {}
25   -
26   - /**
27   - * 从请求中,获得认证 Token
28   - *
29   - * @param request 请求
30   - * @param header 认证 Token 对应的 Header 名字
31   - * @return 认证 Token
32   - */
33   - public static String obtainAuthorization(HttpServletRequest request, String header) {
34   - String authorization = request.getHeader(header);
35   - if (!StringUtils.hasText(authorization)) {
36   - return null;
37   - }
38   - int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
39   - if (index == -1) { // 未找到
40   - return null;
41   - }
42   - return authorization.substring(index + 7).trim();
43   - }
44   -
45   - /**
46   - * 获得当前认证信息
47   - *
48   - * @return 认证信息
49   - */
50   - public static Authentication getAuthentication() {
51   - SecurityContext context = SecurityContextHolder.getContext();
52   - if (context == null) {
53   - return null;
54   - }
55   - return context.getAuthentication();
56   - }
57   -
58   - /**
59   - * 获取当前用户
60   - *
61   - * @return 当前用户
62   - */
63   - @Nullable
64   - public static LoginUser getLoginUser() {
65   - Authentication authentication = getAuthentication();
66   - if (authentication == null) {
67   - return null;
68   - }
69   - return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
70   - }
71   -
72   - /**
73   - * 获得当前用户的编号,从上下文中
74   - *
75   - * @return 用户编号
76   - */
77   - @Nullable
78   - public static Long getLoginUserId() {
79   - LoginUser loginUser = getLoginUser();
80   - return loginUser != null ? loginUser.getId() : null;
81   - }
82   -
83   - /**
84   - * 设置当前用户
85   - *
86   - * @param loginUser 登录用户
87   - * @param request 请求
88   - */
89   - public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
90   - // 创建 Authentication,并设置到上下文
91   - Authentication authentication = buildAuthentication(loginUser, request);
92   - SecurityContextHolder.getContext().setAuthentication(authentication);
93   - }
94   -
95   - private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
96   - // 创建 UsernamePasswordAuthenticationToken 对象
97   - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
98   - loginUser, null, Collections.emptyList());
99   - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
100   - return authenticationToken;
101   - }
102   -
103   -}
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.util;
2   -
3   -import cn.hutool.extra.servlet.JakartaServletUtil;
4   -import cn.hutool.extra.servlet.ServletUtil;
5   -import cn.hutool.json.JSONUtil;
6   -import org.springframework.http.MediaType;
7   -
8   -import jakarta.servlet.http.HttpServletResponse;
9   -
10   -/**
11   - * 客户端工具类
12   - *
13   - * @author 芋道源码
14   - */
15   -public class ServletUtils {
16   -
17   - /**
18   - * 返回 JSON 字符串
19   - *
20   - * @param response 响应
21   - * @param object 对象,会序列化成 JSON 字符串
22   - */
23   - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
24   - public static void writeJSON(HttpServletResponse response, Object object) {
25   - String content = JSONUtil.toJsonStr(object);
26   - JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
27   - }
28   -
29   - public static void write(HttpServletResponse response, String text, String contentType) {
30   - JakartaServletUtil.write(response, text, contentType);
31   - }
32   -
33   -}
yudao-example/yudao-sso-demo-by-code/src/main/resources/application.yaml deleted
1   -server:
2   - port: 18080
yudao-example/yudao-sso-demo-by-code/src/main/resources/static/callback.html deleted
1   -<!DOCTYPE html>
2   -<html lang="en">
3   -<head>
4   - <meta charset="UTF-8">
5   - <title>SSO 授权后的回调页</title>
6   - <!-- jQuery:操作 dom、发起请求等 -->
7   - <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
8   - <!-- 工具类 -->
9   - <script type="application/javascript">
10   - (function ($) {
11   - /**
12   - * 获得 URL 的指定参数的值
13   - *
14   - * @param name 参数名
15   - * @returns 参数值
16   - */
17   - $.getUrlParam = function (name) {
18   - const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
19   - const r = window.location.search.substr(1).match(reg);
20   - if (r != null) return unescape(r[2]); return null;
21   - }
22   - })(jQuery);
23   - </script>
24   -
25   - <script type="application/javascript">
26   - $(function () {
27   - // 获得 code 授权码
28   - const code = $.getUrlParam('code');
29   - if (!code) {
30   - alert('获取不到 code 参数,请排查!')
31   - return;
32   - }
33   -
34   - // 提交
35   - const redirectUri = 'http://127.0.0.1:18080/callback.html'; // 需要修改成,你回调的地址,就是在 index.html 拼接的 redirectUri
36   - $.ajax({
37   - url: "http://127.0.0.1:18080/auth/login-by-code?code=" + code
38   - + '&redirectUri=' + redirectUri,
39   - method: 'POST',
40   - success: function( result ) {
41   - if (result.code !== 0) {
42   - alert('获得访问令牌失败,原因:' + result.msg)
43   - return;
44   - }
45   - alert('获得访问令牌成功!点击确认,跳转回首页')
46   -
47   - // 设置到 localStorage 中
48   - localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
49   - localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
50   -
51   - // 跳转回首页
52   - window.location.href = '/index.html';
53   - }
54   - })
55   - })
56   - </script>
57   -</head>
58   -<body>
59   -正在使用 code 授权码,进行 accessToken 访问令牌的获取
60   -</body>
61   -</html>
yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html deleted
1   -<!DOCTYPE html>
2   -<html lang="en">
3   -<head>
4   - <meta charset="UTF-8">
5   - <title>首页</title>
6   - <!-- jQuery:操作 dom、发起请求等 -->
7   - <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
8   -
9   - <script type="application/javascript">
10   -
11   - /**
12   - * 跳转单点登录
13   - */
14   - function ssoLogin() {
15   - const clientId = 'yudao-sso-demo-by-code'; // 可以改写成,你的 clientId
16   - const redirectUri = encodeURIComponent('http://127.0.0.1:18080/callback.html'); // 注意,需要使用 encodeURIComponent 编码地址
17   - const responseType = 'code'; // 1)授权码模式,对应 code;2)简化模式,对应 token
18   - window.location.href = 'http://127.0.0.1:1024/sso?client_id=' + clientId
19   - + '&redirect_uri=' + redirectUri
20   - + '&response_type=' + responseType;
21   - }
22   -
23   - /**
24   - * 修改昵称
25   - */
26   - function updateNickname() {
27   - const nickname = prompt("请输入新的昵称", "");
28   - if (!nickname) {
29   - return;
30   - }
31   - // 更新用户的昵称
32   - const accessToken = localStorage.getItem('ACCESS-TOKEN');
33   - $.ajax({
34   - url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
35   - method: 'PUT',
36   - headers: {
37   - 'Authorization': 'Bearer ' + accessToken
38   - },
39   - success: function (result) {
40   - if (result.code !== 0) {
41   - alert('更新昵称失败,原因:' + result.msg)
42   - return;
43   - }
44   - alert('更新昵称成功!');
45   - $('#nicknameSpan').html(nickname);
46   - }
47   - });
48   - }
49   -
50   - /**
51   - * 刷新令牌
52   - */
53   - function refreshToken() {
54   - const refreshToken = localStorage.getItem('REFRESH-TOKEN');
55   - if (!refreshToken) {
56   - alert("获取不到刷新令牌");
57   - return;
58   - }
59   - $.ajax({
60   - url: "http://127.0.0.1:18080/auth/refresh-token?refreshToken=" + refreshToken,
61   - method: 'POST',
62   - success: function (result) {
63   - if (result.code !== 0) {
64   - alert('刷新访问令牌失败,原因:' + result.msg)
65   - return;
66   - }
67   - alert('更新访问令牌成功!');
68   - $('#accessTokenSpan').html(result.data.access_token);
69   -
70   - // 设置到 localStorage 中
71   - localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
72   - localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
73   - }
74   - });
75   - }
76   -
77   - /**
78   - * 登出,删除访问令牌
79   - */
80   - function logout() {
81   - const accessToken = localStorage.getItem('ACCESS-TOKEN');
82   - if (!accessToken) {
83   - location.reload();
84   - return;
85   - }
86   - $.ajax({
87   - url: "http://127.0.0.1:18080/auth/logout",
88   - method: 'POST',
89   - headers: {
90   - 'Authorization': 'Bearer ' + accessToken
91   - },
92   - success: function (result) {
93   - if (result.code !== 0) {
94   - alert('退出登录失败,原因:' + result.msg)
95   - return;
96   - }
97   - alert('退出登录成功!');
98   - // 删除 localStorage 中
99   - localStorage.removeItem('ACCESS-TOKEN');
100   - localStorage.removeItem('REFRESH-TOKEN');
101   -
102   - location.reload();
103   - }
104   - });
105   - }
106   -
107   - $(function () {
108   - const accessToken = localStorage.getItem('ACCESS-TOKEN');
109   - // 情况一:未登录
110   - if (!accessToken) {
111   - $('#noLoginDiv').css("display", "block");
112   - return;
113   - }
114   -
115   - // 情况二:已登录
116   - $('#yesLoginDiv').css("display", "block");
117   - $('#accessTokenSpan').html(accessToken);
118   - // 获得登录用户的信息
119   - $.ajax({
120   - url: "http://127.0.0.1:18080/user/get",
121   - method: 'GET',
122   - headers: {
123   - 'Authorization': 'Bearer ' + accessToken
124   - },
125   - success: function (result) {
126   - if (result.code !== 0) {
127   - alert('获得个人信息失败,原因:' + result.msg)
128   - return;
129   - }
130   - $('#nicknameSpan').html(result.data.nickname);
131   - }
132   - });
133   - })
134   - </script>
135   -</head>
136   -<body>
137   -<!-- 情况一:未登录:1)跳转 ruoyi-vue-pro 的 SSO 登录页 -->
138   -<div id="noLoginDiv" style="display: none">
139   - 您未登录,点击 <a href="#" onclick="ssoLogin()">跳转 </a> SSO 单点登录
140   -</div>
141   -
142   -<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
143   -<div id="yesLoginDiv" style="display: none">
144   - 您已登录!<button onclick="logout()">退出登录</button> <br />
145   - 昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
146   - 访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
147   -</div>
148   -</body>
149   -<style>
150   - body { /** 页面居中 */
151   - border-radius: 20px;
152   - height: 350px;
153   - position: absolute;
154   - left: 50%;
155   - top: 50%;
156   - transform: translate(-50%,-50%);
157   - }
158   -</style>
159   -</html>
yudao-example/yudao-sso-demo-by-password/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <modelVersion>4.0.0</modelVersion>
6   -
7   - <!-- 由于方便大家拷贝,使用不使用 yudao 作为 Maven parent -->
8   -
9   - <groupId>cn.iocoder.boot</groupId>
10   - <artifactId>yudao-sso-demo-by-password</artifactId>
11   - <version>1.0.0-snapshot</version>
12   - <packaging>jar</packaging>
13   -
14   - <name>${project.artifactId}</name>
15   - <description>基于密码模式,如何实现 SSO 单点登录?</description>
16   - <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
17   -
18   - <properties>
19   - <!-- Maven 相关 -->
20   - <maven.compiler.source>21</maven.compiler.source>
21   - <maven.compiler.target>21</maven.compiler.target>
22   - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
23   - <!-- 统一依赖管理 -->
24   - <spring.boot.version>3.2.0</spring.boot.version>
25   - </properties>
26   -
27   - <dependencyManagement>
28   - <dependencies>
29   - <!-- 统一依赖管理 -->
30   - <dependency>
31   - <groupId>org.springframework.boot</groupId>
32   - <artifactId>spring-boot-dependencies</artifactId>
33   - <version>${spring.boot.version}</version>
34   - <type>pom</type>
35   - <scope>import</scope>
36   - </dependency>
37   - </dependencies>
38   - </dependencyManagement>
39   -
40   - <dependencies>
41   - <!-- Web 相关 -->
42   - <dependency>
43   - <groupId>org.springframework.boot</groupId>
44   - <artifactId>spring-boot-starter-web</artifactId>
45   - </dependency>
46   -
47   - <dependency>
48   - <groupId>org.springframework.boot</groupId>
49   - <artifactId>spring-boot-starter-security</artifactId>
50   - </dependency>
51   -
52   - <dependency>
53   - <groupId>cn.hutool</groupId>
54   - <artifactId>hutool-all</artifactId>
55   - <version>5.8.22</version>
56   - </dependency>
57   -
58   - <dependency>
59   - <groupId>org.projectlombok</groupId>
60   - <artifactId>lombok</artifactId>
61   - <optional>true</optional>
62   - </dependency>
63   - </dependencies>
64   -
65   -</project>
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java deleted
1   -package cn.iocoder.yudao.ssodemo;
2   -
3   -import org.springframework.boot.SpringApplication;
4   -import org.springframework.boot.autoconfigure.SpringBootApplication;
5   -
6   -@SpringBootApplication
7   -public class SSODemoApplication {
8   -
9   - public static void main(String[] args) {
10   - SpringApplication.run(SSODemoApplication.class, args);
11   - }
12   -
13   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java deleted
1   -package cn.iocoder.yudao.ssodemo.client;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
5   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
6   -import org.springframework.core.ParameterizedTypeReference;
7   -import org.springframework.http.*;
8   -import org.springframework.stereotype.Component;
9   -import org.springframework.util.Assert;
10   -import org.springframework.util.Base64Utils;
11   -import org.springframework.util.LinkedMultiValueMap;
12   -import org.springframework.util.MultiValueMap;
13   -import org.springframework.web.client.RestTemplate;
14   -
15   -import java.nio.charset.StandardCharsets;
16   -
17   -/**
18   - * OAuth 2.0 客户端
19   - *
20   - * 对应调用 OAuth2OpenController 接口
21   - */
22   -@Component
23   -public class OAuth2Client {
24   -
25   - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2";
26   -
27   - /**
28   - * 租户编号
29   - *
30   - * 默认使用 1;如果使用别的租户,可以调整
31   - */
32   - public static final Long TENANT_ID = 1L;
33   -
34   - private static final String CLIENT_ID = "yudao-sso-demo-by-password";
35   - private static final String CLIENT_SECRET = "test";
36   -
37   -
38   -// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
39   - private final RestTemplate restTemplate = new RestTemplate();
40   -
41   - /**
42   - * 校验访问令牌,并返回它的基本信息
43   - *
44   - * @param token 访问令牌
45   - * @return 访问令牌的基本信息
46   - */
47   - public CommonResult<OAuth2CheckTokenRespDTO> checkToken(String token) {
48   - // 1.1 构建请求头
49   - HttpHeaders headers = new HttpHeaders();
50   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
51   - headers.set("tenant-id", TENANT_ID.toString());
52   - addClientHeader(headers);
53   - // 1.2 构建请求参数
54   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
55   - body.add("token", token);
56   -
57   - // 2. 执行请求
58   - ResponseEntity<CommonResult<OAuth2CheckTokenRespDTO>> exchange = restTemplate.exchange(
59   - BASE_URL + "/check-token",
60   - HttpMethod.POST,
61   - new HttpEntity<>(body, headers),
62   - new ParameterizedTypeReference<CommonResult<OAuth2CheckTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
63   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
64   - return exchange.getBody();
65   - }
66   -
67   - /**
68   - * 使用刷新令牌,获得(刷新)访问令牌
69   - *
70   - * @param refreshToken 刷新令牌
71   - * @return 访问令牌
72   - */
73   - public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(String refreshToken) {
74   - // 1.1 构建请求头
75   - HttpHeaders headers = new HttpHeaders();
76   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
77   - headers.set("tenant-id", TENANT_ID.toString());
78   - addClientHeader(headers);
79   - // 1.2 构建请求参数
80   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
81   - body.add("grant_type", "refresh_token");
82   - body.add("refresh_token", refreshToken);
83   -
84   - // 2. 执行请求
85   - ResponseEntity<CommonResult<OAuth2AccessTokenRespDTO>> exchange = restTemplate.exchange(
86   - BASE_URL + "/token",
87   - HttpMethod.POST,
88   - new HttpEntity<>(body, headers),
89   - new ParameterizedTypeReference<CommonResult<OAuth2AccessTokenRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
90   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
91   - return exchange.getBody();
92   - }
93   -
94   - /**
95   - * 删除访问令牌
96   - *
97   - * @param token 访问令牌
98   - * @return 成功
99   - */
100   - public CommonResult<Boolean> revokeToken(String token) {
101   - // 1.1 构建请求头
102   - HttpHeaders headers = new HttpHeaders();
103   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
104   - headers.set("tenant-id", TENANT_ID.toString());
105   - addClientHeader(headers);
106   - // 1.2 构建请求参数
107   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
108   - body.add("token", token);
109   -
110   - // 2. 执行请求
111   - ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
112   - BASE_URL + "/token",
113   - HttpMethod.DELETE,
114   - new HttpEntity<>(body, headers),
115   - new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
116   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
117   - return exchange.getBody();
118   - }
119   -
120   - private static void addClientHeader(HttpHeaders headers) {
121   - // client 拼接,需要 BASE64 编码
122   - String client = CLIENT_ID + ":" + CLIENT_SECRET;
123   - client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
124   - headers.add("Authorization", "Basic " + client);
125   - }
126   -
127   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java deleted
1   -package cn.iocoder.yudao.ssodemo.client;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
5   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
6   -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
7   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
8   -import org.springframework.core.ParameterizedTypeReference;
9   -import org.springframework.http.*;
10   -import org.springframework.stereotype.Component;
11   -import org.springframework.util.Assert;
12   -import org.springframework.util.LinkedMultiValueMap;
13   -import org.springframework.util.MultiValueMap;
14   -import org.springframework.web.client.RestTemplate;
15   -
16   -/**
17   - * 用户 User 信息的客户端
18   - *
19   - * 对应调用 OAuth2UserController 接口
20   - */
21   -@Component
22   -public class UserClient {
23   -
24   - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user";
25   -
26   - // @Resource // 可优化,注册一个 RestTemplate Bean,然后注入
27   - private final RestTemplate restTemplate = new RestTemplate();
28   -
29   - public CommonResult<UserInfoRespDTO> getUser() {
30   - // 1.1 构建请求头
31   - HttpHeaders headers = new HttpHeaders();
32   - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
33   - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
34   - addTokenHeader(headers);
35   - // 1.2 构建请求参数
36   - MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
37   -
38   - // 2. 执行请求
39   - ResponseEntity<CommonResult<UserInfoRespDTO>> exchange = restTemplate.exchange(
40   - BASE_URL + "/get",
41   - HttpMethod.GET,
42   - new HttpEntity<>(body, headers),
43   - new ParameterizedTypeReference<CommonResult<UserInfoRespDTO>>() {}); // 解决 CommonResult 的泛型丢失
44   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
45   - return exchange.getBody();
46   - }
47   -
48   - public CommonResult<Boolean> updateUser(UserUpdateReqDTO updateReqDTO) {
49   - // 1.1 构建请求头
50   - HttpHeaders headers = new HttpHeaders();
51   - headers.setContentType(MediaType.APPLICATION_JSON);
52   - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString());
53   - addTokenHeader(headers);
54   - // 1.2 构建请求参数
55   - // 使用 updateReqDTO 即可
56   -
57   - // 2. 执行请求
58   - ResponseEntity<CommonResult<Boolean>> exchange = restTemplate.exchange(
59   - BASE_URL + "/update",
60   - HttpMethod.PUT,
61   - new HttpEntity<>(updateReqDTO, headers),
62   - new ParameterizedTypeReference<CommonResult<Boolean>>() {}); // 解决 CommonResult 的泛型丢失
63   - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
64   - return exchange.getBody();
65   - }
66   -
67   -
68   - private static void addTokenHeader(HttpHeaders headers) {
69   - LoginUser loginUser = SecurityUtils.getLoginUser();
70   - Assert.notNull(loginUser, "登录用户不能为空");
71   - headers.add("Authorization", "Bearer " + loginUser.getAccessToken());
72   - }
73   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto;
2   -
3   -import lombok.Data;
4   -
5   -import java.io.Serializable;
6   -
7   -/**
8   - * 通用返回
9   - *
10   - * @param <T> 数据泛型
11   - */
12   -@Data
13   -public class CommonResult<T> implements Serializable {
14   -
15   - /**
16   - * 错误码
17   - */
18   - private Integer code;
19   - /**
20   - * 返回数据
21   - */
22   - private T data;
23   - /**
24   - * 错误提示,用户可阅读
25   - */
26   - private String msg;
27   -
28   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
2   -
3   -import com.fasterxml.jackson.annotation.JsonProperty;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Data;
6   -import lombok.NoArgsConstructor;
7   -
8   -/**
9   - * 访问令牌 Response DTO
10   - */
11   -@Data
12   -@NoArgsConstructor
13   -@AllArgsConstructor
14   -public class OAuth2AccessTokenRespDTO {
15   -
16   - /**
17   - * 访问令牌
18   - */
19   - @JsonProperty("access_token")
20   - private String accessToken;
21   -
22   - /**
23   - * 刷新令牌
24   - */
25   - @JsonProperty("refresh_token")
26   - private String refreshToken;
27   -
28   - /**
29   - * 令牌类型
30   - */
31   - @JsonProperty("token_type")
32   - private String tokenType;
33   -
34   - /**
35   - * 过期时间;单位:秒
36   - */
37   - @JsonProperty("expires_in")
38   - private Long expiresIn;
39   -
40   - /**
41   - * 授权范围;如果多个授权范围,使用空格分隔
42   - */
43   - private String scope;
44   -
45   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.oauth2;
2   -
3   -import com.fasterxml.jackson.annotation.JsonProperty;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Data;
6   -import lombok.NoArgsConstructor;
7   -
8   -import java.util.List;
9   -
10   -/**
11   - * 校验令牌 Response DTO
12   - *
13   - * @author 芋道源码
14   - */
15   -@Data
16   -@NoArgsConstructor
17   -@AllArgsConstructor
18   -public class OAuth2CheckTokenRespDTO {
19   -
20   - /**
21   - * 用户编号
22   - */
23   - @JsonProperty("user_id")
24   - private Long userId;
25   - /**
26   - * 用户类型
27   - */
28   - @JsonProperty("user_type")
29   - private Integer userType;
30   - /**
31   - * 租户编号
32   - */
33   - @JsonProperty("tenant_id")
34   - private Long tenantId;
35   -
36   - /**
37   - * 客户端编号
38   - */
39   - @JsonProperty("client_id")
40   - private String clientId;
41   - /**
42   - * 授权范围
43   - */
44   - private List<String> scopes;
45   -
46   - /**
47   - * 访问令牌
48   - */
49   - @JsonProperty("access_token")
50   - private String accessToken;
51   -
52   - /**
53   - * 过期时间
54   - *
55   - * 时间戳 / 1000,即单位:秒
56   - */
57   - private Long exp;
58   -
59   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.user;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
6   -
7   -import java.util.List;
8   -
9   -/**
10   - * 获得用户基本信息 Response dto
11   - */
12   -@Data
13   -@NoArgsConstructor
14   -@AllArgsConstructor
15   -public class UserInfoRespDTO {
16   -
17   - /**
18   - * 用户编号
19   - */
20   - private Long id;
21   -
22   - /**
23   - * 用户账号
24   - */
25   - private String username;
26   -
27   - /**
28   - * 用户昵称
29   - */
30   - private String nickname;
31   -
32   - /**
33   - * 用户邮箱
34   - */
35   - private String email;
36   - /**
37   - * 手机号码
38   - */
39   - private String mobile;
40   -
41   - /**
42   - * 用户性别
43   - */
44   - private Integer sex;
45   -
46   - /**
47   - * 用户头像
48   - */
49   - private String avatar;
50   -
51   - /**
52   - * 所在部门
53   - */
54   - private Dept dept;
55   -
56   - /**
57   - * 所属岗位数组
58   - */
59   - private List<Post> posts;
60   -
61   - /**
62   - * 部门
63   - */
64   - @Data
65   - public static class Dept {
66   -
67   - /**
68   - * 部门编号
69   - */
70   - private Long id;
71   -
72   - /**
73   - * 部门名称
74   - */
75   - private String name;
76   -
77   - }
78   -
79   - /**
80   - * 岗位
81   - */
82   - @Data
83   - public static class Post {
84   -
85   - /**
86   - * 岗位编号
87   - */
88   - private Long id;
89   -
90   - /**
91   - * 岗位名称
92   - */
93   - private String name;
94   -
95   - }
96   -
97   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java deleted
1   -package cn.iocoder.yudao.ssodemo.client.dto.user;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Data;
5   -import lombok.NoArgsConstructor;
6   -
7   -/**
8   - * 更新用户基本信息 Request DTO
9   - */
10   -@Data
11   -@NoArgsConstructor
12   -@AllArgsConstructor
13   -public class UserUpdateReqDTO {
14   -
15   - /**
16   - * 用户昵称
17   - */
18   - private String nickname;
19   -
20   - /**
21   - * 用户邮箱
22   - */
23   - private String email;
24   -
25   - /**
26   - * 手机号码
27   - */
28   - private String mobile;
29   -
30   - /**
31   - * 用户性别
32   - */
33   - private Integer sex;
34   -
35   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java deleted
1   -package cn.iocoder.yudao.ssodemo.controller;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
5   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
6   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
7   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
8   -import org.springframework.web.bind.annotation.PostMapping;
9   -import org.springframework.web.bind.annotation.RequestMapping;
10   -import org.springframework.web.bind.annotation.RequestParam;
11   -import org.springframework.web.bind.annotation.RestController;
12   -
13   -import jakarta.annotation.Resource;
14   -import jakarta.servlet.http.HttpServletRequest;
15   -
16   -@RestController
17   -@RequestMapping("/auth")
18   -public class AuthController {
19   -
20   - @Resource
21   - private OAuth2Client oauth2Client;
22   -
23   - /**
24   - * 使用刷新令牌,获得(刷新)访问令牌
25   - *
26   - * @param refreshToken 刷新令牌
27   - * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
28   - */
29   - @PostMapping("/refresh-token")
30   - public CommonResult<OAuth2AccessTokenRespDTO> refreshToken(@RequestParam("refreshToken") String refreshToken) {
31   - return oauth2Client.refreshToken(refreshToken);
32   - }
33   -
34   - /**
35   - * 退出登录
36   - *
37   - * @param request 请求
38   - * @return 成功
39   - */
40   - @PostMapping("/logout")
41   - public CommonResult<Boolean> logout(HttpServletRequest request) {
42   - String token = SecurityUtils.obtainAuthorization(request, "Authorization");
43   - if (StrUtil.isNotBlank(token)) {
44   - return oauth2Client.revokeToken(token);
45   - }
46   - // 返回成功
47   - return new CommonResult<>();
48   - }
49   -
50   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java deleted
1   -package cn.iocoder.yudao.ssodemo.controller;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.UserClient;
4   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
5   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO;
6   -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO;
7   -import org.springframework.web.bind.annotation.*;
8   -
9   -import jakarta.annotation.Resource;
10   -
11   -@RestController
12   -@RequestMapping("/user")
13   -public class UserController {
14   -
15   - @Resource
16   - private UserClient userClient;
17   -
18   - /**
19   - * 获得当前登录用户的基本信息
20   - *
21   - * @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段
22   - */
23   - @GetMapping("/get")
24   - public CommonResult<UserInfoRespDTO> getUser() {
25   - return userClient.getUser();
26   - }
27   -
28   - /**
29   - * 更新当前登录用户的昵称
30   - *
31   - * @param nickname 昵称
32   - * @return 成功
33   - */
34   - @PutMapping("/update")
35   - public CommonResult<Boolean> updateUser(@RequestParam("nickname") String nickname) {
36   - UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null);
37   - return userClient.updateUser(updateReqDTO);
38   - }
39   -
40   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.config;
2   -
3   -import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter;
4   -import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl;
5   -import org.springframework.context.annotation.Bean;
6   -import org.springframework.context.annotation.Configuration;
7   -import org.springframework.http.HttpMethod;
8   -import org.springframework.security.config.Customizer;
9   -import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10   -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11   -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
12   -import org.springframework.security.web.AuthenticationEntryPoint;
13   -import org.springframework.security.web.SecurityFilterChain;
14   -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
15   -
16   -import jakarta.annotation.Resource;
17   -
18   -@Configuration(proxyBeanMethods = false)
19   -@EnableWebSecurity
20   -public class SecurityConfiguration {
21   -
22   - @Resource
23   - private TokenAuthenticationFilter tokenAuthenticationFilter;
24   -
25   - @Resource
26   - private AccessDeniedHandlerImpl accessDeniedHandler;
27   - @Resource
28   - private AuthenticationEntryPoint authenticationEntryPoint;
29   -
30   - @Bean
31   - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
32   - // 设置 URL 安全权限
33   - httpSecurity
34   - // 开启跨域
35   - .cors(Customizer.withDefaults())
36   - // CSRF 禁用,因为不使用 Session
37   - .csrf(AbstractHttpConfigurer::disable)
38   - // 一堆自定义的 Spring Security 处理器
39   - .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint)
40   - .accessDeniedHandler(accessDeniedHandler));
41   -
42   - // 设置每个请求的权限
43   - httpSecurity.authorizeHttpRequests(c -> c
44   - // 1. 静态资源,可匿名访问
45   - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
46   - // 2. 登录相关的接口,可匿名访问
47   - .requestMatchers("/auth/login-by-code").permitAll()
48   - .requestMatchers("/auth/refresh-token").permitAll()
49   - .requestMatchers("/auth/logout").permitAll())
50   - // 3. 兜底规则,必须认证
51   - .authorizeHttpRequests(c -> c.anyRequest().authenticated());
52   -
53   - // 添加 Token Filter
54   - httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
55   - return httpSecurity.build();
56   - }
57   -
58   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core;
2   -
3   -import lombok.Data;
4   -
5   -import java.util.List;
6   -
7   -/**
8   - * 登录用户信息
9   - *
10   - * @author 芋道源码
11   - */
12   -@Data
13   -public class LoginUser {
14   -
15   - /**
16   - * 用户编号
17   - */
18   - private Long id;
19   - /**
20   - * 用户类型
21   - */
22   - private Integer userType;
23   - /**
24   - * 租户编号
25   - */
26   - private Long tenantId;
27   - /**
28   - * 授权范围
29   - */
30   - private List<String> scopes;
31   -
32   - /**
33   - * 访问令牌
34   - */
35   - private String accessToken;
36   -
37   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.filter;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
4   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
5   -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO;
6   -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
7   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
8   -import org.springframework.stereotype.Component;
9   -import org.springframework.util.StringUtils;
10   -import org.springframework.web.filter.OncePerRequestFilter;
11   -
12   -import jakarta.annotation.Resource;
13   -import jakarta.servlet.FilterChain;
14   -import jakarta.servlet.ServletException;
15   -import jakarta.servlet.http.HttpServletRequest;
16   -import jakarta.servlet.http.HttpServletResponse;
17   -import java.io.IOException;
18   -
19   -/**
20   - * Token 过滤器,验证 token 的有效性
21   - * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文
22   - *
23   - * @author 芋道源码
24   - */
25   -@Component
26   -public class TokenAuthenticationFilter extends OncePerRequestFilter {
27   -
28   - @Resource
29   - private OAuth2Client oauth2Client;
30   -
31   - @Override
32   - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
33   - FilterChain filterChain) throws ServletException, IOException {
34   - // 1. 获得访问令牌
35   - String token = SecurityUtils.obtainAuthorization(request, "Authorization");
36   - if (StringUtils.hasText(token)) {
37   - // 2. 基于 token 构建登录用户
38   - LoginUser loginUser = buildLoginUserByToken(token);
39   - // 3. 设置当前用户
40   - if (loginUser != null) {
41   - SecurityUtils.setLoginUser(loginUser, request);
42   - }
43   - }
44   -
45   - // 继续过滤链
46   - filterChain.doFilter(request, response);
47   - }
48   -
49   - private LoginUser buildLoginUserByToken(String token) {
50   - try {
51   - CommonResult<OAuth2CheckTokenRespDTO> accessTokenResult = oauth2Client.checkToken(token);
52   - OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData();
53   - if (accessToken == null) {
54   - return null;
55   - }
56   - // 构建登录用户
57   - return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType())
58   - .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes())
59   - .setAccessToken(accessToken.getAccessToken());
60   - } catch (Exception exception) {
61   - // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可
62   - return null;
63   - }
64   - }
65   -
66   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.handler;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
5   -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
6   -import lombok.extern.slf4j.Slf4j;
7   -import org.springframework.http.HttpStatus;
8   -import org.springframework.security.access.AccessDeniedException;
9   -import org.springframework.security.web.access.AccessDeniedHandler;
10   -import org.springframework.security.web.access.ExceptionTranslationFilter;
11   -import org.springframework.stereotype.Component;
12   -
13   -import jakarta.servlet.FilterChain;
14   -import jakarta.servlet.ServletException;
15   -import jakarta.servlet.http.HttpServletRequest;
16   -import jakarta.servlet.http.HttpServletResponse;
17   -import java.io.IOException;
18   -
19   -/**
20   - * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。
21   - *
22   - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类
23   - *
24   - * @author 芋道源码
25   - */
26   -@Component
27   -@SuppressWarnings("JavadocReference")
28   -@Slf4j
29   -public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
30   -
31   - @Override
32   - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e)
33   - throws IOException, ServletException {
34   - // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏
35   - log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),
36   - SecurityUtils.getLoginUserId(), e);
37   - // 返回 403
38   - CommonResult<Object> result = new CommonResult<>();
39   - result.setCode(HttpStatus.FORBIDDEN.value());
40   - result.setMsg("没有该操作权限");
41   - ServletUtils.writeJSON(response, result);
42   - }
43   -
44   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.handler;
2   -
3   -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
4   -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils;
5   -import lombok.extern.slf4j.Slf4j;
6   -import org.springframework.http.HttpStatus;
7   -import org.springframework.security.core.AuthenticationException;
8   -import org.springframework.security.web.AuthenticationEntryPoint;
9   -import org.springframework.security.web.access.ExceptionTranslationFilter;
10   -import org.springframework.stereotype.Component;
11   -
12   -import jakarta.servlet.FilterChain;
13   -import jakarta.servlet.http.HttpServletRequest;
14   -import jakarta.servlet.http.HttpServletResponse;
15   -
16   -/**
17   - * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页
18   - *
19   - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类
20   - */
21   -@Component
22   -@Slf4j
23   -@SuppressWarnings("JavadocReference") // 忽略文档引用报错
24   -public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
25   -
26   - @Override
27   - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
28   - log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e);
29   - // 返回 401
30   - CommonResult<Object> result = new CommonResult<>();
31   - result.setCode(HttpStatus.UNAUTHORIZED.value());
32   - result.setMsg("账号未登录");
33   - ServletUtils.writeJSON(response, result);
34   - }
35   -
36   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.util;
2   -
3   -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser;
4   -import org.springframework.lang.Nullable;
5   -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
6   -import org.springframework.security.core.Authentication;
7   -import org.springframework.security.core.context.SecurityContext;
8   -import org.springframework.security.core.context.SecurityContextHolder;
9   -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
10   -import org.springframework.util.StringUtils;
11   -
12   -import jakarta.servlet.http.HttpServletRequest;
13   -import java.util.Collections;
14   -
15   -/**
16   - * 安全服务工具类
17   - *
18   - * @author 芋道源码
19   - */
20   -public class SecurityUtils {
21   -
22   - public static final String AUTHORIZATION_BEARER = "Bearer";
23   -
24   - private SecurityUtils() {}
25   -
26   - /**
27   - * 从请求中,获得认证 Token
28   - *
29   - * @param request 请求
30   - * @param header 认证 Token 对应的 Header 名字
31   - * @return 认证 Token
32   - */
33   - public static String obtainAuthorization(HttpServletRequest request, String header) {
34   - String authorization = request.getHeader(header);
35   - if (!StringUtils.hasText(authorization)) {
36   - return null;
37   - }
38   - int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
39   - if (index == -1) { // 未找到
40   - return null;
41   - }
42   - return authorization.substring(index + 7).trim();
43   - }
44   -
45   - /**
46   - * 获得当前认证信息
47   - *
48   - * @return 认证信息
49   - */
50   - public static Authentication getAuthentication() {
51   - SecurityContext context = SecurityContextHolder.getContext();
52   - if (context == null) {
53   - return null;
54   - }
55   - return context.getAuthentication();
56   - }
57   -
58   - /**
59   - * 获取当前用户
60   - *
61   - * @return 当前用户
62   - */
63   - @Nullable
64   - public static LoginUser getLoginUser() {
65   - Authentication authentication = getAuthentication();
66   - if (authentication == null) {
67   - return null;
68   - }
69   - return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
70   - }
71   -
72   - /**
73   - * 获得当前用户的编号,从上下文中
74   - *
75   - * @return 用户编号
76   - */
77   - @Nullable
78   - public static Long getLoginUserId() {
79   - LoginUser loginUser = getLoginUser();
80   - return loginUser != null ? loginUser.getId() : null;
81   - }
82   -
83   - /**
84   - * 设置当前用户
85   - *
86   - * @param loginUser 登录用户
87   - * @param request 请求
88   - */
89   - public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
90   - // 创建 Authentication,并设置到上下文
91   - Authentication authentication = buildAuthentication(loginUser, request);
92   - SecurityContextHolder.getContext().setAuthentication(authentication);
93   - }
94   -
95   - private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
96   - // 创建 UsernamePasswordAuthenticationToken 对象
97   - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
98   - loginUser, null, Collections.emptyList());
99   - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
100   - return authenticationToken;
101   - }
102   -
103   -}
yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java deleted
1   -package cn.iocoder.yudao.ssodemo.framework.core.util;
2   -
3   -import cn.hutool.extra.servlet.JakartaServletUtil;
4   -import cn.hutool.extra.servlet.ServletUtil;
5   -import cn.hutool.json.JSONUtil;
6   -import org.springframework.http.MediaType;
7   -
8   -import jakarta.servlet.http.HttpServletResponse;
9   -
10   -/**
11   - * 客户端工具类
12   - *
13   - * @author 芋道源码
14   - */
15   -public class ServletUtils {
16   -
17   - /**
18   - * 返回 JSON 字符串
19   - *
20   - * @param response 响应
21   - * @param object 对象,会序列化成 JSON 字符串
22   - */
23   - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
24   - public static void writeJSON(HttpServletResponse response, Object object) {
25   - String content = JSONUtil.toJsonStr(object);
26   - JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE);
27   - }
28   -
29   - public static void write(HttpServletResponse response, String text, String contentType) {
30   - JakartaServletUtil.write(response, text, contentType);
31   - }
32   -
33   -}
yudao-example/yudao-sso-demo-by-password/src/main/resources/application.yaml deleted
1   -server:
2   - port: 18080
yudao-example/yudao-sso-demo-by-password/src/main/resources/static/index.html deleted
1   -<!DOCTYPE html>
2   -<html lang="en">
3   -<head>
4   - <meta charset="UTF-8">
5   - <title>首页</title>
6   - <!-- jQuery:操作 dom、发起请求等 -->
7   - <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
8   -
9   - <script type="application/javascript">
10   -
11   - /**
12   - * 跳转单点登录
13   - */
14   - function passwordLogin() {
15   - window.location.href = '/login.html'
16   - }
17   -
18   - /**
19   - * 修改昵称
20   - */
21   - function updateNickname() {
22   - const nickname = prompt("请输入新的昵称", "");
23   - if (!nickname) {
24   - return;
25   - }
26   - // 更新用户的昵称
27   - const accessToken = localStorage.getItem('ACCESS-TOKEN');
28   - $.ajax({
29   - url: "http://127.0.0.1:18080/user/update?nickname=" + nickname,
30   - method: 'PUT',
31   - headers: {
32   - 'Authorization': 'Bearer ' + accessToken
33   - },
34   - success: function (result) {
35   - if (result.code !== 0) {
36   - alert('更新昵称失败,原因:' + result.msg)
37   - return;
38   - }
39   - alert('更新昵称成功!');
40   - $('#nicknameSpan').html(nickname);
41   - }
42   - });
43   - }
44   -
45   - /**
46   - * 刷新令牌
47   - */
48   - function refreshToken() {
49   - const refreshToken = localStorage.getItem('REFRESH-TOKEN');
50   - if (!refreshToken) {
51   - alert("获取不到刷新令牌");
52   - return;
53   - }
54   - $.ajax({
55   - url: "http://127.0.0.1:18080/auth/refresh-token?refreshToken=" + refreshToken,
56   - method: 'POST',
57   - success: function (result) {
58   - if (result.code !== 0) {
59   - alert('刷新访问令牌失败,原因:' + result.msg)
60   - return;
61   - }
62   - alert('更新访问令牌成功!');
63   - $('#accessTokenSpan').html(result.data.access_token);
64   -
65   - // 设置到 localStorage 中
66   - localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
67   - localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
68   - }
69   - });
70   - }
71   -
72   - /**
73   - * 登出,删除访问令牌
74   - */
75   - function logout() {
76   - const accessToken = localStorage.getItem('ACCESS-TOKEN');
77   - if (!accessToken) {
78   - location.reload();
79   - return;
80   - }
81   - $.ajax({
82   - url: "http://127.0.0.1:18080/auth/logout",
83   - method: 'POST',
84   - headers: {
85   - 'Authorization': 'Bearer ' + accessToken
86   - },
87   - success: function (result) {
88   - if (result.code !== 0) {
89   - alert('退出登录失败,原因:' + result.msg)
90   - return;
91   - }
92   - alert('退出登录成功!');
93   - // 删除 localStorage 中
94   - localStorage.removeItem('ACCESS-TOKEN');
95   - localStorage.removeItem('REFRESH-TOKEN');
96   -
97   - location.reload();
98   - }
99   - });
100   - }
101   -
102   - $(function () {
103   - const accessToken = localStorage.getItem('ACCESS-TOKEN');
104   - // 情况一:未登录
105   - if (!accessToken) {
106   - $('#noLoginDiv').css("display", "block");
107   - return;
108   - }
109   -
110   - // 情况二:已登录
111   - $('#yesLoginDiv').css("display", "block");
112   - $('#accessTokenSpan').html(accessToken);
113   - // 获得登录用户的信息
114   - $.ajax({
115   - url: "http://127.0.0.1:18080/user/get",
116   - method: 'GET',
117   - headers: {
118   - 'Authorization': 'Bearer ' + accessToken
119   - },
120   - success: function (result) {
121   - if (result.code !== 0) {
122   - alert('获得个人信息失败,原因:' + result.msg)
123   - return;
124   - }
125   - $('#nicknameSpan').html(result.data.nickname);
126   - }
127   - });
128   - })
129   - </script>
130   -</head>
131   -<body>
132   -<!-- 情况一:未登录:1)跳转 ruoyi-vue-pro 的 SSO 登录页 -->
133   -<div id="noLoginDiv" style="display: none">
134   - 您未登录,点击 <a href="#" onclick="passwordLogin()">跳转 </a> 账号密码登录
135   -</div>
136   -
137   -<!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
138   -<div id="yesLoginDiv" style="display: none">
139   - 您已登录!<button onclick="logout()">退出登录</button> <br />
140   - 昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
141   - 访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
142   -</div>
143   -</body>
144   -<style>
145   - body { /** 页面居中 */
146   - border-radius: 20px;
147   - height: 350px;
148   - position: absolute;
149   - left: 50%;
150   - top: 50%;
151   - transform: translate(-50%,-50%);
152   - }
153   -</style>
154   -</html>
yudao-example/yudao-sso-demo-by-password/src/main/resources/static/login.html deleted
1   -<!DOCTYPE html>
2   -<html lang="en">
3   -<head>
4   - <meta charset="UTF-8">
5   - <title>登录</title>
6   - <!-- jQuery:操作 dom、发起请求等 -->
7   - <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/2.1.2/jquery.min.js" type="application/javascript"></script>
8   -
9   - <script type="application/javascript">
10   -
11   - /**
12   - * 账号密码登录
13   - */
14   - function login() {
15   - const clientId = 'yudao-sso-demo-by-password'; // 可以改写成,你的 clientId
16   - const clientSecret = 'test'; // 可以改写成,你的 clientSecret
17   - const grantType = 'password'; // 密码模式
18   -
19   - // 账号 + 密码
20   - const username = $('#username').val();
21   - const password = $('#password').val();
22   - if (username.length === 0 || password.length === 0) {
23   - alert('账号或密码未输入');
24   - return;
25   - }
26   -
27   - // 发起请求
28   - $.ajax({
29   - url: "http://127.0.0.1:48080/admin-api/system/oauth2/token?"
30   - // 客户端
31   - + "client_id=" + clientId
32   - + "&client_secret=" + clientSecret
33   - // 密码模式的参数
34   - + "&grant_type=" + grantType
35   - + "&username=" + username
36   - + "&password=" + password
37   - + '&scope=user.read user.write',
38   - method: 'POST',
39   - headers: {
40   - 'tenant-id': '1', // 多租户编号,写死
41   - },
42   - success: function (result) {
43   - if (result.code !== 0) {
44   - alert('登录失败,原因:' + result.msg)
45   - return;
46   - }
47   - // 设置到 localStorage 中
48   - localStorage.setItem('ACCESS-TOKEN', result.data.access_token);
49   - localStorage.setItem('REFRESH-TOKEN', result.data.refresh_token);
50   -
51   - // 提示登录成功
52   - alert('登录成功!点击确认,跳转回首页');
53   - window.location.href = '/index.html';
54   - }
55   - });
56   - }
57   - </script>
58   -</head>
59   -<body>
60   -账号:<input id="username" value="admin" /> <br />
61   -密码:<input id="password" value="admin123" > <br />
62   -<button style="float: right; margin-top: 5px;" onclick="login()">登录</button>
63   -</body>
64   -<style>
65   - body { /** 页面居中 */
66   - border-radius: 20px;
67   - height: 350px;
68   - position: absolute;
69   - left: 50%;
70   - top: 50%;
71   - transform: translate(-50%,-50%);
72   - }
73   -</style>
74   -</html>