全网首发 最佳实践 实现img标签转发访问需要权限的资源的最佳实践 src访问需要权限的资源 全网首发 可实现一切资源访问(附代码)Springboot

本文档介绍了如何在前端无法直接通过img标签访问需要权限的第三方资源时,利用后端进行转发访问的解决方案。通过后端控制器接收请求,携带token转发请求,获取资源后返回给前端,实现了安全地在前端展示资源。详细步骤包括控制器、服务层和服务工具类的代码实现,涉及OkHttp3工具包的使用。
摘要由CSDN通过智能技术生成

实现img标签转发访问需要权限的资源

概述

在对接三方服务中,有诸多接口访问资源是需要权限的,比如图片视频此类的,那么前端就无法直接用img等标签来直接展示了,那么本文章的就是提供一种后代转发访问资源来实现页面标签直接访问的。

问题

在对接三方服务中,比如我这次业务是对接的上上签(https://www.bestsign.cn/)服务,那么其中其提供的查询合同详情接口会返回片段例如"highQualityPreviewUrl": "/contract-api/contracts/xxx/documents/xxx/get-high-quality-document-image/1?needMergeLabel=true",(xxx是id,模糊处理),前端页面通过<img src=host+highQualityPreviewUrl>是无法访问的,必须通过jwt即请求头中加Cookie:acc_token=xxxxx这样的方式才能访问到其资源,对于前端来说是不易实现的,因为根据w3c协议浏览器是禁止这样设置头信息的,很多浏览器也遵循了这个协议,那么有没有这样还是通过img标签来访问这些资源呢,答案是有的。

原理

通过上面的问题描述来看,前端是很难实现这种问题的解决了。那么后端来实现转发资源就很简单了,且token不暴露更加安全。

流程

前端通过标签访问服务器->服务器携带token转发访问三方服务器->将三方资源转发给到前端->前端展示

演示

演示

实现

下面就直接给出相关层的代码了,这里我以img标签来实现图片资源访问来举例,也可以其他资源实现,本质就是response.getOutputStream里面的流不一样,给出很全面的代码,最核心的controller代码

controller

// 注意这里一定是@Controller这个注解
@Controller
@RequestMapping("openForword/signOnline")
public class SignOnlineOpenForwordController {
    @Autowired
    private IBestSignService bestSignService;
    
    @GetMapping("/viewUrl")
    public void viewUrl(@RequestParam(value = "url") String url, HttpServletResponse response) throws Exception {
        // todo 服务转发获取到资源的流
        String base64code = bestSignService.viewUrlForword(url, response);
        BufferedImage bi = Base64Util.base64ToBufferedImage(base64code);
        // 将流写入到response.getOutputStream()中
        ImageIO.write(bi, "JPEG", response.getOutputStream());
    }
}

serviceimpl

这里的ajaxResult 结果可以替换成okhttp3的集成工具来获取到三方服务器的内容,这个方法里一定要是有token携带访问到的资源工具,大同小异,我会在util给出okhttp3的工具

public String viewUrlForword(String url, HttpServletResponse response) {

        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");

        AjaxResult ajaxResult = bestSignClient.executeRequest(url, "GET", new JSONObject(), System.currentTimeMillis(), "");

        return ajaxResult.getData().getReposebody();
    }

util

Base64Util工具

public class Base64Util {

    /**
     * 将文件转成base64 字符串
     * 不含/r/n
     *
     * @param file 文件
     * @return *
     * @throws Exception
     */
    public static String file2base64Str(File file) throws Exception {
        byte[] bFile;
        byte[] bEncodedFile = null;
        try {
            bFile = Files.readAllBytes(file.toPath());
            bEncodedFile = Base64.getEncoder().encode(bFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(bEncodedFile);
    }

    /**
     * 将base64字符解码保存文件
     * 不含/r/n
     *
     * @param base64Code
     * @param file
     * @throws Exception
     */
    public static void base64Str2file(String base64Code, File file) throws Exception {
        FileOutputStream fos;
        byte[] bFileDecodeString = Base64.getDecoder().decode(base64Code);
        try {
            fos = new FileOutputStream(file);
            fos.write(bFileDecodeString);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * base64TobyteArr
     *
     * @param base64code
     * @return
     */
    public static byte[] base64ToByteArr(String base64code) {
        return DatatypeConverter.parseBase64Binary(base64code);
    }

    /**
     * byteArrTobyteArr
     *
     * @param bytes
     * @return
     */
    public static String byteArrToBase64(byte[] bytes) {
        return DatatypeConverter.printBase64Binary(bytes);
    }


    /**
     * BufferedImage 编码转换为 base64
     *
     * @param bufferedImage
     * @return
     */
    public static String BufferedImageToBase64(BufferedImage bufferedImage) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ImageIO.write(bufferedImage, "png", baos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        byte[] bytes = baos.toByteArray();
        return byteArrToBase64(bytes);
    }

    /**
     * base64 编码转换为 BufferedImage
     *
     * @param base64
     * @return
     */
    public static BufferedImage base64ToBufferedImage(String base64) {
        BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        try {
            byte[] bytes = decoder.decodeBuffer(base64);
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            return ImageIO.read(bais);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


}

OkHttp3集成的工具包

@Slf4j
@SuppressWarnings("all")
public class OkHttpClientUtil {
    //shared perform best
    private static final OkHttpClient okHttpClient = new OkHttpClient
            .Builder()
            .readTimeout(1, TimeUnit.MINUTES)
            .build();

    private static final ObjectMapper objectMapper = new ObjectMapper();

    private static final MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");

    private static final MediaType MULTIPART_TYPE = MediaType.parse("multipart/form-data");

    /**
     * @param host
     * @param uri
     * @param method
     * @param headers
     * @param requestData
     * @return
     */
    public static Response ajax(String host, String uri, String method, Headers headers, JSONObject requestData) {
        log.info("host->{}", host);
        log.info("uri->{}", uri);
        log.info("method->{}", method);
        log.info("headers->{}", headers);
        log.info("requestData->{}", requestData);
        String urlWithQueryParam = String.format("%s%s", host, uri);
        try {
            final Request.Builder requestBuilder = new Request
                    .Builder()
                    .url(urlWithQueryParam)
                    .headers(headers);

            if (Objects.equals(method.toUpperCase(), "GET")) {
                requestBuilder.get();
            } else {
                final RequestBody requestBody = RequestBody.create(JSON_TYPE, requestData == null ? "" : objectMapper.writeValueAsString(requestData));

                requestBuilder.method(method.toUpperCase(), requestBody);
            }
            final Response response = okHttpClient.newCall(requestBuilder.build()).execute();
            log.info("response->{}", response);
            return response;
        } catch (Exception e) {
            log.error("Exception e->{}", e);
        }
        return null;
    }

    /**
     * @param host
     * @param uri
     * @param method
     * @param headers
     * @param requestData
     * @return
     */
    public static Response update(String host, String uri, Headers headers, JSONObject requestData, String parameterName, File file) {
        log.info("host->{}", host);
        log.info("uri->{}", uri);
        log.info("headers->{}", headers);
        log.info("requestData->{}", requestData);
        String urlWithQueryParam = String.format("%s%s", host, uri);
        try {
            RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);

            MultipartBody body = new MultipartBody.Builder()
                    .setType(MULTIPART_TYPE)
                    .addFormDataPart(parameterName, file.getName(), fileBody)
                    .build();

            final Request.Builder requestBuilder = new Request
                    .Builder()
                    .post(body)
                    .url(urlWithQueryParam)
                    .headers(headers);

            final Response response = okHttpClient.newCall(requestBuilder.build()).execute();
            log.info("response->{}", response);
            return response;
        } catch (Exception e) {
            log.error("Exception e->{}", e);
        }
        return null;
    }


    /**
     * 同步下载网络文件
     *
     * @param url          下载的链接,精确到文件
     * @param destFileDir  下载的文件储存目录
     * @param destFileName 下载文件名称(自己命名)
     * @return file
     */
    public static File downloadSyn(final String url, final String destFileDir, final String destFileName) {
        Request request = new Request.Builder().url(url).build();
        // 同步请求
        Response response = null;
        InputStream is = null;
        byte[] buf = new byte[4096];
        int len = 0;
        FileOutputStream fos = null;

        try {
            response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()) {
                // 储存下载文件的目录
                File dir = new File(destFileDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                File file = new File(dir, destFileName);
                is = response.body().byteStream();
                fos = new FileOutputStream(file);
                int size = 0;
                long total = response.body().contentLength();
                while ((size = is.read(buf)) != -1) {
                    len += size;
                    fos.write(buf, 0, size);
                    int process = (int) Math.floor(((double) len / total) * 100);
                    log.info("FileName:{},Downloading progress:{} %", destFileName, process);
                }
                fos.flush();
                return file;
            } else {
                throw new IOException("Unexpected code " + response);
            }
        } catch (IOException e) {
            log.error("error:{}", e);
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return null;
    }

    /**
     * 异步下载网络文件
     *
     * @param url          下载的链接,精确到文件
     * @param destFileDir  下载的文件储存目录
     * @param destFileName 下载文件名称(自己命名)
     * @param listener     下载监听
     */
    public static void downloadsASyn(final String url, final String destFileDir, final String destFileName, final IOnDownloadListener listener) {
        Request request = new Request.Builder()
                .url(url)
                .build();
        //异步请求
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 下载失败监听回调
                listener.onDownloadFailed(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream is = null;
                byte[] buf = new byte[4096];
                int len = 0;
                FileOutputStream fos = null;

                // 储存下载文件的目录
                File dir = new File(destFileDir);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                File file = new File(dir, destFileName);

                try {
                    is = response.body().byteStream();
                    fos = new FileOutputStream(file);
                    int size = 0;
                    long total = response.body().contentLength();
                    while ((size = is.read(buf)) != -1) {
                        len += size;
                        fos.write(buf, 0, size);
                        int process = (int) Math.floor(((double) len / total) * 100);
                        // 控制台打印文件下载的百分比情况
                        listener.onDownloading(destFileName, process);
                    }

                    fos.flush();
                    // 下载完成
                    listener.onDownloadSuccess(file);
                } catch (Exception e) {
                    log.error("error:{}", e);
                    listener.onDownloadFailed(e);
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                }

            }
        });
    }


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若光672

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值