|
@@ -9,44 +9,31 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
|
|
|
import com.kxs.common.core.constant.CacheConstants;
|
|
import com.kxs.common.core.constant.CacheConstants;
|
|
|
import com.kxs.common.core.constant.CommonConstants;
|
|
import com.kxs.common.core.constant.CommonConstants;
|
|
|
-import com.kxs.common.core.constant.SecurityConstants;
|
|
|
|
|
import com.kxs.common.core.constant.enums.ErrorTypeEnum;
|
|
import com.kxs.common.core.constant.enums.ErrorTypeEnum;
|
|
|
import com.kxs.common.core.util.R;
|
|
import com.kxs.common.core.util.R;
|
|
|
import com.kxs.common.core.util.SpringContextHolder;
|
|
import com.kxs.common.core.util.SpringContextHolder;
|
|
|
import com.kxs.common.log.annotation.SysLog;
|
|
import com.kxs.common.log.annotation.SysLog;
|
|
|
-import com.kxs.common.security.annotation.Inner;
|
|
|
|
|
-import com.kxs.common.security.support.handler.SkyAuthenticationFailureEventHandler;
|
|
|
|
|
import com.kxs.common.security.util.OAuth2EndpointUtils;
|
|
import com.kxs.common.security.util.OAuth2EndpointUtils;
|
|
|
-import com.kxs.common.security.util.OAuth2ErrorCodesExpand;
|
|
|
|
|
import com.kxs.common.security.util.OAuthClientException;
|
|
import com.kxs.common.security.util.OAuthClientException;
|
|
|
import com.kxs.system.api.model.SysOauthClientDetails;
|
|
import com.kxs.system.api.model.SysOauthClientDetails;
|
|
|
import com.kxs.system.api.vo.TokenVo;
|
|
import com.kxs.system.api.vo.TokenVo;
|
|
|
import com.kxs.system.biz.service.SysOauthClientDetailsService;
|
|
import com.kxs.system.biz.service.SysOauthClientDetailsService;
|
|
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
|
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
|
|
-import jakarta.servlet.http.HttpServletRequest;
|
|
|
|
|
-import jakarta.servlet.http.HttpServletResponse;
|
|
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.SneakyThrows;
|
|
import lombok.SneakyThrows;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.cache.CacheManager;
|
|
import org.springframework.cache.CacheManager;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.http.HttpHeaders;
|
|
import org.springframework.http.HttpHeaders;
|
|
|
-import org.springframework.http.HttpStatus;
|
|
|
|
|
-import org.springframework.http.MediaType;
|
|
|
|
|
-import org.springframework.http.converter.HttpMessageConverter;
|
|
|
|
|
-import org.springframework.http.server.ServletServerHttpResponse;
|
|
|
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
|
|
import org.springframework.security.authentication.event.LogoutSuccessEvent;
|
|
import org.springframework.security.authentication.event.LogoutSuccessEvent;
|
|
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
|
|
|
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
|
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
|
|
-import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
|
|
|
|
|
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
|
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
|
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
|
|
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
|
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
|
|
-import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
|
|
|
|
|
-import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
|
|
|
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
|
|
import org.springframework.util.StringUtils;
|
|
import org.springframework.util.StringUtils;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
@@ -55,6 +42,7 @@ import org.springframework.web.servlet.ModelAndView;
|
|
|
import java.security.Principal;
|
|
import java.security.Principal;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
+import java.util.Objects;
|
|
|
import java.util.Set;
|
|
import java.util.Set;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
@@ -70,10 +58,6 @@ import java.util.stream.Collectors;
|
|
|
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
|
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
|
|
|
public class TokenController {
|
|
public class TokenController {
|
|
|
|
|
|
|
|
- private final HttpMessageConverter<OAuth2AccessTokenResponse> accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
|
|
|
|
|
-
|
|
|
|
|
- private final AuthenticationFailureHandler authenticationFailureHandler = new SkyAuthenticationFailureEventHandler();
|
|
|
|
|
-
|
|
|
|
|
private final OAuth2AuthorizationService authorizationService;
|
|
private final OAuth2AuthorizationService authorizationService;
|
|
|
|
|
|
|
|
private final SysOauthClientDetailsService clientDetailsService;
|
|
private final SysOauthClientDetailsService clientDetailsService;
|
|
@@ -84,8 +68,9 @@ public class TokenController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 认证页面
|
|
* 认证页面
|
|
|
- * @param modelAndView
|
|
|
|
|
- * @param error 表单登录失败处理回调的错误信息
|
|
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param error 表单登录失败处理回调的错误信息
|
|
|
|
|
+ * @param modelAndView 模型和视图
|
|
|
* @return ModelAndView
|
|
* @return ModelAndView
|
|
|
*/
|
|
*/
|
|
|
@GetMapping("/login")
|
|
@GetMapping("/login")
|
|
@@ -117,16 +102,17 @@ public class TokenController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 退出并删除token
|
|
* 退出并删除token
|
|
|
|
|
+ *
|
|
|
* @param authHeader Authorization
|
|
* @param authHeader Authorization
|
|
|
*/
|
|
*/
|
|
|
@DeleteMapping("/logout")
|
|
@DeleteMapping("/logout")
|
|
|
- public R<Boolean> logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
|
|
|
|
|
|
|
+ public R logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) {
|
|
|
if (StrUtil.isBlank(authHeader)) {
|
|
if (StrUtil.isBlank(authHeader)) {
|
|
|
return R.ok();
|
|
return R.ok();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
String tokenValue = authHeader.replace(OAuth2AccessToken.TokenType.BEARER.getValue(), StrUtil.EMPTY).trim();
|
|
String tokenValue = authHeader.replace(OAuth2AccessToken.TokenType.BEARER.getValue(), StrUtil.EMPTY).trim();
|
|
|
- return removeToken(tokenValue);
|
|
|
|
|
|
|
+ return removeToken(new String[]{tokenValue});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -135,9 +121,7 @@ public class TokenController {
|
|
|
*/
|
|
*/
|
|
|
@SneakyThrows
|
|
@SneakyThrows
|
|
|
@GetMapping("/checkToken")
|
|
@GetMapping("/checkToken")
|
|
|
- public R<Object> checkToken(String token, HttpServletResponse response, HttpServletRequest request) {
|
|
|
|
|
- ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ public R<Object> checkToken(String token) {
|
|
|
if (StrUtil.isBlank(token)) {
|
|
if (StrUtil.isBlank(token)) {
|
|
|
|
|
|
|
|
return R.failed(ErrorTypeEnum.TOKEN_EMPTY);
|
|
return R.failed(ErrorTypeEnum.TOKEN_EMPTY);
|
|
@@ -158,36 +142,40 @@ public class TokenController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 令牌管理调用
|
|
|
|
|
- * @param token token
|
|
|
|
|
|
|
+ * 删除
|
|
|
|
|
+ * @param tokens tokens
|
|
|
|
|
+ * @return success/false
|
|
|
*/
|
|
*/
|
|
|
- @DeleteMapping("/{token}")
|
|
|
|
|
@SysLog("删除用户token")
|
|
@SysLog("删除用户token")
|
|
|
|
|
+ @DeleteMapping("/delete")
|
|
|
@PreAuthorize("@pms.hasPermission('sys_token_del')")
|
|
@PreAuthorize("@pms.hasPermission('sys_token_del')")
|
|
|
- public R<Boolean> removeToken(@PathVariable("token") String token) {
|
|
|
|
|
- OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
|
|
|
|
- if (authorization == null) {
|
|
|
|
|
- return R.ok();
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ public R removeToken(@RequestBody String[] tokens) {
|
|
|
|
|
+ for (String token : tokens) {
|
|
|
|
|
+ OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
|
|
|
|
+ if (authorization == null) {
|
|
|
|
|
+ return R.ok();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getAccessToken();
|
|
|
|
|
- if (accessToken == null || StrUtil.isBlank(accessToken.getToken().getTokenValue())) {
|
|
|
|
|
- return R.ok();
|
|
|
|
|
|
|
+ OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getAccessToken();
|
|
|
|
|
+ if (accessToken == null || StrUtil.isBlank(accessToken.getToken().getTokenValue())) {
|
|
|
|
|
+ return R.ok();
|
|
|
|
|
+ }
|
|
|
|
|
+ // 清空用户信息(立即删除)
|
|
|
|
|
+ Objects.requireNonNull(cacheManager.getCache(CacheConstants.USER_DETAILS)).evictIfPresent(authorization.getPrincipalName());
|
|
|
|
|
+ // 清空access token
|
|
|
|
|
+ authorizationService.remove(authorization);
|
|
|
|
|
+ // 处理自定义退出事件,保存相关日志
|
|
|
|
|
+ SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
|
|
|
|
|
+ authorization.getPrincipalName(), authorization.getRegisteredClientId())));
|
|
|
}
|
|
}
|
|
|
- // 清空用户信息(立即删除)
|
|
|
|
|
- cacheManager.getCache(CacheConstants.USER_DETAILS).evictIfPresent(authorization.getPrincipalName());
|
|
|
|
|
- // 清空access token
|
|
|
|
|
- authorizationService.remove(authorization);
|
|
|
|
|
- // 处理自定义退出事件,保存相关日志
|
|
|
|
|
- SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
|
|
|
|
|
- authorization.getPrincipalName(), authorization.getRegisteredClientId())));
|
|
|
|
|
return R.ok();
|
|
return R.ok();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 查询token
|
|
|
|
|
|
|
+ * 令牌列表
|
|
|
|
|
+ *
|
|
|
* @param params 分页参数
|
|
* @param params 分页参数
|
|
|
- * @return
|
|
|
|
|
|
|
+ * @return r<page>
|
|
|
*/
|
|
*/
|
|
|
@PostMapping("/page")
|
|
@PostMapping("/page")
|
|
|
public R<Page> tokenList(@RequestBody Map<String, Object> params) {
|
|
public R<Page> tokenList(@RequestBody Map<String, Object> params) {
|
|
@@ -196,6 +184,7 @@ public class TokenController {
|
|
|
int current = MapUtil.getInt(params, CommonConstants.CURRENT);
|
|
int current = MapUtil.getInt(params, CommonConstants.CURRENT);
|
|
|
int size = MapUtil.getInt(params, CommonConstants.SIZE);
|
|
int size = MapUtil.getInt(params, CommonConstants.SIZE);
|
|
|
Set<String> keys = redisTemplate.keys(key);
|
|
Set<String> keys = redisTemplate.keys(key);
|
|
|
|
|
+ assert keys != null;
|
|
|
List<String> pages = keys.stream().skip((long) (current - 1) * size).limit(size).collect(Collectors.toList());
|
|
List<String> pages = keys.stream().skip((long) (current - 1) * size).limit(size).collect(Collectors.toList());
|
|
|
Page result = new Page(current, size);
|
|
Page result = new Page(current, size);
|
|
|
|
|
|