diff --git a/pom.xml b/pom.xml index 956bca6..794e1ec 100644 --- a/pom.xml +++ b/pom.xml @@ -45,6 +45,10 @@ true + + org.springframework.boot + spring-boot-starter-thymeleaf + diff --git a/src/main/java/im/zhaojun/common/config/GlobalScheduleTask.java b/src/main/java/im/zhaojun/common/config/GlobalScheduleTask.java new file mode 100644 index 0000000..534e7b9 --- /dev/null +++ b/src/main/java/im/zhaojun/common/config/GlobalScheduleTask.java @@ -0,0 +1,75 @@ +package im.zhaojun.common.config; + +import im.zhaojun.common.model.StorageConfig; +import im.zhaojun.common.model.constant.StorageConfigConstant; +import im.zhaojun.common.model.enums.StorageTypeEnum; +import im.zhaojun.common.service.AbstractFileService; +import im.zhaojun.common.service.StorageConfigService; +import im.zhaojun.common.service.SystemConfigService; +import im.zhaojun.onedrive.service.OneDriveService; +import im.zhaojun.onedrive.service.OneDriveServiceImpl; +import im.zhaojun.onedrive.service.OneDriveToken; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Arrays; + +/** + * @author zhaojun + */ +@Configuration +@EnableScheduling +@Slf4j +public class GlobalScheduleTask { + + @Resource + private StorageConfigService storageConfigService; + + @Resource + private OneDriveService oneDriveService; + + @Resource + private SystemConfigService systemConfigService; + + /** + * 项目启动 30 秒后, 每半小时执行一次刷新 OneDrive Token 的定时任务. + */ + @Scheduled(fixedRate = 1000 * 60 * 30, initialDelay = 1000 * 30) + public void autoRefreshOneDriveToken() { + + AbstractFileService currentFileService = systemConfigService.getCurrentFileService(); + + if (!(currentFileService instanceof OneDriveServiceImpl)) { + log.debug("当前启用存储类型, 不是 OneDrive, 跳过自动刷新 AccessToken"); + return; + } + + if (currentFileService.getIsUnInitialized()) { + log.debug("当前启用 OneDrive 未初始化成功, 跳过自动刷新 AccessToken"); + return; + } + + refreshOneDriveToken(); + } + + /** + * 调用刷新 OneDrive Token + */ + public void refreshOneDriveToken() { + OneDriveToken refreshToken = oneDriveService.getRefreshToken(); + + StorageConfig accessTokenConfig = + storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.ACCESS_TOKEN_KEY); + StorageConfig refreshTokenConfig = + storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.REFRESH_TOKEN_KEY); + accessTokenConfig.setValue(refreshToken.getAccessToken()); + refreshTokenConfig.setValue(refreshToken.getRefreshToken()); + + storageConfigService.updateStorageConfig(Arrays.asList(accessTokenConfig, refreshTokenConfig)); + log.info("刷新 OneDrive key 时间: " + LocalDateTime.now()); + } +} \ No newline at end of file diff --git a/src/main/java/im/zhaojun/common/model/StorageConfig.java b/src/main/java/im/zhaojun/common/model/StorageConfig.java index bd4dd30..db50ae1 100644 --- a/src/main/java/im/zhaojun/common/model/StorageConfig.java +++ b/src/main/java/im/zhaojun/common/model/StorageConfig.java @@ -27,6 +27,7 @@ public class StorageConfig { private String title; + @Column(length = 2048) private String value; public Integer getId() { diff --git a/src/main/java/im/zhaojun/common/model/constant/StorageConfigConstant.java b/src/main/java/im/zhaojun/common/model/constant/StorageConfigConstant.java index 1d5a4c4..73c8adc 100644 --- a/src/main/java/im/zhaojun/common/model/constant/StorageConfigConstant.java +++ b/src/main/java/im/zhaojun/common/model/constant/StorageConfigConstant.java @@ -30,4 +30,8 @@ public class StorageConfigConstant { public static final String FILE_PATH_KEY = "filePath"; -} + public static final String ACCESS_TOKEN_KEY = "accessToken"; + + public static final String REFRESH_TOKEN_KEY = "refreshToken"; + +} \ No newline at end of file diff --git a/src/main/java/im/zhaojun/common/model/enums/StorageTypeEnum.java b/src/main/java/im/zhaojun/common/model/enums/StorageTypeEnum.java index 9093539..455daea 100644 --- a/src/main/java/im/zhaojun/common/model/enums/StorageTypeEnum.java +++ b/src/main/java/im/zhaojun/common/model/enums/StorageTypeEnum.java @@ -1,24 +1,28 @@ package im.zhaojun.common.model.enums; +import com.fasterxml.jackson.annotation.JsonFormat; + import java.util.HashMap; import java.util.Map; /** * @author zhaojun */ +@JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum StorageTypeEnum { /** * 当前系统支持的所有存储策略 */ - UPYUN("upyun", "又拍云 USS"), - QINIU("qiniu", "七牛云 KODO"), - HUAWEI("huawei", "华为云 OBS"), ALIYUN("aliyun", "阿里云 OSS"), FTP("ftp", "FTP"), + HUAWEI("huawei", "华为云 OBS"), LOCAL("local", "本地存储"), + MINIO("minio", "MINIO"), + ONE_DRIVE("onedrive", "OneDrive"), + QINIU("qiniu", "七牛云 KODO"), TENCENT("tencent", "腾讯云 COS"), - MINIO("minio", "MINIO"); + UPYUN("upyun", "又拍云 USS"); private String key; private String description; diff --git a/src/main/java/im/zhaojun/common/repository/StorageConfigRepository.java b/src/main/java/im/zhaojun/common/repository/StorageConfigRepository.java index 3ce0ff0..e0d4398 100644 --- a/src/main/java/im/zhaojun/common/repository/StorageConfigRepository.java +++ b/src/main/java/im/zhaojun/common/repository/StorageConfigRepository.java @@ -20,4 +20,12 @@ public interface StorageConfigRepository extends JpaRepository findByTypeOrderById(StorageTypeEnum type); + /** + * 根据存储类型找到某个 KEY 的值 + * @param type 存储类型 + * @param key KEY + * @return KEY 对应的对象 + */ + StorageConfig findByTypeAndKey(StorageTypeEnum type, String key); + } \ No newline at end of file diff --git a/src/main/java/im/zhaojun/common/service/StorageConfigService.java b/src/main/java/im/zhaojun/common/service/StorageConfigService.java index 5209903..6c836a2 100644 --- a/src/main/java/im/zhaojun/common/service/StorageConfigService.java +++ b/src/main/java/im/zhaojun/common/service/StorageConfigService.java @@ -23,6 +23,12 @@ public class StorageConfigService { return storageConfigRepository.findByTypeOrderById(storageTypeEnum); } + + public StorageConfig selectByTypeAndKey(StorageTypeEnum storageType, String key) { + return storageConfigRepository.findByTypeAndKey(storageType, key); + } + + public Map selectStorageConfigMapByKey(StorageTypeEnum storageTypeEnum) { Map map = new HashMap<>(24); for (StorageConfig storageConfig : selectStorageConfigByType(storageTypeEnum)) { @@ -31,6 +37,7 @@ public class StorageConfigService { return map; } + public void updateStorageConfig(List storageConfigList) { storageConfigRepository.saveAll(storageConfigList); } diff --git a/src/main/java/im/zhaojun/common/util/HttpUtil.java b/src/main/java/im/zhaojun/common/util/HttpUtil.java index df43847..7cc0a1b 100644 --- a/src/main/java/im/zhaojun/common/util/HttpUtil.java +++ b/src/main/java/im/zhaojun/common/util/HttpUtil.java @@ -11,13 +11,13 @@ import org.springframework.web.client.RestTemplate; public class HttpUtil { public static String getTextContent(String url) { - RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class); + RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate"); String result = restTemplate.getForObject(url, String.class); return result == null ? "" : result; } public static boolean checkUrlExist(String url) { - RestTemplate restTemplate = SpringContextHolder.getBean(RestTemplate.class); + RestTemplate restTemplate = SpringContextHolder.getBean("restTemplate"); try { restTemplate.headForHeaders(url); return true; diff --git a/src/main/java/im/zhaojun/onedrive/config/OneDriveConfig.java b/src/main/java/im/zhaojun/onedrive/config/OneDriveConfig.java index fe5d59d..5719ffe 100644 --- a/src/main/java/im/zhaojun/onedrive/config/OneDriveConfig.java +++ b/src/main/java/im/zhaojun/onedrive/config/OneDriveConfig.java @@ -1,35 +1,41 @@ -// package im.zhaojun.onedrive.config; -// -// import org.nuxeo.onedrive.client.*; -// import org.springframework.context.annotation.Configuration; -// -// @Configuration -// public class OneDriveConfig { -// -// -// public void a () { -// OneDriveAPI api = new OneDriveBasicAPI("YOUR_ACCESS_TOKEN"); -// -// OneDriveFolder folder = new OneDriveFolder(api, "FOLDER_ID"); -// OneDriveFile file = new OneDriveFile(api, "FILE_ID"); -// } -// -// public static void main(String[] args) throws OneDriveAPIException { -// OneDriveBasicAPI api = new OneDriveBasicAPI("EwAgA61DBAAUcSSzoTJJsy+XrnQXgAKO5cj4yc8AAQ0ZknDUY8YnwB9aJv7vA9YjiRAVMnKc+rG11fSXLZRAA8Q/CgJaz+OkRN60vaLDfp6KxbmVlob6kxeD/peOUI2eHtk0055Q2+n057tlyVAvGIFl9dvqkItoAthjmybcSkKBZS5h1meWxQ5IOvzSVrdgCKL0NOtTxfh33ZUDsYjvSid6NOX4Bs+pRjvZhQkvqEfGt8KlOL+JoIowmv2I+u09iDmS60BMwSoeK2K3CCLIXxLaiiPYUMsrNk65j4PWEBwBEfyHb6j3lrM/YvwFLq7Y8KJVjrXjFENC7ruja6Ko/cfTMX90yLkUEckpsZ30E6RJHWEHt7jXtNwndDZVknYDZgAACL5pnk17FJfb8AGGxJL1Y0CnAzgkTM2gw+WkFRRDDNzujuW1LQofwZ119HdeANhPrBZ14x32VaPGL1l0RvtR9LCeAN+EogcV5xhVpmCExitaXQB6OkZ6BnXaxLj5TNvFRNeZq0ZfJ3T08clLA1vXHkZhNKgiFDI8xUbahy4r6QpzgoF+0+dz+MA1NzQCQCsRGieS63OD1BKrzRsNxzls5Z9rKzBT6CpWpiaiOg4mmW0yeino/L9zz9Gf5kAJr813bpNr+rH/E8MPd0pZf+6hv37FaVCM7RN1V7CkkCDnRAxwxEK8pDgZhRjZOw7gKutPOiOoTO9ptjh2Jcrds714HitX2HI3RsRY+yyAOcb8XI27m4daSEGCJCuu/TJwXTE4ul54MWsi8MrcDlZN9DOjckiJIqVI8IbvhM+OUAP4FUIfZJJrIVa8WFwxcsMmjlLTxp/I7+JfdvZjJSk3j1yYvbWFviyoSkpQgw2hIDhZxCg083Z6qS467g5H9Uz3fQc+Ss0K0Mud6RcZTU9RqCcp+h92tUc8+gDxQ2NwJsG5vcmSRwf5KHKvsWjt6yK4OHxCpkLYi31eJZtv2EjQGXX1gYyhc/2wQ+cHPvbgBzIfhXetbZKpSxoowAQO/J1i5oRs90h24kjTd4qJd3qspxk1lhZcEC8IkfZXjNgjEQI="); -// -// OneDriveFolder folder = new OneDriveFolder(api, "PDF"); -// -// for (OneDriveItem.Metadata metadata : OneDriveFolder.getRoot(api)) { -// System.out.println(metadata.getName()); -// if (metadata.isFile()) { -// } -// } -// -// // for (OneDriveItem.Metadata search : OneDriveFolder.getRoot(api).search("index.html")) { -// // System.out.println(search.getName()); -// // } -// -// // OneDriveFile file = new OneDriveFile(api, "key.txt"); -// // System.out.println(file); -// } -// } +package im.zhaojun.onedrive.config; + +import im.zhaojun.common.model.StorageConfig; +import im.zhaojun.common.model.constant.StorageConfigConstant; +import im.zhaojun.common.model.enums.StorageTypeEnum; +import im.zhaojun.common.service.StorageConfigService; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.util.Collections; + +/** + * @author zhaojun + * @date 2020/1/18 17:13 + */ +@Configuration +public class OneDriveConfig { + + @Resource + private StorageConfigService storageConfigService; + + @Bean + public RestTemplate oneDriveRestTemplate() { + RestTemplate restTemplate = new RestTemplate(); + + ClientHttpRequestInterceptor interceptor = (httpRequest, bytes, clientHttpRequestExecution) -> { + StorageConfig accessTokenConfig = + storageConfigService.selectByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.ACCESS_TOKEN_KEY); + String tokenValue = String.format("%s %s", "Bearer", accessTokenConfig.getValue()); + httpRequest.getHeaders().add("Authorization", tokenValue); + return clientHttpRequestExecution.execute(httpRequest, bytes); + }; + restTemplate.setInterceptors(Collections.singletonList(interceptor)); + return restTemplate; + } + + +} diff --git a/src/main/java/im/zhaojun/onedrive/controller/OneDriveController.java b/src/main/java/im/zhaojun/onedrive/controller/OneDriveController.java index f046d98..9c14b85 100644 --- a/src/main/java/im/zhaojun/onedrive/controller/OneDriveController.java +++ b/src/main/java/im/zhaojun/onedrive/controller/OneDriveController.java @@ -1,32 +1,30 @@ package im.zhaojun.onedrive.controller; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpResponse; -import cn.hutool.http.HttpUtil; +import im.zhaojun.onedrive.service.OneDriveService; +import im.zhaojun.onedrive.service.OneDriveToken; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RequestMapping; -import javax.servlet.http.HttpServletRequest; +import javax.annotation.Resource; /** * @author zhaojun */ @Controller +@RequestMapping("/onedirve") public class OneDriveController { - @GetMapping("/onedirve/callback") - @ResponseBody - public String onedriveCallback(String code, HttpServletRequest request) { - String json = "client_id=04a73532-6c16-4fe4-92e5-f2cd125ed553&redirect_uri=http://localhost:8080/onedirve/callback&client_secret=2gY/t?*Eff6i36TgKTtiG*08/k]@.I4[&code=" + code + "&grant_type=authorization_code"; + @Resource + private OneDriveService oneDriveService; - HttpRequest post = HttpUtil.createPost("https://login.microsoftonline.com/common/oauth2/v2.0/token"); - post.body(json, "application/x-www-form-urlencoded"); - HttpResponse response = post.execute(); - - System.out.println(response.body()); - return response.body(); + @GetMapping("/callback") + public String onedriveCallback(String code, Model model) { + OneDriveToken oneDriveToken = oneDriveService.getToken(code); + model.addAttribute("accessToken", oneDriveToken.getAccessToken()); + model.addAttribute("refreshToken", oneDriveToken.getRefreshToken()); + return "callback"; } - } diff --git a/src/main/java/im/zhaojun/onedrive/service/OneDriveService.java b/src/main/java/im/zhaojun/onedrive/service/OneDriveService.java new file mode 100644 index 0000000..894766e --- /dev/null +++ b/src/main/java/im/zhaojun/onedrive/service/OneDriveService.java @@ -0,0 +1,124 @@ +package im.zhaojun.onedrive.service; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import im.zhaojun.common.model.StorageConfig; +import im.zhaojun.common.model.constant.StorageConfigConstant; +import im.zhaojun.common.model.dto.FileItemDTO; +import im.zhaojun.common.model.enums.FileTypeEnum; +import im.zhaojun.common.model.enums.StorageTypeEnum; +import im.zhaojun.common.repository.StorageConfigRepository; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhaojun + * @date 2020/1/12 12:17 + */ +@Service +public class OneDriveService { + + public static final String DRIVER_INFO_URL = "https://graph.microsoft.com/v1.0/drive"; + + public static final String DRIVER_ROOT_URL = "https://graph.microsoft.com/v1.0/drive/root/children"; + + public static final String DRIVER_ITEMS_URL = "https://graph.microsoft.com/v1.0/drive/root:{path}:/children"; + + public static final String AUTHENTICATE_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; + + @Value("${zfile.onedirve.clientId}") + private String clientId; + + @Value("${zfile.onedirve.redirectUri}") + private String redirectUri; + + @Value("${zfile.onedirve.clientSecret}") + private String clientSecret; + + @Value("${zfile.onedirve.scope}") + private String scope; + + @Resource + private RestTemplate oneDriveRestTemplate; + + @Resource + private StorageConfigRepository storageConfigRepository; + + public OneDriveToken getToken(String code) { + String param = "client_id=" + clientId + + "&redirect_uri=" + redirectUri + + "&client_secret=" + clientSecret + + "&code=" + code + + "&scope=" + scope + + "&grant_type=authorization_code"; + + HttpRequest post = HttpUtil.createPost(AUTHENTICATE_URL); + + post.body(param, "application/x-www-form-urlencoded"); + HttpResponse response = post.execute(); + return JSONObject.parseObject(response.body(), OneDriveToken.class); + } + + public OneDriveToken getRefreshToken() { + StorageConfig refreshStorageConfig = + storageConfigRepository.findByTypeAndKey(StorageTypeEnum.ONE_DRIVE, StorageConfigConstant.REFRESH_TOKEN_KEY); + + String param = "client_id=" + clientId + + "&redirect_uri=" + redirectUri + + "&client_secret=" + clientSecret + + "&refresh_token=" + refreshStorageConfig.getValue() + + "&grant_type=refresh_token"; + + HttpRequest post = HttpUtil.createPost(AUTHENTICATE_URL); + + post.body(param, "application/x-www-form-urlencoded"); + HttpResponse response = post.execute(); + return JSONObject.parseObject(response.body(), OneDriveToken.class); + } + + public String getUserInfo() { + return oneDriveRestTemplate.getForObject(DRIVER_INFO_URL, String.class); + } + + public List list(String path) { + ResponseEntity responseEntity = oneDriveRestTemplate.getForEntity("/".equalsIgnoreCase(path) ? DRIVER_ROOT_URL : DRIVER_ITEMS_URL, String.class, path); + String body = responseEntity.getBody(); + + JSONObject root = JSON.parseObject(body); + + JSONArray fileList = root.getJSONArray("value"); + List result = new ArrayList<>(); + + for (int i = 0; i < fileList.size(); i++) { + + FileItemDTO fileItemDTO = new FileItemDTO(); + JSONObject fileItem = fileList.getJSONObject(i); + fileItemDTO.setName(fileItem.getString("name")); + fileItemDTO.setSize(fileItem.getLong("size")); + fileItemDTO.setTime(fileItem.getDate("lastModifiedDateTime")); + + if (fileItem.containsKey("file")) { + fileItemDTO.setUrl(fileItem.getString("@microsoft.graph.downloadUrl")); + fileItemDTO.setType(FileTypeEnum.FILE); + } else { + fileItemDTO.setType(FileTypeEnum.FOLDER); + } + + fileItemDTO.setPath(path); + result.add(fileItemDTO); + } + + return result; + } + +} diff --git a/src/main/java/im/zhaojun/onedrive/service/OneDriveServiceImpl.java b/src/main/java/im/zhaojun/onedrive/service/OneDriveServiceImpl.java new file mode 100644 index 0000000..45e7c6f --- /dev/null +++ b/src/main/java/im/zhaojun/onedrive/service/OneDriveServiceImpl.java @@ -0,0 +1,76 @@ +package im.zhaojun.onedrive.service; + +import im.zhaojun.common.config.GlobalScheduleTask; +import im.zhaojun.common.model.StorageConfig; +import im.zhaojun.common.model.constant.StorageConfigConstant; +import im.zhaojun.common.model.dto.FileItemDTO; +import im.zhaojun.common.model.enums.StorageTypeEnum; +import im.zhaojun.common.service.AbstractFileService; +import im.zhaojun.common.service.FileService; +import im.zhaojun.common.service.StorageConfigService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + * @author zhaojun + * @date 2020/1/12 13:53 + */ +@Service +@Slf4j +public class OneDriveServiceImpl extends AbstractFileService implements FileService { + + public static final String SYSTEM_ONEDRIVE_CACHE_PREFIX = "zfile-onedrive-cache:"; + + public static final String SYSTEM_ONEDRIVE_CACHE_ASSESS_TOKEN_KEY = "accessToken"; + + public static final String SYSTEM_ONEDRIVE_CACHE_REFRESH_TOKEN_KEY = "refreshToken"; + + @Resource + private GlobalScheduleTask globalScheduleTask; + + @Resource + private StorageConfigService storageConfigService; + + @Resource + private OneDriveService oneDriveService; + + @Override + public void init() { + try { + Map stringStorageConfigMap = + storageConfigService.selectStorageConfigMapByKey(StorageTypeEnum.ONE_DRIVE); + String accessToken = stringStorageConfigMap.get(StorageConfigConstant.ACCESS_TOKEN_KEY).getValue(); + String refreshToken = stringStorageConfigMap.get(StorageConfigConstant.REFRESH_TOKEN_KEY).getValue(); + + if (StringUtils.isEmpty(accessToken) || StringUtils.isEmpty(refreshToken)) { + log.debug("初始化存储策略 [{}] 失败: 参数不完整", getStorageTypeEnum().getDescription()); + isInitialized = false; + } else { + globalScheduleTask.refreshOneDriveToken(); + isInitialized = testConnection(); + } + } catch (Exception e) { + log.debug(getStorageTypeEnum().getDescription() + " 初始化异常, 已跳过"); + } + } + + @Override + public List fileList(String path) { + return oneDriveService.list(path); + } + + @Override + public String getDownloadUrl(String path) { + return null; + } + + @Override + public StorageTypeEnum getStorageTypeEnum() { + return StorageTypeEnum.ONE_DRIVE; + } +} diff --git a/src/main/java/im/zhaojun/onedrive/service/OneDriveToken.java b/src/main/java/im/zhaojun/onedrive/service/OneDriveToken.java new file mode 100644 index 0000000..585997e --- /dev/null +++ b/src/main/java/im/zhaojun/onedrive/service/OneDriveToken.java @@ -0,0 +1,18 @@ +package im.zhaojun.onedrive.service; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.Data; + +/** + * @author zhaojun + * @date 2020/1/18 17:28 + */ +@Data +public class OneDriveToken { + + @JSONField(name = "access_token") + private String accessToken; + + @JSONField(name = "refresh_token") + private String refreshToken; +}