ソースを参照

资源签名 url 的生成和判定

reghao 2 年 前
コミット
f18c12cc9e
19 ファイル変更346 行追加85 行削除
  1. 6 3
      dfs-store/src/main/java/cn/reghao/dfs/store/config/CacheConfig.java
  2. 63 2
      dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectGetController.java
  3. 8 7
      dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectUploadController.java
  4. 1 1
      dfs-store/src/main/java/cn/reghao/dfs/store/db/repository/VideoRepository.java
  5. 21 8
      dfs-store/src/main/java/cn/reghao/dfs/store/inerceptor/AccessLogInterceptor.java
  6. 20 8
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/ObjectServiceImpl.java
  7. 2 1
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/OssServerServiceImpl.java
  8. 43 0
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/SignService.java
  9. 7 5
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/AudioFileServiceImpl.java
  10. 23 2
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/ImageFileServiceImpl.java
  11. 44 2
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/MediaScopeServiceImpl.java
  12. 11 2
      dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/VideoFileServiceImpl.java
  13. 1 42
      dfs-store/src/main/java/cn/reghao/dfs/store/service/GetObjectService.java
  14. 33 0
      dfs-store/src/main/java/cn/reghao/dfs/store/util/SignatureUtil.java
  15. 54 0
      oss-api/src/main/java/cn/reghao/oss/api/constant/ChannelAction.java
  16. 1 1
      oss-api/src/main/java/cn/reghao/oss/api/iface/media/AudioFileService.java
  17. 1 0
      oss-api/src/main/java/cn/reghao/oss/api/iface/media/ImageFileService.java
  18. 6 0
      oss-api/src/main/java/cn/reghao/oss/api/iface/media/MediaScopeService.java
  19. 1 1
      oss-api/src/main/java/cn/reghao/oss/api/iface/media/VideoFileService.java

+ 6 - 3
dfs-store/src/main/java/cn/reghao/dfs/store/config/CacheConfig.java

@@ -38,16 +38,19 @@ public class CacheConfig {
     @Bean
     public CacheManager cacheManager() {
         CaffeineCacheManager cacheManager = new CaffeineCacheManager();
-        Caffeine<Object, Object> caffeineCache = caffeineCache();
+        Caffeine<Object, Object> caffeineCache = Caffeine.newBuilder()
+                .initialCapacity(1000)
+                .maximumSize(10_000)
+                .expireAfterAccess(365, TimeUnit.DAYS);;
         cacheManager.setCaffeine(caffeineCache);
         return cacheManager;
     }
 
-    @Bean("caffeineCache")
+    /*@Bean("caffeineCache")
     public Caffeine<Object, Object> caffeineCache() {
         return Caffeine.newBuilder()
                 .initialCapacity(1000)
                 .maximumSize(10_000)
                 .expireAfterAccess(365, TimeUnit.DAYS);
-    }
+    }*/
 }

+ 63 - 2
dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectGetController.java

@@ -1,10 +1,18 @@
 package cn.reghao.dfs.store.controller;
 
 import cn.reghao.dfs.store.service.GetObjectService;
+import cn.reghao.dfs.store.util.JwtUtil;
 import cn.reghao.dfs.store.util.ObjectUtil;
+import cn.reghao.dfs.store.util.SignatureUtil;
+import cn.reghao.oss.api.constant.UploadChannel;
+import cn.reghao.oss.api.dto.OssPayload;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author reghao
@@ -13,9 +21,11 @@ import java.io.IOException;
 @RestController
 public class ObjectGetController {
     private final GetObjectService getObjectService;
+    private final Cache<String, String> cache;
 
     public ObjectGetController(GetObjectService getObjectService) {
         this.getObjectService = getObjectService;
+        this.cache = Caffeine.newBuilder().maximumSize(10_000).expireAfterAccess(1, TimeUnit.HOURS).build();
     }
 
     @RequestMapping(value = "/**", method = RequestMethod.HEAD)
@@ -25,12 +35,63 @@ public class ObjectGetController {
     }
 
     @GetMapping(value = "/**")
-    public void getObject(@RequestParam(value = "download", required = false) String download) throws IOException {
+    public void getObject(@RequestParam(value = "token", required = false) String token,
+                          @RequestParam(value = "t", required = false) Long timestamp,
+                          @RequestParam(value = "nonce", required = false) String nonce,
+                          @RequestParam(value = "sign", required = false) String sign,
+                          @RequestParam(value = "client", required = false) String client) throws IOException {
         String objectName = ObjectUtil.getObjectName();
-        if (download == null) {
+        if (client != null && !client.isBlank()) {
             getObjectService.getObject(objectName);
+            return;
+        }
+
+        if (objectName.startsWith(UploadChannel.avatar.getPrefix()) ||
+                objectName.startsWith(UploadChannel.image.getPrefix())) {
+            getObjectService.getObject(objectName);
+            return;
+        }
+
+        String queryString = String.format("token=%s&t=%s&nonce=%s", token, timestamp, nonce);
+        String url = String.format("%s/%s", "//oss.reghao.cn", objectName);
+        String requestString = String.format("%s%s?%s", "GET", url, queryString);
+        String secretKey = "oss.reghao.cn";
+        boolean valid = SignatureUtil.valid(requestString, secretKey, sign);
+        if (!valid) {
+            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        long current = System.currentTimeMillis();
+        if (current > timestamp) {
+            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        /*String value = cache.getIfPresent(nonce);
+        if (value == null) {
+            cache.put(nonce, nonce);
         } else {
+            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }*/
+
+        OssPayload ossPayload = JwtUtil.getOssPayload(token);
+        int channelId = ossPayload.getChannelId();
+        long userId = ossPayload.getUserId();
+        String prefix = UploadChannel.getUploadChannel(channelId).getPrefix();
+        if (!objectName.startsWith(prefix)) {
+            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
+            return;
+        }
+
+        String action = ossPayload.getAction();
+        if ("access".equals(action)) {
+            getObjectService.getObject(objectName);
+        } else if ("download".equals(action)) {
             getObjectService.downloadObject(objectName);
+        } else {
+            getObjectService.writeResponse(HttpServletResponse.SC_FORBIDDEN);
         }
     }
 }

+ 8 - 7
dfs-store/src/main/java/cn/reghao/dfs/store/controller/ObjectUploadController.java

@@ -51,25 +51,26 @@ public class ObjectUploadController {
     }
 
     @PostMapping(value = "/")
-    public ResponseEntity<String> postObject(MultipartFile file, Integer channelId, String client) throws Exception {
+    public ResponseEntity<String> postObject(MultipartFile file, Integer channelId, String client, String sha256sum)
+            throws Exception {
         /* permission check */
         if (client == null) {
             String token = ServletUtil.getBearerToken();
             if (token == null) {
-                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                         .body(WebResult.failWithMsg("no token in request"));
             }
 
             OssPayload ossPayload = JwtUtil.getOssPayload(token);
             String action = ossPayload.getAction();
             if (!"upload".equals(action)) {
-                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                return ResponseEntity.status(HttpStatus.FORBIDDEN)
                         .body(WebResult.failWithMsg("it's not upload token"));
             }
 
             int channelId1 = ossPayload.getChannelId();
             if (channelId != channelId1) {
-                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
+                return ResponseEntity.status(HttpStatus.FORBIDDEN)
                         .body(WebResult.failWithMsg("channel not match in token"));
             }
             /*long userId1 = ossPayload.getUserId();
@@ -83,7 +84,7 @@ public class ObjectUploadController {
         Result result = channelValidateService.validate(savedFile, channelId);
         if (result.getCode() != 0) {
             FileUtils.deleteQuietly(savedFile);
-            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(WebResult.result(result));
+            return ResponseEntity.status(HttpStatus.FORBIDDEN).body(WebResult.result(result));
         }
 
         /* store file */
@@ -110,8 +111,8 @@ public class ObjectUploadController {
     }
 
     @DeleteMapping(value = "/")
-    public String deleteObject(String objectId) {
+    public ResponseEntity<String> deleteObject(String objectId) {
         //putObjectService.deleteObject(objectId);
-        return WebResult.success();
+        return ResponseEntity.ok().build();
     }
 }

+ 1 - 1
dfs-store/src/main/java/cn/reghao/dfs/store/db/repository/VideoRepository.java

@@ -55,7 +55,7 @@ public class VideoRepository {
         return list;
     }
 
-    @Cacheable(cacheNames = "oss:store:videoUrls", key = "#videoFileId", unless = "#result.empty")
+    //@Cacheable(cacheNames = "oss:store:videoUrls", key = "#videoFileId", unless = "#result.empty")
     public List<VideoUrlDto> findVideoUrls(String videoFileId) {
         return videoFileMapper.findVideoUrls(videoFileId);
     }

+ 21 - 8
dfs-store/src/main/java/cn/reghao/dfs/store/inerceptor/AccessLogInterceptor.java

@@ -1,5 +1,6 @@
 package cn.reghao.dfs.store.inerceptor;
 
+import cn.reghao.jutil.web.ServletUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
@@ -8,6 +9,7 @@ import org.springframework.web.servlet.ModelAndView;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.util.Set;
 
 /**
  * 访问日志拦截器
@@ -18,24 +20,35 @@ import javax.servlet.http.HttpServletResponse;
 @Slf4j
 @Component
 public class AccessLogInterceptor implements HandlerInterceptor {
+    private final Set<String> refererSet = Set.of(
+            "https://bili.reghao.cn/",
+            "https://admin.reghao.cn/",
+            "https://account.reghao.cn/");
+
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
         String uri = request.getRequestURI();
         String method = request.getMethod();
+        String userAgent = request.getHeader("user-agent");
+        String ipv4 = request.getRemoteAddr();
+        String referer = request.getHeader("referer");
+        String client = ServletUtil.getRequestParam("client", "");
+        if (!client.isBlank()) {
+            return true;
+        }
+
+        if (referer == null || !refererSet.contains(referer)) {
+            log.info("request {} from {}", uri, referer);
+            response.setStatus(403);
+            return false;
+        }
+
         return true;
     }
 
     @Override
     public void postHandle(HttpServletRequest request, HttpServletResponse response,
                            Object handler, @Nullable ModelAndView modelAndView) throws Exception {
-        String uri = request.getRequestURI();
-        String method = request.getMethod();
-        int statusCode = response.getStatus();
-        String userAgent = request.getHeader("user-agent");
-        String ipv4 = request.getRemoteAddr();
-        String referer = request.getHeader("referer");
-        String sign = request.getParameter("sign");
-        //log.info("{} {} -> {} {}", statusCode, method, uri, userAgent);
     }
 
     @Override

+ 20 - 8
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/ObjectServiceImpl.java

@@ -4,6 +4,9 @@ import cn.reghao.dfs.store.db.repository.ObjectRepository;
 import cn.reghao.dfs.store.model.po.FileMeta;
 import cn.reghao.dfs.store.service.ObjectNameService;
 import cn.reghao.dfs.store.util.JwtUtil;
+import cn.reghao.dfs.store.util.SignatureUtil;
+import cn.reghao.oss.api.constant.ChannelAction;
+import cn.reghao.oss.api.constant.UploadChannel;
 import cn.reghao.oss.api.dto.DownloadUrl;
 import cn.reghao.oss.api.dto.ObjectMeta;
 import cn.reghao.oss.api.dto.ObjectPrefix;
@@ -14,6 +17,7 @@ import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
+import java.util.UUID;
 
 /**
  * @author reghao
@@ -59,14 +63,22 @@ public class ObjectServiceImpl implements ObjectService {
         FileMeta fileMeta = objectRepository.getByObjectId(objectId);
         String objectName = fileMeta.getObjectName();
         String url = objectNameService.getObjectUrl(objectName);
-        String url1 = String.format("%s?download", url);
+        String signedUrl = getSignedUrl(url, userId);
+        return new DownloadUrl(signedUrl, "");
+    }
+
+    private String getSignedUrl(String url, long loginUser) {
+        String secretId = loginUser+"";
+        long timestamp = System.currentTimeMillis() + 3600*1000;
+        String secretKey = "oss.reghao.cn";
 
-        String action = "download";
-        long expireAt = System.currentTimeMillis() + 3600*1000;
-        // String signKey = RandomString.getSalt(64);
-        String signKey = "oss.reghao.cn";
-        OssPayload ossPayload = new OssPayload(action, channelId, userId);
-        String token = JwtUtil.createToken(ossPayload, expireAt, signKey);
-        return new DownloadUrl(url1, token);
+        String action = ChannelAction.download.getName();
+        OssPayload ossPayload = new OssPayload(action, UploadChannel.video.getCode(), loginUser);
+        String token = JwtUtil.createToken(ossPayload, timestamp, secretKey);
+        String nonce = UUID.randomUUID().toString();
+        String queryString = String.format("token=%s&t=%s&nonce=%s", token, timestamp, nonce);
+        String requestString = String.format("%s%s?%s", "GET", url, queryString);
+        String sign = SignatureUtil.sign(requestString, secretKey);
+        return String.format("%s?token=%s&t=%s&nonce=%s&sign=%s", url, token, timestamp, nonce, sign);
     }
 }

+ 2 - 1
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/OssServerServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.reghao.dfs.store.rpc;
 
 import cn.reghao.dfs.store.util.JwtUtil;
+import cn.reghao.oss.api.constant.ChannelAction;
 import cn.reghao.oss.api.constant.UploadChannel;
 import cn.reghao.oss.api.dto.OssPayload;
 import cn.reghao.oss.api.dto.ServerInfo;
@@ -17,7 +18,7 @@ import org.springframework.stereotype.Service;
 public class OssServerServiceImpl implements OssServerService {
     @Override
     public ServerInfo getServerInfo(long userId, int channelId) {
-        String action = "upload";
+        String action = ChannelAction.upload.getName();
         long expireAt = System.currentTimeMillis() + 3600*1000;
         // String signKey = RandomString.getSalt(64);
         String signKey = "oss.reghao.cn";

+ 43 - 0
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/SignService.java

@@ -0,0 +1,43 @@
+package cn.reghao.dfs.store.rpc;
+
+import cn.reghao.dfs.store.util.JwtUtil;
+import cn.reghao.dfs.store.util.SignatureUtil;
+import cn.reghao.oss.api.constant.ChannelAction;
+import cn.reghao.oss.api.constant.ObjectType;
+import cn.reghao.oss.api.constant.UploadChannel;
+import cn.reghao.oss.api.dto.OssPayload;
+import org.springframework.stereotype.Service;
+
+import java.util.UUID;
+
+/**
+ * url 签名服务
+ *
+ * @author reghao
+ * @date 2023-10-18 14:16:01
+ */
+@Service
+public class SignService {
+    public String getSignedUrl(long loginUser, String url, int contentType, long expire) {
+        //String secretId = "reghao";
+        long timestamp = System.currentTimeMillis() + expire*1000;
+        String secretKey = "oss.reghao.cn";
+
+        String action = ChannelAction.access.getName();
+        OssPayload ossPayload = new OssPayload(action, UploadChannel.video.getCode(), loginUser);
+        if (contentType == ObjectType.Image.getCode()) {
+            ossPayload = new OssPayload(action, UploadChannel.photo.getCode(), loginUser);
+        } else if (contentType == ObjectType.Video.getCode()) {
+            ossPayload = new OssPayload(action, UploadChannel.video.getCode(), loginUser);
+        } else if (contentType == ObjectType.Audio.getCode()) {
+            ossPayload = new OssPayload(action, UploadChannel.audio.getCode(), loginUser);
+        }
+
+        String token = JwtUtil.createToken(ossPayload, timestamp, secretKey);
+        String nonce = UUID.randomUUID().toString();
+        String queryString = String.format("token=%s&t=%s&nonce=%s", token, timestamp, nonce);
+        String requestString = String.format("%s%s?%s", "GET", url, queryString);
+        String sign = SignatureUtil.sign(requestString, secretKey);
+        return String.format("%s?token=%s&t=%s&nonce=%s&sign=%s", url, token, timestamp, nonce, sign);
+    }
+}

+ 7 - 5
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/AudioFileServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.reghao.dfs.store.rpc.media;
 
 import cn.reghao.dfs.store.db.repository.AudioRepository;
+import cn.reghao.dfs.store.rpc.SignService;
+import cn.reghao.oss.api.constant.ObjectType;
 import cn.reghao.oss.api.dto.media.AudioInfo;
 import cn.reghao.oss.api.dto.media.AudioUrl;
 import cn.reghao.oss.api.iface.media.AudioFileService;
@@ -17,9 +19,11 @@ import java.util.List;
 @Service
 public class AudioFileServiceImpl implements AudioFileService {
     private final AudioRepository audioRepository;
+    private final SignService signService;
 
-    public AudioFileServiceImpl(AudioRepository audioRepository) {
+    public AudioFileServiceImpl(AudioRepository audioRepository, SignService signService) {
         this.audioRepository = audioRepository;
+        this.signService = signService;
     }
 
     @Override
@@ -29,13 +33,11 @@ public class AudioFileServiceImpl implements AudioFileService {
     }
 
     @Override
-    public List<AudioUrl> getAudioUrls(String audioFileId) {
+    public List<AudioUrl> getAudioUrls(String audioFileId, long loginUser) {
         List<AudioUrl> audioUrls = audioRepository.getAudioUrls(audioFileId);
         audioUrls.forEach(audioUrl -> {
             String url = audioUrl.getUrl();
-
-            String sign = "";
-            String signedUrl = url;
+            String signedUrl = signService.getSignedUrl(loginUser, url, ObjectType.Audio.getCode(), 3600);
             audioUrl.setUrl(signedUrl);
         });
         return audioUrls;

+ 23 - 2
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/ImageFileServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.reghao.dfs.store.rpc.media;
 
 import cn.reghao.dfs.store.db.repository.ImageRepository;
+import cn.reghao.dfs.store.rpc.SignService;
+import cn.reghao.oss.api.constant.ObjectType;
 import cn.reghao.oss.api.dto.media.ImageUrlDto;
 import cn.reghao.oss.api.iface.media.ImageFileService;
 import org.apache.dubbo.config.annotation.DubboService;
@@ -17,9 +19,11 @@ import java.util.Set;
 @Service
 public class ImageFileServiceImpl implements ImageFileService {
     private final ImageRepository imageRepository;
+    private final SignService signService;
 
-    public ImageFileServiceImpl(ImageRepository imageRepository) {
+    public ImageFileServiceImpl(ImageRepository imageRepository, SignService signService) {
         this.imageRepository = imageRepository;
+        this.signService = signService;
     }
 
     @Override
@@ -39,6 +43,23 @@ public class ImageFileServiceImpl implements ImageFileService {
 
     @Override
     public List<ImageUrlDto> getImageUrls(Set<String> imageFileIds) {
-        return imageRepository.getImageUrls(imageFileIds);
+        List<ImageUrlDto> list = imageRepository.getImageUrls(imageFileIds);
+        long loginUser = 10001;
+        list.forEach(imageUrlDto -> {
+            String originalUrl = imageUrlDto.getOriginalUrl();
+            String signedUrl = signService.getSignedUrl(loginUser, originalUrl, ObjectType.Image.getCode(), 600);
+            imageUrlDto.setOriginalUrl(signedUrl);
+
+            String thumbnailUrl = imageUrlDto.getThumbnailUrl();
+            String signedUrl1 = signService.getSignedUrl(loginUser, thumbnailUrl, ObjectType.Image.getCode(), 600);
+            imageUrlDto.setThumbnailUrl(signedUrl1);
+        });
+        return list;
+    }
+
+    @Override
+    public String getSignedUrl(String url, long loginUser) {
+        String signedUrl = signService.getSignedUrl(loginUser, url, ObjectType.Image.getCode(), 600);
+        return signedUrl;
     }
 }

+ 44 - 2
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/MediaScopeServiceImpl.java

@@ -7,12 +7,12 @@ import cn.reghao.dfs.store.db.repository.ObjectRepository;
 import cn.reghao.dfs.store.model.po.AudioFile;
 import cn.reghao.dfs.store.model.po.ImageFile;
 import cn.reghao.dfs.store.model.po.VideoFile;
+import cn.reghao.oss.api.constant.ObjectType;
 import cn.reghao.oss.api.iface.media.MediaScopeService;
 import org.apache.dubbo.config.annotation.DubboService;
 import org.springframework.stereotype.Service;
 
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -67,4 +67,46 @@ public class MediaScopeServiceImpl implements MediaScopeService {
             objectRepository.updateObjectScope1(scope, objectNames);
         }
     }
+
+    @Override
+    public void setObjectScope(int scope, String objectId, int contentType) {
+        List<String> objectIds;
+        if (contentType == ObjectType.Image.getCode()) {
+            objectIds = imageFileMapper.findByImageFileId(objectId).stream()
+                    .map(ImageFile::getObjectId)
+                    .collect(Collectors.toList());
+        } else  if (contentType == ObjectType.Video.getCode()) {
+            objectIds = videoFileMapper.findByVideoFileId(objectId).stream()
+                    .map(VideoFile::getObjectId)
+                    .collect(Collectors.toList());
+        } else if (contentType == ObjectType.Audio.getCode()) {
+            objectIds = audioFileMapper.findByAudioFileId(objectId).stream()
+                    .map(AudioFile::getObjectId)
+                    .collect(Collectors.toList());
+        } else if (contentType == ObjectType.Text.getCode()) {
+            objectIds = Collections.emptyList();
+        } else {
+            objectIds = Collections.emptyList();
+        }
+
+        if (!objectIds.isEmpty()) {
+            List<String> objectNames = objectRepository.getObjectNames(objectIds);
+            objectRepository.updateObjectScope1(scope, objectNames);
+        }
+    }
+
+    @Override
+    public void setObjectsScope(int scope, List<String> objectsId, int contentType) {
+        List<String> objectIds = new ArrayList<>();
+        if (contentType == ObjectType.Image.getCode()) {
+            objectIds = imageFileMapper.findByImageFileIds(new HashSet<>(objectsId)).stream()
+                    .map(ImageFile::getObjectId)
+                    .collect(Collectors.toList());
+        }
+
+        if (!objectIds.isEmpty()) {
+            List<String> objectNames = objectRepository.getObjectNames(objectIds);
+            objectRepository.updateObjectScope1(scope, objectNames);
+        }
+    }
 }

+ 11 - 2
dfs-store/src/main/java/cn/reghao/dfs/store/rpc/media/VideoFileServiceImpl.java

@@ -3,6 +3,8 @@ package cn.reghao.dfs.store.rpc.media;
 import cn.reghao.dfs.store.db.repository.ObjectRepository;
 import cn.reghao.dfs.store.db.repository.VideoRepository;
 import cn.reghao.dfs.store.model.po.VideoFile;
+import cn.reghao.dfs.store.rpc.SignService;
+import cn.reghao.oss.api.constant.ObjectType;
 import cn.reghao.oss.api.dto.media.VideoInfo;
 import cn.reghao.oss.api.dto.media.VideoUrlDto;
 import cn.reghao.oss.api.iface.media.VideoFileService;
@@ -20,10 +22,12 @@ import java.util.List;
 public class VideoFileServiceImpl implements VideoFileService {
     private final VideoRepository videoRepository;
     private final ObjectRepository objectRepository;
+    private final SignService signService;
 
-    public VideoFileServiceImpl(VideoRepository videoRepository, ObjectRepository objectRepository) {
+    public VideoFileServiceImpl(VideoRepository videoRepository, ObjectRepository objectRepository, SignService signService) {
         this.videoRepository = videoRepository;
         this.objectRepository = objectRepository;
+        this.signService = signService;
     }
 
     @Override
@@ -44,8 +48,13 @@ public class VideoFileServiceImpl implements VideoFileService {
     }
 
     @Override
-    public List<VideoUrlDto> getVideoUrls(String videoFileId) {
+    public List<VideoUrlDto> getVideoUrls(String videoFileId, long loginUser) {
         List<VideoUrlDto> list = videoRepository.findVideoUrls(videoFileId);
+        list.forEach(videoUrlDto -> {
+            String url = videoUrlDto.getUrl();
+            String signedUrl = signService.getSignedUrl(loginUser, url, ObjectType.Video.getCode(), 3600);
+            videoUrlDto.setUrl(signedUrl);
+        });
         return list;
     }
 }

+ 1 - 42
dfs-store/src/main/java/cn/reghao/dfs/store/service/GetObjectService.java

@@ -61,9 +61,7 @@ public class GetObjectService {
     }
 
     public void getObject(String objectName) throws IOException {
-        String userAgent = ServletUtil.getRequest().getHeader("user-agent");
         String host = ServletUtil.getRequest().getHeader("host");
-        HttpServletResponse response = ServletUtil.getResponse();
 
         ObjectMeta objectMeta = objectRepository.getObjectMeta(objectName);
         if (objectMeta == null) {
@@ -71,11 +69,6 @@ public class GetObjectService {
             return;
         }
 
-        if (!checkPermission(objectMeta.getScope(), objectMeta.getUploadBy())) {
-            writeResponse(HttpServletResponse.SC_UNAUTHORIZED);
-            return;
-        }
-
         long len = objectMeta.getSize();
         String range = ServletUtil.getRequest().getHeader("range");
         if (range != null) {
@@ -91,27 +84,6 @@ public class GetObjectService {
     }
 
     public void downloadObject(String objectName) throws IOException {
-        String token = ServletUtil.getBearerToken();
-        if (token == null) {
-            writeResponse(HttpServletResponse.SC_UNAUTHORIZED);
-        }
-
-        OssPayload ossPayload = JwtUtil.getOssPayload(token);
-        String action = ossPayload.getAction();
-        if (!"download".equals(action)) {
-            writeResponse(HttpServletResponse.SC_UNAUTHORIZED);
-        }
-
-        int channelId1 = ossPayload.getChannelId();
-        String prefix = UploadChannel.getUploadChannel(channelId1).getPrefix();
-        if (!objectName.startsWith(prefix)) {
-            writeResponse(HttpServletResponse.SC_UNAUTHORIZED);
-        }
-
-        String userAgent = ServletUtil.getRequest().getHeader("user-agent");
-        String host = ServletUtil.getRequest().getHeader("host");
-        HttpServletResponse response = ServletUtil.getResponse();
-
         ObjectMeta objectMeta = objectRepository.getObjectMeta(objectName);
         if (objectMeta == null) {
             writeResponse(HttpServletResponse.SC_NOT_FOUND);
@@ -121,20 +93,7 @@ public class GetObjectService {
         writeDownloadContent(objectMeta);
     }
 
-    private boolean checkPermission(int scope, long uploadBy) {
-        long loginUser = UserContext.getUser();
-        if (scope == ObjectScope.PRIVATE.getCode()) {
-            return loginUser == uploadBy;
-        } else if (scope == ObjectScope.FRIEND.getCode()) {
-            return false;
-        } else if (scope == ObjectScope.PROTECT.getCode()) {
-            return loginUser == uploadBy || (loginUser > 10000 && loginUser < 10100);
-        }
-
-        return true;
-    }
-
-    private void writeResponse(int statusCode) throws IOException {
+    public void writeResponse(int statusCode) throws IOException {
         HttpServletResponse response = ServletUtil.getResponse();
         response.setStatus(statusCode);
         OutputStream outputStream = response.getOutputStream();

+ 33 - 0
dfs-store/src/main/java/cn/reghao/dfs/store/util/SignatureUtil.java

@@ -0,0 +1,33 @@
+package cn.reghao.dfs.store.util;
+
+import cn.reghao.jutil.jdk.converter.ByteHex;
+import org.checkerframework.checker.units.qual.A;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author reghao
+ * @date 2023-10-17 14:57:33
+ */
+public class SignatureUtil {
+    private static final String ALGORITHM = "HmacSHA256";
+
+    public static String sign(String message, String secret) {
+        try {
+            Mac hmac = Mac.getInstance(ALGORITHM);
+            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM);
+            hmac.init(secretKey);
+            byte[] bytes = hmac.doFinal(message.getBytes(StandardCharsets.UTF_8));
+            return ByteHex.bytes2Hex(bytes);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    public static boolean valid(String message, String secret, String signature) {
+        return signature != null && signature.equals(sign(message, secret));
+    }
+}

+ 54 - 0
oss-api/src/main/java/cn/reghao/oss/api/constant/ChannelAction.java

@@ -0,0 +1,54 @@
+package cn.reghao.oss.api.constant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author reghao
+ * @date 2023-10-18 13:15:45
+ */
+public enum ChannelAction {
+    access(1, "访问"),
+    download(2, "下载"),
+    upload(3, "上传"),
+    delete(4, "删除");
+
+    private final int code;
+    private final String desc;
+
+    private static Map<Integer, String> descMap = new HashMap<>();
+    static {
+        for (ChannelAction scope : ChannelAction.values()) {
+            descMap.put(scope.code, scope.desc);
+        }
+    }
+
+    ChannelAction(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getName() {
+        return this.name();
+    }
+
+    /**
+     * 提供给 @ValidEnum 调用
+     *
+     * @param
+     * @return
+     * @date 2023-10-11 14:44:42
+     */
+    public int getValue() {
+        return this.code;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    // TODO 第一次调用时会初始化 descMap
+    public static String getDescByCode(int code) {
+        return descMap.get(code);
+    }
+}

+ 1 - 1
oss-api/src/main/java/cn/reghao/oss/api/iface/media/AudioFileService.java

@@ -11,5 +11,5 @@ import java.util.List;
  */
 public interface AudioFileService {
     AudioInfo getAudioInfo(String audioFileId);
-    List<AudioUrl> getAudioUrls(String audioFileId);
+    List<AudioUrl> getAudioUrls(String audioFileId, long loginUser);
 }

+ 1 - 0
oss-api/src/main/java/cn/reghao/oss/api/iface/media/ImageFileService.java

@@ -14,4 +14,5 @@ public interface ImageFileService {
     void deleteByImageFileIds(List<String> imageFileIds);
     ImageUrlDto getImageUrl(String imageFileId);
     List<ImageUrlDto> getImageUrls(Set<String> imageFileIds);
+    String getSignedUrl(String url, long loginUser);
 }

+ 6 - 0
oss-api/src/main/java/cn/reghao/oss/api/iface/media/MediaScopeService.java

@@ -1,5 +1,6 @@
 package cn.reghao.oss.api.iface.media;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -7,7 +8,12 @@ import java.util.Set;
  * @date 2023-10-12 15:06:21
  */
 public interface MediaScopeService {
+    @Deprecated
     void setVideoScope(String videoFileId, int scope);
+    @Deprecated
     void setAudioScope(String audioFileId, int scope);
+    @Deprecated
     void setImagesScope(Set<String> imageFileIds, int scope);
+    void setObjectScope(int scope, String objectId, int contentType);
+    void setObjectsScope(int scope, List<String> objectIds, int contentType);
 }

+ 1 - 1
oss-api/src/main/java/cn/reghao/oss/api/iface/media/VideoFileService.java

@@ -12,5 +12,5 @@ import java.util.List;
 public interface VideoFileService {
     void deleteVideoFile(String videoFileId);
     VideoInfo getVideoInfo(String videoFileId);
-    List<VideoUrlDto> getVideoUrls(String videoFileId);
+    List<VideoUrlDto> getVideoUrls(String videoFileId, long loginUser);
 }