Commit ab40254bc1591644394e07f34762eef2a598cc53

Authored by YunaiV
1 parent ff54f169

完成 yudao-sso-demo-by-code 退出登录

yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/client/OAuth2Client.java
@@ -121,6 +121,32 @@ public class OAuth2Client { @@ -121,6 +121,32 @@ public class OAuth2Client {
121 return exchange.getBody(); 121 return exchange.getBody();
122 } 122 }
123 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 +
124 private static void addClientHeader(HttpHeaders headers) { 150 private static void addClientHeader(HttpHeaders headers) {
125 // client 拼接,需要 BASE64 编码 151 // client 拼接,需要 BASE64 编码
126 String client = CLIENT_ID + ":" + CLIENT_SECRET; 152 String client = CLIENT_ID + ":" + CLIENT_SECRET;
yudao-example/yudao-sso-demo-by-code/src/main/java/cn/iocoder/yudao/ssodemo/controller/AuthController.java
1 package cn.iocoder.yudao.ssodemo.controller; 1 package cn.iocoder.yudao.ssodemo.controller;
2 2
  3 +import cn.hutool.core.util.StrUtil;
3 import cn.iocoder.yudao.ssodemo.client.OAuth2Client; 4 import cn.iocoder.yudao.ssodemo.client.OAuth2Client;
4 import cn.iocoder.yudao.ssodemo.client.dto.CommonResult; 5 import cn.iocoder.yudao.ssodemo.client.dto.CommonResult;
5 import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO; 6 import cn.iocoder.yudao.ssodemo.client.dto.oauth2.OAuth2AccessTokenRespDTO;
  7 +import cn.iocoder.yudao.ssodemo.framework.core.util.SecurityUtils;
6 import org.springframework.web.bind.annotation.PostMapping; 8 import org.springframework.web.bind.annotation.PostMapping;
7 import org.springframework.web.bind.annotation.RequestMapping; 9 import org.springframework.web.bind.annotation.RequestMapping;
8 import org.springframework.web.bind.annotation.RequestParam; 10 import org.springframework.web.bind.annotation.RequestParam;
9 import org.springframework.web.bind.annotation.RestController; 11 import org.springframework.web.bind.annotation.RestController;
10 12
11 import javax.annotation.Resource; 13 import javax.annotation.Resource;
  14 +import javax.servlet.http.HttpServletRequest;
12 15
13 @RestController 16 @RestController
14 @RequestMapping("/auth") 17 @RequestMapping("/auth")
@@ -41,4 +44,20 @@ public class AuthController { @@ -41,4 +44,20 @@ public class AuthController {
41 return oauth2Client.refreshToken(refreshToken); 44 return oauth2Client.refreshToken(refreshToken);
42 } 45 }
43 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, "Authentication");
  56 + if (StrUtil.isNotBlank(token)) {
  57 + return oauth2Client.revokeToken(token);
  58 + }
  59 + // 返回成功
  60 + return new CommonResult<>();
  61 + }
  62 +
44 } 63 }
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 { @@ -31,6 +31,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
31 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() 31 .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
32 // 2. 登录相关的接口,可匿名访问 32 // 2. 登录相关的接口,可匿名访问
33 .antMatchers("/auth/login-by-code").permitAll() 33 .antMatchers("/auth/login-by-code").permitAll()
  34 + .antMatchers("/auth/refresh-token").permitAll()
  35 + .antMatchers("/auth/logout").permitAll()
34 // last. 兜底规则,必须认证 36 // last. 兜底规则,必须认证
35 .and().authorizeRequests() 37 .and().authorizeRequests()
36 .anyRequest().authenticated(); 38 .anyRequest().authenticated();
yudao-example/yudao-sso-demo-by-code/src/main/resources/static/index.html
@@ -74,6 +74,36 @@ @@ -74,6 +74,36 @@
74 }); 74 });
75 } 75 }
76 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 + 'Authentication': '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 +
77 $(function () { 107 $(function () {
78 const accessToken = localStorage.getItem('ACCESS-TOKEN'); 108 const accessToken = localStorage.getItem('ACCESS-TOKEN');
79 // 情况一:未登录 109 // 情况一:未登录
@@ -111,7 +141,7 @@ @@ -111,7 +141,7 @@
111 141
112 <!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 --> 142 <!-- 情况二:已登录:1)展示用户信息;2)刷新访问令牌;3)退出登录 -->
113 <div id="yesLoginDiv" style="display: none"> 143 <div id="yesLoginDiv" style="display: none">
114 - 您已登录!<button>退出登录</button> <br /> 144 + 您已登录!<button onclick="logout()">退出登录</button> <br />
115 昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br /> 145 昵称:<span id="nicknameSpan"> 加载中... </span> <button onclick="updateNickname()">修改昵称</button> <br />
116 访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br /> 146 访问令牌:<span id="accessTokenSpan"> 加载中... </span> <button onclick="refreshToken()">刷新令牌</button> <br />
117 </div> 147 </div>