diff --git a/pom.xml b/pom.xml index 3a66524bc1..d7a0ad627a 100644 --- a/pom.xml +++ b/pom.xml @@ -23,8 +23,6 @@ - - ${project.artifactId} diff --git a/yudao-example/pom.xml b/yudao-example/pom.xml deleted file mode 100644 index 82b85a5f96..0000000000 --- a/yudao-example/pom.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - 4.0.0 - - - - cn.iocoder.boot - yudao-example - 1.0.0-snapshot - pom - - yudao-sso-demo-by-code - yudao-sso-demo-by-password - - - ${project.artifactId} - 提供各种示例,例如说:SSO 单点登录 - https://github.com/YunaiV/ruoyi-vue-pro - - diff --git a/yudao-example/yudao-sso-demo-by-code/pom.xml b/yudao-example/yudao-sso-demo-by-code/pom.xml deleted file mode 100644 index 84f482b077..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - 4.0.0 - - - - cn.iocoder.boot - yudao-sso-demo-by-code - 1.0.0-snapshot - jar - - ${project.artifactId} - 基于授权码模式,如何实现 SSO 单点登录? - https://github.com/YunaiV/ruoyi-vue-pro - - - - 21 - 21 - UTF-8 - - 3.2.0 - - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring.boot.version} - pom - import - - - - - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-security - - - - cn.hutool - hutool-all - 5.8.22 - - - - org.projectlombok - lombok - true - - - - diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java deleted file mode 100644 index f6b160745d..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.iocoder.yudao.ssodemo; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SSODemoApplication { - - public static void main(String[] args) { - SpringApplication.run(SSODemoApplication.class, args); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java deleted file mode 100644 index d2e160f6f0..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java +++ /dev/null @@ -1,157 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.Base64Utils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -import java.nio.charset.StandardCharsets; - -/** - * OAuth 2.0 客户端 - * - * 对应调用 OAuth2OpenController 接口 - */ -@Component -public class OAuth2Client { - - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2"; - - /** - * 租户编号 - * - * 默认使用 1;如果使用别的租户,可以调整 - */ - public static final Long TENANT_ID = 1L; - - private static final String CLIENT_ID = "yudao-sso-demo-by-code"; - private static final String CLIENT_SECRET = "test"; - - -// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入 - private final RestTemplate restTemplate = new RestTemplate(); - - /** - * 使用 code 授权码,获得访问令牌 - * - * @param code 授权码 - * @param redirectUri 重定向 URI - * @return 访问令牌 - */ - public CommonResult postAccessToken(String code, String redirectUri) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("grant_type", "authorization_code"); - body.add("code", code); - body.add("redirect_uri", redirectUri); -// body.add("state", ""); // 选填;填了会校验 - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/token", - HttpMethod.POST, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - /** - * 校验访问令牌,并返回它的基本信息 - * - * @param token 访问令牌 - * @return 访问令牌的基本信息 - */ - public CommonResult checkToken(String token) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("token", token); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/check-token", - HttpMethod.POST, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - /** - * 使用刷新令牌,获得(刷新)访问令牌 - * - * @param refreshToken 刷新令牌 - * @return 访问令牌 - */ - public CommonResult refreshToken(String refreshToken) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("grant_type", "refresh_token"); - body.add("refresh_token", refreshToken); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/token", - HttpMethod.POST, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - /** - * 删除访问令牌 - * - * @param token 访问令牌 - * @return 成功 - */ - public CommonResult revokeToken(String token) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("token", token); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/token", - HttpMethod.DELETE, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - private static void addClientHeader(HttpHeaders headers) { - // client 拼接,需要 BASE64 编码 - String client = CLIENT_ID + ":" + CLIENT_SECRET; - client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8)); - headers.add("Authorization", "Basic " + client); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java deleted file mode 100644 index 666bd3ee4b..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO; -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -/** - * 用户 User 信息的客户端 - * - * 对应调用 OAuth2UserController 接口 - */ -@Component -public class UserClient { - - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user"; - - // @Resource // 可优化,注册一个 RestTemplate Bean,然后注入 - private final RestTemplate restTemplate = new RestTemplate(); - - public CommonResult getUser() { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString()); - addTokenHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/get", - HttpMethod.GET, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - public CommonResult updateUser(UserUpdateReqDTO updateReqDTO) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString()); - addTokenHeader(headers); - // 1.2 构建请求参数 - // 使用 updateReqDTO 即可 - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/update", - HttpMethod.PUT, - new HttpEntity<>(updateReqDTO, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - - private static void addTokenHeader(HttpHeaders headers) { - LoginUser loginUser = SecurityUtils.getLoginUser(); - Assert.notNull(loginUser, "登录用户不能为空"); - headers.add("Authorization", "Bearer " + loginUser.getAccessToken()); - } -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java deleted file mode 100644 index 548fe51e4c..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto; - -import lombok.Data; - -import java.io.Serializable; - -/** - * 通用返回 - * - * @param 数据泛型 - */ -@Data -public class CommonResult implements Serializable { - - /** - * 错误码 - */ - private Integer code; - /** - * 返回数据 - */ - private T data; - /** - * 错误提示,用户可阅读 - */ - private String msg; - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java deleted file mode 100644 index 6a5369a207..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.oauth2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 访问令牌 Response DTO - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2AccessTokenRespDTO { - - /** - * 访问令牌 - */ - @JsonProperty("access_token") - private String accessToken; - - /** - * 刷新令牌 - */ - @JsonProperty("refresh_token") - private String refreshToken; - - /** - * 令牌类型 - */ - @JsonProperty("token_type") - private String tokenType; - - /** - * 过期时间;单位:秒 - */ - @JsonProperty("expires_in") - private Long expiresIn; - - /** - * 授权范围;如果多个授权范围,使用空格分隔 - */ - private String scope; - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java deleted file mode 100644 index 862bcf04bc..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.oauth2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * 校验令牌 Response DTO - * - * @author 芋道源码 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2CheckTokenRespDTO { - - /** - * 用户编号 - */ - @JsonProperty("user_id") - private Long userId; - /** - * 用户类型 - */ - @JsonProperty("user_type") - private Integer userType; - /** - * 租户编号 - */ - @JsonProperty("tenant_id") - private Long tenantId; - - /** - * 客户端编号 - */ - @JsonProperty("client_id") - private String clientId; - /** - * 授权范围 - */ - private List scopes; - - /** - * 访问令牌 - */ - @JsonProperty("access_token") - private String accessToken; - - /** - * 过期时间 - * - * 时间戳 / 1000,即单位:秒 - */ - private Long exp; - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java deleted file mode 100644 index e81bea9eb8..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java +++ /dev/null @@ -1,97 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * 获得用户基本信息 Response dto - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserInfoRespDTO { - - /** - * 用户编号 - */ - private Long id; - - /** - * 用户账号 - */ - private String username; - - /** - * 用户昵称 - */ - private String nickname; - - /** - * 用户邮箱 - */ - private String email; - /** - * 手机号码 - */ - private String mobile; - - /** - * 用户性别 - */ - private Integer sex; - - /** - * 用户头像 - */ - private String avatar; - - /** - * 所在部门 - */ - private Dept dept; - - /** - * 所属岗位数组 - */ - private List posts; - - /** - * 部门 - */ - @Data - public static class Dept { - - /** - * 部门编号 - */ - private Long id; - - /** - * 部门名称 - */ - private String name; - - } - - /** - * 岗位 - */ - @Data - public static class Post { - - /** - * 岗位编号 - */ - private Long id; - - /** - * 岗位名称 - */ - private String name; - - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java deleted file mode 100644 index e711d73111..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 更新用户基本信息 Request DTO - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserUpdateReqDTO { - - /** - * 用户昵称 - */ - private String nickname; - - /** - * 用户邮箱 - */ - private String email; - - /** - * 手机号码 - */ - private String mobile; - - /** - * 用户性别 - */ - private Integer sex; - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java deleted file mode 100644 index 63257fcfd9..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.yudao.ssodemo.controller; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.ssodemo.client.OAuth2Client; -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; - -@RestController -@RequestMapping("/auth") -public class AuthController { - - @Resource - private OAuth2Client oauth2Client; - - /** - * 使用 code 访问令牌,获得访问令牌 - * - * @param code 授权码 - * @param redirectUri 重定向 URI - * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段 - */ - @PostMapping("/login-by-code") - public CommonResult loginByCode(@RequestParam("code") String code, - @RequestParam("redirectUri") String redirectUri) { - return oauth2Client.postAccessToken(code, redirectUri); - } - - /** - * 使用刷新令牌,获得(刷新)访问令牌 - * - * @param refreshToken 刷新令牌 - * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段 - */ - @PostMapping("/refresh-token") - public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { - return oauth2Client.refreshToken(refreshToken); - } - - /** - * 退出登录 - * - * @param request 请求 - * @return 成功 - */ - @PostMapping("/logout") - public CommonResult logout(HttpServletRequest request) { - String token = SecurityUtils.obtainAuthorization(request, "Authorization"); - if (StrUtil.isNotBlank(token)) { - return oauth2Client.revokeToken(token); - } - // 返回成功 - return new CommonResult<>(); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java deleted file mode 100644 index 6f6a2357ac..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.ssodemo.controller; - -import cn.iocoder.yudao.ssodemo.client.UserClient; -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; - -@RestController -@RequestMapping("/user") -public class UserController { - - @Resource - private UserClient userClient; - - /** - * 获得当前登录用户的基本信息 - * - * @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段 - */ - @GetMapping("/get") - public CommonResult getUser() { - return userClient.getUser(); - } - - /** - * 更新当前登录用户的昵称 - * - * @param nickname 昵称 - * @return 成功 - */ - @PutMapping("/update") - public CommonResult updateUser(@RequestParam("nickname") String nickname) { - UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null); - return userClient.updateUser(updateReqDTO); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java deleted file mode 100644 index 6c90098f29..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.config; - -import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter; -import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -import jakarta.annotation.Resource; - -@Configuration(proxyBeanMethods = false) -@EnableWebSecurity -public class SecurityConfiguration{ - - @Resource - private TokenAuthenticationFilter tokenAuthenticationFilter; - - @Resource - private AccessDeniedHandlerImpl accessDeniedHandler; - @Resource - private AuthenticationEntryPoint authenticationEntryPoint; - - @Bean - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { - // 设置 URL 安全权限 - httpSecurity - // 开启跨域 - .cors(Customizer.withDefaults()) - // CSRF 禁用,因为不使用 Session - .csrf(AbstractHttpConfigurer::disable) - // 一堆自定义的 Spring Security 处理器 - .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler)); - - // 设置每个请求的权限 - httpSecurity.authorizeHttpRequests(c -> c - // 1. 静态资源,可匿名访问 - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll() - // 2. 登录相关的接口,可匿名访问 - .requestMatchers("/auth/login-by-code").permitAll() - .requestMatchers("/auth/refresh-token").permitAll() - .requestMatchers("/auth/logout").permitAll()) - // 3. 兜底规则,必须认证 - .authorizeHttpRequests(c -> c.anyRequest().authenticated()); - - // 添加 Token Filter - httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - return httpSecurity.build(); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java deleted file mode 100644 index 44f3edf547..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core; - -import lombok.Data; - -import java.util.List; - -/** - * 登录用户信息 - * - * @author 芋道源码 - */ -@Data -public class LoginUser { - - /** - * 用户编号 - */ - private Long id; - /** - * 用户类型 - */ - private Integer userType; - /** - * 租户编号 - */ - private Long tenantId; - /** - * 授权范围 - */ - private List scopes; - - /** - * 访问令牌 - */ - private String accessToken; - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java deleted file mode 100644 index 511371ce24..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.filter; - -import cn.iocoder.yudao.ssodemo.client.OAuth2Client; -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO; -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import jakarta.annotation.Resource; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Token 过滤器,验证 token 的有效性 - * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 - * - * @author 芋道源码 - */ -@Component -public class TokenAuthenticationFilter extends OncePerRequestFilter { - - @Resource - private OAuth2Client oauth2Client; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - // 1. 获得访问令牌 - String token = SecurityUtils.obtainAuthorization(request, "Authorization"); - if (StringUtils.hasText(token)) { - // 2. 基于 token 构建登录用户 - LoginUser loginUser = buildLoginUserByToken(token); - // 3. 设置当前用户 - if (loginUser != null) { - SecurityUtils.setLoginUser(loginUser, request); - } - } - - // 继续过滤链 - filterChain.doFilter(request, response); - } - - private LoginUser buildLoginUserByToken(String token) { - try { - CommonResult accessTokenResult = oauth2Client.checkToken(token); - OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData(); - if (accessToken == null) { - return null; - } - // 构建登录用户 - return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) - .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()) - .setAccessToken(accessToken.getAccessToken()); - } catch (Exception exception) { - // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 - return null; - } - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java deleted file mode 100644 index e0c1a141d4..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.handler; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.stereotype.Component; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。 - * - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类 - * - * @author 芋道源码 - */ -@Component -@SuppressWarnings("JavadocReference") -@Slf4j -public class AccessDeniedHandlerImpl implements AccessDeniedHandler { - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) - throws IOException, ServletException { - // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏 - log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(), - SecurityUtils.getLoginUserId(), e); - // 返回 403 - CommonResult result = new CommonResult<>(); - result.setCode(HttpStatus.FORBIDDEN.value()); - result.setMsg("没有该操作权限"); - ServletUtils.writeJSON(response, result); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java deleted file mode 100644 index 950a3317aa..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.handler; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.stereotype.Component; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -/** - * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页 - * - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类 - */ -@Component -@Slf4j -@SuppressWarnings("JavadocReference") // 忽略文档引用报错 -public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { - log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e); - // 返回 401 - CommonResult result = new CommonResult<>(); - result.setCode(HttpStatus.UNAUTHORIZED.value()); - result.setMsg("账号未登录"); - ServletUtils.writeJSON(response, result); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java deleted file mode 100644 index ad70aae25f..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.util; - -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser; -import org.springframework.lang.Nullable; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.util.StringUtils; - -import jakarta.servlet.http.HttpServletRequest; -import java.util.Collections; - -/** - * 安全服务工具类 - * - * @author 芋道源码 - */ -public class SecurityUtils { - - public static final String AUTHORIZATION_BEARER = "Bearer"; - - private SecurityUtils() {} - - /** - * 从请求中,获得认证 Token - * - * @param request 请求 - * @param header 认证 Token 对应的 Header 名字 - * @return 认证 Token - */ - public static String obtainAuthorization(HttpServletRequest request, String header) { - String authorization = request.getHeader(header); - if (!StringUtils.hasText(authorization)) { - return null; - } - int index = authorization.indexOf(AUTHORIZATION_BEARER + " "); - if (index == -1) { // 未找到 - return null; - } - return authorization.substring(index + 7).trim(); - } - - /** - * 获得当前认证信息 - * - * @return 认证信息 - */ - public static Authentication getAuthentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context == null) { - return null; - } - return context.getAuthentication(); - } - - /** - * 获取当前用户 - * - * @return 当前用户 - */ - @Nullable - public static LoginUser getLoginUser() { - Authentication authentication = getAuthentication(); - if (authentication == null) { - return null; - } - return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null; - } - - /** - * 获得当前用户的编号,从上下文中 - * - * @return 用户编号 - */ - @Nullable - public static Long getLoginUserId() { - LoginUser loginUser = getLoginUser(); - return loginUser != null ? loginUser.getId() : null; - } - - /** - * 设置当前用户 - * - * @param loginUser 登录用户 - * @param request 请求 - */ - public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) { - // 创建 Authentication,并设置到上下文 - Authentication authentication = buildAuthentication(loginUser, request); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { - // 创建 UsernamePasswordAuthenticationToken 对象 - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - loginUser, null, Collections.emptyList()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - return authenticationToken; - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java deleted file mode 100644 index 644f836b22..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.util; - -import cn.hutool.extra.servlet.JakartaServletUtil; -import cn.hutool.extra.servlet.ServletUtil; -import cn.hutool.json.JSONUtil; -import org.springframework.http.MediaType; - -import jakarta.servlet.http.HttpServletResponse; - -/** - * 客户端工具类 - * - * @author 芋道源码 - */ -public class ServletUtils { - - /** - * 返回 JSON 字符串 - * - * @param response 响应 - * @param object 对象,会序列化成 JSON 字符串 - */ - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 - public static void writeJSON(HttpServletResponse response, Object object) { - String content = JSONUtil.toJsonStr(object); - JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); - } - - public static void write(HttpServletResponse response, String text, String contentType) { - JakartaServletUtil.write(response, text, contentType); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/resources/application.yaml b/yudao-example/yudao-sso-demo-by-code/src/main/resources/application.yaml deleted file mode 100644 index a62cf97dc1..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/resources/application.yaml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 18080 diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/resources/static/callback.html b/yudao-example/yudao-sso-demo-by-code/src/main/resources/static/callback.html deleted file mode 100644 index 123a1af9bc..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/resources/static/callback.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - SSO 授权后的回调页 - - - - - - - - -正在使用 code 授权码,进行 accessToken 访问令牌的获取 - - diff --git a/yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html b/yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html deleted file mode 100644 index a4f858363e..0000000000 --- a/yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html +++ /dev/null @@ -1,159 +0,0 @@ - - - - - 首页 - - - - - - - - - 您未登录,点击 跳转 SSO 单点登录 - - - - - 您已登录!退出登录 - 昵称: 加载中... 修改昵称 - 访问令牌: 加载中... 刷新令牌 - - - - diff --git a/yudao-example/yudao-sso-demo-by-password/pom.xml b/yudao-example/yudao-sso-demo-by-password/pom.xml deleted file mode 100644 index 8c2b6b81ae..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/pom.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - 4.0.0 - - - - cn.iocoder.boot - yudao-sso-demo-by-password - 1.0.0-snapshot - jar - - ${project.artifactId} - 基于密码模式,如何实现 SSO 单点登录? - https://github.com/YunaiV/ruoyi-vue-pro - - - - 21 - 21 - UTF-8 - - 3.2.0 - - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring.boot.version} - pom - import - - - - - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-security - - - - cn.hutool - hutool-all - 5.8.22 - - - - org.projectlombok - lombok - true - - - - diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java deleted file mode 100644 index f6b160745d..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/SSODemoApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package cn.iocoder.yudao.ssodemo; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SSODemoApplication { - - public static void main(String[] args) { - SpringApplication.run(SSODemoApplication.class, args); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java deleted file mode 100644 index 4f6b634818..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java +++ /dev/null @@ -1,127 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.Base64Utils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -import java.nio.charset.StandardCharsets; - -/** - * OAuth 2.0 客户端 - * - * 对应调用 OAuth2OpenController 接口 - */ -@Component -public class OAuth2Client { - - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api/system/oauth2"; - - /** - * 租户编号 - * - * 默认使用 1;如果使用别的租户,可以调整 - */ - public static final Long TENANT_ID = 1L; - - private static final String CLIENT_ID = "yudao-sso-demo-by-password"; - private static final String CLIENT_SECRET = "test"; - - -// @Resource // 可优化,注册一个 RestTemplate Bean,然后注入 - private final RestTemplate restTemplate = new RestTemplate(); - - /** - * 校验访问令牌,并返回它的基本信息 - * - * @param token 访问令牌 - * @return 访问令牌的基本信息 - */ - public CommonResult checkToken(String token) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("token", token); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/check-token", - HttpMethod.POST, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - /** - * 使用刷新令牌,获得(刷新)访问令牌 - * - * @param refreshToken 刷新令牌 - * @return 访问令牌 - */ - public CommonResult refreshToken(String refreshToken) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("grant_type", "refresh_token"); - body.add("refresh_token", refreshToken); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/token", - HttpMethod.POST, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - /** - * 删除访问令牌 - * - * @param token 访问令牌 - * @return 成功 - */ - public CommonResult revokeToken(String token) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", TENANT_ID.toString()); - addClientHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("token", token); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/token", - HttpMethod.DELETE, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - private static void addClientHeader(HttpHeaders headers) { - // client 拼接,需要 BASE64 编码 - String client = CLIENT_ID + ":" + CLIENT_SECRET; - client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8)); - headers.add("Authorization", "Basic " + client); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java deleted file mode 100644 index 666bd3ee4b..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/UserClient.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO; -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import org.springframework.core.ParameterizedTypeReference; -import org.springframework.http.*; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -/** - * 用户 User 信息的客户端 - * - * 对应调用 OAuth2UserController 接口 - */ -@Component -public class UserClient { - - private static final String BASE_URL = "http://127.0.0.1:48080/admin-api//system/oauth2/user"; - - // @Resource // 可优化,注册一个 RestTemplate Bean,然后注入 - private final RestTemplate restTemplate = new RestTemplate(); - - public CommonResult getUser() { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString()); - addTokenHeader(headers); - // 1.2 构建请求参数 - MultiValueMap body = new LinkedMultiValueMap<>(); - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/get", - HttpMethod.GET, - new HttpEntity<>(body, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - public CommonResult updateUser(UserUpdateReqDTO updateReqDTO) { - // 1.1 构建请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("tenant-id", OAuth2Client.TENANT_ID.toString()); - addTokenHeader(headers); - // 1.2 构建请求参数 - // 使用 updateReqDTO 即可 - - // 2. 执行请求 - ResponseEntity> exchange = restTemplate.exchange( - BASE_URL + "/update", - HttpMethod.PUT, - new HttpEntity<>(updateReqDTO, headers), - new ParameterizedTypeReference>() {}); // 解决 CommonResult 的泛型丢失 - Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功"); - return exchange.getBody(); - } - - - private static void addTokenHeader(HttpHeaders headers) { - LoginUser loginUser = SecurityUtils.getLoginUser(); - Assert.notNull(loginUser, "登录用户不能为空"); - headers.add("Authorization", "Bearer " + loginUser.getAccessToken()); - } -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java deleted file mode 100644 index 548fe51e4c..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/CommonResult.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto; - -import lombok.Data; - -import java.io.Serializable; - -/** - * 通用返回 - * - * @param 数据泛型 - */ -@Data -public class CommonResult implements Serializable { - - /** - * 错误码 - */ - private Integer code; - /** - * 返回数据 - */ - private T data; - /** - * 错误提示,用户可阅读 - */ - private String msg; - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java deleted file mode 100644 index 6a5369a207..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2AccessTokenRespDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.oauth2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 访问令牌 Response DTO - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2AccessTokenRespDTO { - - /** - * 访问令牌 - */ - @JsonProperty("access_token") - private String accessToken; - - /** - * 刷新令牌 - */ - @JsonProperty("refresh_token") - private String refreshToken; - - /** - * 令牌类型 - */ - @JsonProperty("token_type") - private String tokenType; - - /** - * 过期时间;单位:秒 - */ - @JsonProperty("expires_in") - private Long expiresIn; - - /** - * 授权范围;如果多个授权范围,使用空格分隔 - */ - private String scope; - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java deleted file mode 100644 index 862bcf04bc..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/oauth2/OAuth2CheckTokenRespDTO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.oauth2; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * 校验令牌 Response DTO - * - * @author 芋道源码 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2CheckTokenRespDTO { - - /** - * 用户编号 - */ - @JsonProperty("user_id") - private Long userId; - /** - * 用户类型 - */ - @JsonProperty("user_type") - private Integer userType; - /** - * 租户编号 - */ - @JsonProperty("tenant_id") - private Long tenantId; - - /** - * 客户端编号 - */ - @JsonProperty("client_id") - private String clientId; - /** - * 授权范围 - */ - private List scopes; - - /** - * 访问令牌 - */ - @JsonProperty("access_token") - private String accessToken; - - /** - * 过期时间 - * - * 时间戳 / 1000,即单位:秒 - */ - private Long exp; - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java deleted file mode 100644 index e81bea9eb8..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserInfoRespDTO.java +++ /dev/null @@ -1,97 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * 获得用户基本信息 Response dto - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserInfoRespDTO { - - /** - * 用户编号 - */ - private Long id; - - /** - * 用户账号 - */ - private String username; - - /** - * 用户昵称 - */ - private String nickname; - - /** - * 用户邮箱 - */ - private String email; - /** - * 手机号码 - */ - private String mobile; - - /** - * 用户性别 - */ - private Integer sex; - - /** - * 用户头像 - */ - private String avatar; - - /** - * 所在部门 - */ - private Dept dept; - - /** - * 所属岗位数组 - */ - private List posts; - - /** - * 部门 - */ - @Data - public static class Dept { - - /** - * 部门编号 - */ - private Long id; - - /** - * 部门名称 - */ - private String name; - - } - - /** - * 岗位 - */ - @Data - public static class Post { - - /** - * 岗位编号 - */ - private Long id; - - /** - * 岗位名称 - */ - private String name; - - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java deleted file mode 100644 index e711d73111..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/client/dto/user/UserUpdateReqDTO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.ssodemo.client.dto.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 更新用户基本信息 Request DTO - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserUpdateReqDTO { - - /** - * 用户昵称 - */ - private String nickname; - - /** - * 用户邮箱 - */ - private String email; - - /** - * 手机号码 - */ - private String mobile; - - /** - * 用户性别 - */ - private Integer sex; - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java deleted file mode 100644 index 3011ad81fa..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.ssodemo.controller; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.ssodemo.client.OAuth2Client; -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletRequest; - -@RestController -@RequestMapping("/auth") -public class AuthController { - - @Resource - private OAuth2Client oauth2Client; - - /** - * 使用刷新令牌,获得(刷新)访问令牌 - * - * @param refreshToken 刷新令牌 - * @return 访问令牌;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段 - */ - @PostMapping("/refresh-token") - public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { - return oauth2Client.refreshToken(refreshToken); - } - - /** - * 退出登录 - * - * @param request 请求 - * @return 成功 - */ - @PostMapping("/logout") - public CommonResult logout(HttpServletRequest request) { - String token = SecurityUtils.obtainAuthorization(request, "Authorization"); - if (StrUtil.isNotBlank(token)) { - return oauth2Client.revokeToken(token); - } - // 返回成功 - return new CommonResult<>(); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java deleted file mode 100644 index 6f6a2357ac..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/controller/UserController.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.ssodemo.controller; - -import cn.iocoder.yudao.ssodemo.client.UserClient; -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserInfoRespDTO; -import cn.iocoder.yudao.ssodemo.client.dto.user.UserUpdateReqDTO; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; - -@RestController -@RequestMapping("/user") -public class UserController { - - @Resource - private UserClient userClient; - - /** - * 获得当前登录用户的基本信息 - * - * @return 用户信息;注意,实际项目中,最好创建对应的 ResponseVO 类,只返回必要的字段 - */ - @GetMapping("/get") - public CommonResult getUser() { - return userClient.getUser(); - } - - /** - * 更新当前登录用户的昵称 - * - * @param nickname 昵称 - * @return 成功 - */ - @PutMapping("/update") - public CommonResult updateUser(@RequestParam("nickname") String nickname) { - UserUpdateReqDTO updateReqDTO = new UserUpdateReqDTO(nickname, null, null, null); - return userClient.updateUser(updateReqDTO); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java deleted file mode 100644 index b90f650e2e..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.config; - -import cn.iocoder.yudao.ssodemo.framework.core.filter.TokenAuthenticationFilter; -import cn.iocoder.yudao.ssodemo.framework.core.handler.AccessDeniedHandlerImpl; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -import jakarta.annotation.Resource; - -@Configuration(proxyBeanMethods = false) -@EnableWebSecurity -public class SecurityConfiguration { - - @Resource - private TokenAuthenticationFilter tokenAuthenticationFilter; - - @Resource - private AccessDeniedHandlerImpl accessDeniedHandler; - @Resource - private AuthenticationEntryPoint authenticationEntryPoint; - - @Bean - protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { - // 设置 URL 安全权限 - httpSecurity - // 开启跨域 - .cors(Customizer.withDefaults()) - // CSRF 禁用,因为不使用 Session - .csrf(AbstractHttpConfigurer::disable) - // 一堆自定义的 Spring Security 处理器 - .exceptionHandling(c -> c.authenticationEntryPoint(authenticationEntryPoint) - .accessDeniedHandler(accessDeniedHandler)); - - // 设置每个请求的权限 - httpSecurity.authorizeHttpRequests(c -> c - // 1. 静态资源,可匿名访问 - .requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll() - // 2. 登录相关的接口,可匿名访问 - .requestMatchers("/auth/login-by-code").permitAll() - .requestMatchers("/auth/refresh-token").permitAll() - .requestMatchers("/auth/logout").permitAll()) - // 3. 兜底规则,必须认证 - .authorizeHttpRequests(c -> c.anyRequest().authenticated()); - - // 添加 Token Filter - httpSecurity.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - return httpSecurity.build(); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java deleted file mode 100644 index 44f3edf547..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/LoginUser.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core; - -import lombok.Data; - -import java.util.List; - -/** - * 登录用户信息 - * - * @author 芋道源码 - */ -@Data -public class LoginUser { - - /** - * 用户编号 - */ - private Long id; - /** - * 用户类型 - */ - private Integer userType; - /** - * 租户编号 - */ - private Long tenantId; - /** - * 授权范围 - */ - private List scopes; - - /** - * 访问令牌 - */ - private String accessToken; - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java deleted file mode 100644 index 511371ce24..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/filter/TokenAuthenticationFilter.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.filter; - -import cn.iocoder.yudao.ssodemo.client.OAuth2Client; -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2CheckTokenRespDTO; -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import jakarta.annotation.Resource; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * Token 过滤器,验证 token 的有效性 - * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 - * - * @author 芋道源码 - */ -@Component -public class TokenAuthenticationFilter extends OncePerRequestFilter { - - @Resource - private OAuth2Client oauth2Client; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - // 1. 获得访问令牌 - String token = SecurityUtils.obtainAuthorization(request, "Authorization"); - if (StringUtils.hasText(token)) { - // 2. 基于 token 构建登录用户 - LoginUser loginUser = buildLoginUserByToken(token); - // 3. 设置当前用户 - if (loginUser != null) { - SecurityUtils.setLoginUser(loginUser, request); - } - } - - // 继续过滤链 - filterChain.doFilter(request, response); - } - - private LoginUser buildLoginUserByToken(String token) { - try { - CommonResult accessTokenResult = oauth2Client.checkToken(token); - OAuth2CheckTokenRespDTO accessToken = accessTokenResult.getData(); - if (accessToken == null) { - return null; - } - // 构建登录用户 - return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) - .setTenantId(accessToken.getTenantId()).setScopes(accessToken.getScopes()) - .setAccessToken(accessToken.getAccessToken()); - } catch (Exception exception) { - // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 - return null; - } - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java deleted file mode 100644 index e0c1a141d4..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AccessDeniedHandlerImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.handler; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils; -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.web.access.AccessDeniedHandler; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.stereotype.Component; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。 - * - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类 - * - * @author 芋道源码 - */ -@Component -@SuppressWarnings("JavadocReference") -@Slf4j -public class AccessDeniedHandlerImpl implements AccessDeniedHandler { - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) - throws IOException, ServletException { - // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏 - log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(), - SecurityUtils.getLoginUserId(), e); - // 返回 403 - CommonResult result = new CommonResult<>(); - result.setCode(HttpStatus.FORBIDDEN.value()); - result.setMsg("没有该操作权限"); - ServletUtils.writeJSON(response, result); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java deleted file mode 100644 index 950a3317aa..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/handler/AuthenticationEntryPointImpl.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.handler; - -import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; -import cn.iocoder.yudao.ssodemo.framework.core.util.ServletUtils; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.access.ExceptionTranslationFilter; -import org.springframework.stereotype.Component; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -/** - * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页 - * - * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类 - */ -@Component -@Slf4j -@SuppressWarnings("JavadocReference") // 忽略文档引用报错 -public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { - log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e); - // 返回 401 - CommonResult result = new CommonResult<>(); - result.setCode(HttpStatus.UNAUTHORIZED.value()); - result.setMsg("账号未登录"); - ServletUtils.writeJSON(response, result); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java deleted file mode 100644 index ad70aae25f..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/SecurityUtils.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.util; - -import cn.iocoder.yudao.ssodemo.framework.core.LoginUser; -import org.springframework.lang.Nullable; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.util.StringUtils; - -import jakarta.servlet.http.HttpServletRequest; -import java.util.Collections; - -/** - * 安全服务工具类 - * - * @author 芋道源码 - */ -public class SecurityUtils { - - public static final String AUTHORIZATION_BEARER = "Bearer"; - - private SecurityUtils() {} - - /** - * 从请求中,获得认证 Token - * - * @param request 请求 - * @param header 认证 Token 对应的 Header 名字 - * @return 认证 Token - */ - public static String obtainAuthorization(HttpServletRequest request, String header) { - String authorization = request.getHeader(header); - if (!StringUtils.hasText(authorization)) { - return null; - } - int index = authorization.indexOf(AUTHORIZATION_BEARER + " "); - if (index == -1) { // 未找到 - return null; - } - return authorization.substring(index + 7).trim(); - } - - /** - * 获得当前认证信息 - * - * @return 认证信息 - */ - public static Authentication getAuthentication() { - SecurityContext context = SecurityContextHolder.getContext(); - if (context == null) { - return null; - } - return context.getAuthentication(); - } - - /** - * 获取当前用户 - * - * @return 当前用户 - */ - @Nullable - public static LoginUser getLoginUser() { - Authentication authentication = getAuthentication(); - if (authentication == null) { - return null; - } - return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null; - } - - /** - * 获得当前用户的编号,从上下文中 - * - * @return 用户编号 - */ - @Nullable - public static Long getLoginUserId() { - LoginUser loginUser = getLoginUser(); - return loginUser != null ? loginUser.getId() : null; - } - - /** - * 设置当前用户 - * - * @param loginUser 登录用户 - * @param request 请求 - */ - public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) { - // 创建 Authentication,并设置到上下文 - Authentication authentication = buildAuthentication(loginUser, request); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { - // 创建 UsernamePasswordAuthenticationToken 对象 - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - loginUser, null, Collections.emptyList()); - authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - return authenticationToken; - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java b/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java deleted file mode 100644 index 644f836b22..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/java/cn/iocoder/yudao/ssodemo/framework/core/util/ServletUtils.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.ssodemo.framework.core.util; - -import cn.hutool.extra.servlet.JakartaServletUtil; -import cn.hutool.extra.servlet.ServletUtil; -import cn.hutool.json.JSONUtil; -import org.springframework.http.MediaType; - -import jakarta.servlet.http.HttpServletResponse; - -/** - * 客户端工具类 - * - * @author 芋道源码 - */ -public class ServletUtils { - - /** - * 返回 JSON 字符串 - * - * @param response 响应 - * @param object 对象,会序列化成 JSON 字符串 - */ - @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 - public static void writeJSON(HttpServletResponse response, Object object) { - String content = JSONUtil.toJsonStr(object); - JakartaServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); - } - - public static void write(HttpServletResponse response, String text, String contentType) { - JakartaServletUtil.write(response, text, contentType); - } - -} diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/resources/application.yaml b/yudao-example/yudao-sso-demo-by-password/src/main/resources/application.yaml deleted file mode 100644 index a62cf97dc1..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/resources/application.yaml +++ /dev/null @@ -1,2 +0,0 @@ -server: - port: 18080 diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/resources/static/index.html b/yudao-example/yudao-sso-demo-by-password/src/main/resources/static/index.html deleted file mode 100644 index 8a5372901d..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/resources/static/index.html +++ /dev/null @@ -1,154 +0,0 @@ - - - - - 首页 - - - - - - - - - 您未登录,点击 跳转 账号密码登录 - - - - - 您已登录!退出登录 - 昵称: 加载中... 修改昵称 - 访问令牌: 加载中... 刷新令牌 - - - - diff --git a/yudao-example/yudao-sso-demo-by-password/src/main/resources/static/login.html b/yudao-example/yudao-sso-demo-by-password/src/main/resources/static/login.html deleted file mode 100644 index 79b866bf2c..0000000000 --- a/yudao-example/yudao-sso-demo-by-password/src/main/resources/static/login.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - 登录 - - - - - - -账号: -密码: -登录 - - -