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 index 6e179c49c..d2e160f6f 100644 --- 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 @@ -121,6 +121,32 @@ public class OAuth2Client { 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; 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 index 4dfa3d0b5..19b07e5b4 100644 --- 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 @@ -1,14 +1,17 @@ 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 javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; @RestController @RequestMapping("/auth") @@ -41,4 +44,20 @@ public class AuthController { return oauth2Client.refreshToken(refreshToken); } + /** + * 退出登录 + * + * @param request 请求 + * @return 成功 + */ + @PostMapping("/logout") + public CommonResult logout(HttpServletRequest request) { + String token = SecurityUtils.obtainAuthorization(request, "Authentication"); + 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/framework/config/SecurityConfiguration.java b/yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/framework/config/SecurityConfiguration.java index 5c8a4e4b7..1d19fcfdb 100644 --- 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 @@ -31,6 +31,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() // 2. 登录相关的接口,可匿名访问 .antMatchers("/auth/login-by-code").permitAll() + .antMatchers("/auth/refresh-token").permitAll() + .antMatchers("/auth/logout").permitAll() // last. 兜底规则,必须认证 .and().authorizeRequests() .anyRequest().authenticated(); 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 index a4bfa666e..9c0dac659 100644 --- 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 @@ -74,6 +74,36 @@ }); } + /** + * 刷新令牌 + */ + function logout() { + const accessToken = localStorage.getItem('ACCESS-TOKEN'); + if (!accessToken) { + location.reload(); + return; + } + $.ajax({ + url: "http://127.0.0.1:18080/auth/logout", + method: 'POST', + headers: { + 'Authentication': 'Bearer ' + accessToken + }, + success: function (result) { + if (result.code !== 0) { + alert('退出登录失败,原因:' + result.msg) + return; + } + alert('退出登录成功!'); + // 删除 localStorage 中 + localStorage.removeItem('ACCESS-TOKEN'); + localStorage.removeItem('REFRESH-TOKEN'); + + location.reload(); + } + }); + } + $(function () { const accessToken = localStorage.getItem('ACCESS-TOKEN'); // 情况一:未登录 @@ -111,7 +141,7 @@