package cn.mb.cloud.auth.endpoint; import cn.hutool.core.util.StrUtil; import cn.mb.cloud.auth.security.component.MbCloudAuthUser; import cn.mb.cloud.common.cache.RedisFastJsonTemplate; import cn.mb.cloud.common.core.constant.SecurityConstants; import cn.mb.cloud.common.core.util.ResponseData; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.AllArgsConstructor; import org.springframework.cache.CacheManager; import org.springframework.data.redis.core.ConvertingCursor; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.http.HttpHeaders; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * @author jason * 删除token端点 */ @RestController @AllArgsConstructor @RequestMapping("/token") public class CloudTokenEndpoint { private static final String OAUTH_ACCESS_KEY_PREFIX = "oauth2_auth_to_access:"; private final TokenStore tokenStore; private final RedisFastJsonTemplate redisTemplate; private final CacheManager cacheManager; /** * 认证页面 * * @return ModelAndView */ @GetMapping("/login") public ModelAndView require() { return new ModelAndView("ftl/login"); } /** * 退出token * * @param authHeader Authorization */ @PostMapping("/logout") public ResponseData logout(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader, HttpServletRequest request) { if (StrUtil.isBlank(authHeader)) { return ResponseData.fail("退出失败,token 为空"); } String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim(); OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) { return ResponseData.fail("退出失败,token 无效"); } OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(accessToken); cacheManager.getCache("user_details").evict(auth2Authentication.getName()); tokenStore.removeAccessToken(accessToken); return ResponseData.success(Boolean.TRUE); } /** * 获取用户的授权码 * * @param authHeader * @return */ @GetMapping("user/permissions") public ResponseData findUserPermissionKey(@RequestHeader(value = HttpHeaders.AUTHORIZATION, required = false) String authHeader) { if (StrUtil.isBlank(authHeader)) { return ResponseData.fail("token 为空"); } String tokenValue = authHeader.replace(OAuth2AccessToken.BEARER_TYPE, StrUtil.EMPTY).trim(); OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue); if (accessToken == null || StrUtil.isBlank(accessToken.getValue())) { return ResponseData.fail("token 无效"); } OAuth2Authentication auth2Authentication = tokenStore.readAuthentication(accessToken); MbCloudAuthUser mbCloudAuthUser = (MbCloudAuthUser) auth2Authentication.getPrincipal(); HashSet permissions = (HashSet) redisTemplate.opsForValue(). get(SecurityConstants.CACHE_USER_PERMISSIONS_KEY + "key:" + mbCloudAuthUser.getId()); return ResponseData.success(permissions); } /** * 令牌管理调用 * * @param token token * @return */ @GetMapping("del/{token}") public ResponseData delToken(@PathVariable("token") String token) { OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token); tokenStore.removeAccessToken(oAuth2AccessToken); return ResponseData.success(); } /** * 查询token * * @param page 分页参数 * @return */ @GetMapping("/page") public ResponseData tokenList(Page page) { //根据分页参数获取对应数据 String key = String.format("%s*", OAUTH_ACCESS_KEY_PREFIX); List pages = findKeysForPage(key, page.getCurrent(), page.getSize()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); page.setRecords(redisTemplate.opsForValue().multiGet(pages)); page.setTotal(Long.valueOf(redisTemplate.keys(key).size())); return ResponseData.success(page); } private List findKeysForPage(String patternKey, Long pageNum, Long pageSize) { ScanOptions options = ScanOptions.scanOptions().match(patternKey).build(); RedisSerializer redisSerializer = (RedisSerializer) redisTemplate.getKeySerializer(); Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection (redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize)); List result = new ArrayList<>(); int tmpIndex = 0; long startIndex = (pageNum - 1) * pageSize; long end = pageNum * pageSize; assert cursor != null; while (cursor.hasNext()) { if (tmpIndex >= startIndex && tmpIndex < end) { result.add(cursor.next().toString()); tmpIndex++; continue; } if (tmpIndex >= end) { break; } tmpIndex++; cursor.next(); } return result; } }