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