Commit 16b8f3ba9484cb936664c84bfbc6caae42d525f8
1 parent
1d00261c
【移除】移除 yudao-example 项目,统一到 https://gitee.com/yudaocode/yudao-demo
Showing
44 changed files
with
0 additions
and
2537 deletions
pom.xml
| @@ -23,8 +23,6 @@ | @@ -23,8 +23,6 @@ | ||
| 23 | <!-- <module>yudao-module-mall</module>--> | 23 | <!-- <module>yudao-module-mall</module>--> |
| 24 | <!-- <module>yudao-module-crm</module>--> | 24 | <!-- <module>yudao-module-crm</module>--> |
| 25 | <!-- <module>yudao-module-erp</module>--> | 25 | <!-- <module>yudao-module-erp</module>--> |
| 26 | - <!-- 示例项目 --> | ||
| 27 | -<!-- <module>yudao-example</module>--> | ||
| 28 | </modules> | 26 | </modules> |
| 29 | 27 | ||
| 30 | <name>${project.artifactId}</name> | 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
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
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> |