Jackson自定义序列化注解之 获取COS预签名地址,标注在属性上

目录

1. 腾讯COS对象存储配置类

2. 腾讯COS对象存储工具类

3. COSPreSignedUrl注解类

4. 注解处理类COSPreSignedUrlHandler


        此文章需要先看Jackson自定义序列化注解 (支持同时使用多个自定义序列化注解)

  1. 腾讯COS对象存储配置类

    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.List;
    
    /**
     * 腾讯COS对象存储配置
     *
     * @author ds
     */
    @Data
    @RefreshScope
    @Configuration
    @ConfigurationProperties(prefix = "cos")
    public class COSProperties {
    
        /**
         * 开发者访问 COS 服务时拥有的用户维度唯一资源标识,用以标识资源,可在 <a href="https://console.cloud.tencent.com/capi">API 密钥管理</a> 页面获取
         */
        private String appid;
    
        /**
         * 开发者拥有的项目身份识别 ID,用于身份认证,可在 <a href="https://console.cloud.tencent.com/capi">API 密钥管理</a> 页面获取
         */
        private String secretId;
    
        /**
         * 开发者拥有的项目身份密钥,可在 <a href="https://console.cloud.tencent.com/capi">API 密钥管理</a> 页面获取
         */
        private String secretKey;
    
        /**
         * 存储桶,COS 中用于存储数据的容器。有关存储桶的进一步说明,请参见 <a href="https://cloud.tencent.com/document/product/436/13312">存储桶概述</a> 文档
         */
        private String bucket;
    
        /**
         * {@link COSProperties#bucket}-{@link COSProperties#appid}
         */
        private String bucketName;
    
        /**
         * 地域信息,枚举值可参见 <a href="https://cloud.tencent.com/document/product/436/6224">可用地域</a> 文档,例如:ap-beijing、ap-hongkong、eu-frankfurt 等
         */
        private String region;
    
        /**
         * {@link COSProperties#bucketName}.cos.{@link COSProperties#region}.myqcloud.com
         */
        private String url;
    
        /**
         * 临时密钥有效时长,单位是秒,默认 1800 秒,目前主账号最长 2 小时(即 7200 秒),子账号最长 36 小时(即 129600)秒
         */
        private Integer durationSeconds;
    
        /**
         * 允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径
         * 列举几种典型的前缀授权场景:
         * 1、允许访问所有对象:"*"
         * 2、允许访问指定的对象:"a/a1.txt", "b/b1.txt"
         * 3、允许访问指定前缀的对象:"a*", "a/*", "b/*"
         * 如果填写了“*”,将允许用户访问所有资源;除非业务需要,否则请按照最小权限原则授予用户相应的访问权限范围。
         */
        private List<String> allowPrefixes;
    
        /**
         * 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。
         * 简单上传、表单上传和分块上传需要以下的权限,其他权限列表请参见 <a href="https://cloud.tencent.com/document/product/436/31923">COS API 授权策略使用指引</a>
         */
        private List<String> allowActions;
    
    
    }
    
  2. 腾讯COS对象存储工具类

    import cn.hutool.core.date.DateTime;
    import cn.hutool.core.date.DateUtil;
    import cn.hutool.core.util.StrUtil;
    import com.qcloud.cos.COSClient;
    import com.qcloud.cos.ClientConfig;
    import com.qcloud.cos.auth.BasicSessionCredentials;
    import com.qcloud.cos.auth.COSCredentials;
    import com.qcloud.cos.http.HttpProtocol;
    import com.qcloud.cos.region.Region;
    import com.tencent.cloud.CosStsClient;
    import com.tencent.cloud.Credentials;
    import com.tencent.cloud.Response;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.codec.digest.HmacAlgorithms;
    import org.apache.commons.codec.digest.HmacUtils;
    
    import java.text.SimpleDateFormat;
    import java.util.Base64;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.TimeZone;
    import java.util.TreeMap;
    
    /**
     * 腾讯COS对象存储工具类
     *
     * @author ds
     */
    @Slf4j
    public class COSUtils {
    
        /**
         * 腾讯COS对象存储配置
         */
        private static final COSProperties cosProperties = SpringUtils.getBean(COSProperties.class);
    
        /**
         * 创建 COSClient 实例,这个实例用来后续调用请求
         *
         * @return COSClient
         */
        private static COSClient createCOSClient() {
            Response credentialResponse = getTemporaryCredentialResponse();
            Credentials credentials = credentialResponse.credentials;
            COSCredentials cred = new BasicSessionCredentials(credentials.tmpSecretId, credentials.tmpSecretKey, credentials.sessionToken);
    
            // ClientConfig 中包含了后续请求 COS 的客户端设置:
            ClientConfig clientConfig = new ClientConfig();
    
            // 设置 bucket 的地域
            clientConfig.setRegion(new Region(cosProperties.getRegion()));
    
            // 设置请求协议, http 或者 https
            // 5.6.53 及更低的版本,建议设置使用 https 协议
            // 5.6.54 及更高版本,默认使用了 https
            clientConfig.setHttpProtocol(HttpProtocol.https);
    
            // 以下的设置,是可选的:
    
            // 设置 socket 读取超时,默认 30s
            clientConfig.setSocketTimeout(30 * 1000);
            // 设置建立连接超时,默认 30s
            clientConfig.setConnectionTimeout(30 * 1000);
    
            // 如果需要的话,设置 http 代理,ip 以及 port
    //        clientConfig.setHttpProxyIp("httpProxyIp");
    //        clientConfig.setHttpProxyPort(80);
    
            // 生成 cos 客户端。
            return new COSClient(cred, clientConfig);
        }
    
        /**
         * 获取存储桶上传表单参数 fixme 需要做缓存
         *
         * @return 秘钥相关
         */
        public static Map<String, Object> getUploadFormData() {
            Map<String, Object> map = new HashMap<>();
            map.put("q-sign-algorithm", "sha1");
            map.put("url", cosProperties.getUrl());
            map.put("q-ak", cosProperties.getSecretId());
    
            DateTime now = DateUtil.date();
            DateTime expiration = DateUtil.offsetHour(now, 1);
            long nowTime = now.getTime() / 1000;
            long expirationTime = expiration.getTime() / 1000;
            String keyTime = nowTime + ";" + expirationTime;
    
            map.put("q-key-time", keyTime);
    
            String policy = getPolicy(expiration, keyTime);
            map.put("policy", Base64.getEncoder().encodeToString(policy.getBytes()));
    
            String signKey = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, cosProperties.getSecretKey()).hmacHex(keyTime);
            String stringToSign = DigestUtils.sha1Hex(policy);
            String signature = new HmacUtils(HmacAlgorithms.HMAC_SHA_1, signKey).hmacHex(stringToSign);
            map.put("q-signature", signature);
            return map;
        }
    
        /**
         * 构造“策略”(Policy)
         *
         * @param expiration 该策略的过期时间
         * @param keyTime    时间戳和期望的签名有效时长算出签名过期时间对应的 Unix 时间戳 EndTimestamp
         * @return Policy
         */
        private static String getPolicy(DateTime expiration, String keyTime) {
            JSONObject policyObj = new JSONObject();
    
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
            // 该策略的过期时间 ISO8601 格式字符串
            policyObj.put("expiration", dateFormat.format(expiration));
    
            JSONArray conditionsArray = new JSONArray();
            policyObj.put("conditions", conditionsArray);
    
            JSONObject qSignAlgorithmObj = new JSONObject();
            qSignAlgorithmObj.put("q-sign-algorithm", "sha1");
            conditionsArray.add(qSignAlgorithmObj);
    
            JSONObject qAkObj = new JSONObject();
            qAkObj.put("q-ak", cosProperties.getSecretId());
            conditionsArray.add(qAkObj);
    
            JSONObject qSignTimeObj = new JSONObject();
            qSignTimeObj.put("q-sign-time", keyTime);
            conditionsArray.add(qSignTimeObj);
    
            return JSON.toJSONString(policyObj);
        }
    
        /**
         * 获取临时秘钥
         *
         * @return 临时秘钥
         */
        public static Response getTemporaryCredentialResponse() {
            Response credential;
            try {
                TreeMap<String, Object> config = new TreeMap<>();
                config.put("secretId", cosProperties.getSecretId());
                config.put("secretKey", cosProperties.getSecretKey());
                config.put("durationSeconds", cosProperties.getDurationSeconds());
                config.put("bucket", cosProperties.getBucketName());
                config.put("region", cosProperties.getRegion());
                config.put("allowPrefixes", cosProperties.getAllowPrefixes().toArray(new String[0]));
                config.put("allowActions", cosProperties.getAllowActions().toArray(new String[0]));
    
                // 设置域名:
                // 如果您使用了腾讯云 cvm,可以设置内部域名
    //            config.put("host", "sts.internal.tencentcloudapi.com");
    
                credential = CosStsClient.getCredential(config);
            } catch (Exception e) {
                e.printStackTrace();
                throw new IllegalArgumentException("no valid secret !");
            }
            return credential;
        }
    
        /**
         * 获取预签名地址
         *
         * @param key 文件key
         * @return 预签名地址
         */
        public static String generatePreSignedUrl(String key) {
            if (StrUtil.isBlank(key)) {
                return StrUtil.EMPTY;
            }
            // 调用 COS 接口之前必须保证本进程存在一个 COSClient 实例,如果没有则创建
            // 详细代码参见本页:简单操作 -> 创建 COSClient
            COSClient cosClient = createCOSClient();
            try {
    
                // 设置签名过期时间(可选), 若未进行设置则默认使用 ClientConfig 中的签名过期时间(1小时)
                // 这里设置签名在半个小时后过期
                Date expirationDate = new Date(System.currentTimeMillis() + 30 * 60 * 1000);
    
                return cosClient.generatePresignedUrl(cosProperties.getBucketName(), key, expirationDate).toString();
            } finally {
                // 确认本进程不再使用 cosClient 实例之后,关闭即可
                cosClient.shutdown();
            }
        }
    
    }
    
  3. COSPreSignedUrl注解类

    import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 获取COS预签名地址,标注在属性上
     * <p/>
     * 会多返回个属性存放预签名地址 属性名为 原属性名 + PreSigned
     *
     * @author ds
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @JacksonAnnotationsInside
    @JacksonBaseSerialize(handlerClazz = COSPreSignedUrlHandler.class)
    public @interface COSPreSignedUrl {
    }
  4. 注解处理类COSPreSignedUrlHandler

    import cn.hutool.core.util.StrUtil;
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.BeanProperty;
    import com.fasterxml.jackson.databind.SerializerProvider;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    /**
     * Jackson 序列化处理类
     * 获取COS预签名地址,标注在属性上
     * <p/>
     * 会多返回个属性存放预签名地址 属性名为 原属性名 + PreSigned
     *
     * @author ds
     */
    public class COSPreSignedUrlHandler implements IJacksonBaseSerializeHandler<COSPreSignedUrl, Object> {
    
        /**
         * Method that can be called to ask implementation to serialize
         * values of type this serializer handles.
         *
         * @param value       Value to serialize; can <b>not</b> be null.
         * @param gen         Generator used to output resulting Json content
         * @param serializers Provider that can be used to get serializers for
         *                    serializing Objects value contains, if any.
         */
        @Override
        public void serializeHandler(BeanProperty beanProperty, COSPreSignedUrl annotation, Object value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            // 为空直接跳过
            if (beanProperty == null) {
                return;
            }
            if (value instanceof String) {
                String str = (String) value;
                if (StrUtil.isNotBlank(str)) {
                    // 只针对String类型属性进行
                    if (Objects.nonNull(annotation) && Objects.equals(String.class, beanProperty.getType().getRawClass())) {
                        gen.writeStringField(beanProperty.getName() + "PreSigned", COSUtils.generatePreSignedUrl(str));
                    }
                }
            } else if (value instanceof List) {
                List<String> list = (List<String>) value;
                // 只针对String类型属性进行
                if (Objects.nonNull(annotation) && Objects.equals(List.class, beanProperty.getType().getRawClass())) {
                    List<String> resultList = list.stream().map(COSUtils::generatePreSignedUrl).collect(Collectors.toList());
                    gen.writeObjectField(beanProperty.getName() + "PreSigned", resultList);
                }
    
            }
        }
    }
    

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值