有个企业微信的应用,最近想要在页面里面展示当前用户的微信头像。
用户的头像是在授权登录后,可以拿到userId,然后服务端再通过获取用户个人信息的接口拿到用户的个人信息,包括头像,存到数据库里。为了不增加额外的存储成本,所以不会将用户的头像再上传到我们自己的存储服务,使用时是直接使用微信头像对应的url从微信服务器中取,得到的微信头像url如下:
http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/0
问题
如果我们的前端页面想要直接通过<img src="http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/0" crossorigin="anonymous">
插入到页面中,会出现跨域问题:
Access to image at 'http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/0' from origin 'http://${your-domain}' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
解决思路
上面说了,为了省成本,我们不会在自己的服务器上再存一份头像的图片,所以拷贝到自己服务器的方案就不考虑了。主要两个方案:
- 后端写个接口帮忙获取图片,获取到的图片转为base64返回给前端。
- 前端拼接特殊url前缀,通过nginx代理转发到微信图片服务器。
两个方案其实都差不多,我们来看下第二种方案的具体思路。
- 前端发起
/wechat_image
前缀的请求,url上携带原微信头像的路径,如:
/wechat_image?url=http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/0&host=wework.qpic.cn
- nginx配置拦截
location /wechat_image
的代理。
实现步骤
1. 处理url
从接口拿到avatar的url:http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/0
我们可以使用new URL
来解析一个url,这里以vue代码为例:
<template>
<img :src="avatarLocal" alt="avatar" class="avatar" crossorigin="anonymous" :onerror="defaultImg" />
</template>
<script>
// 一个默认的占位图像
import iconAvatar from '@/assets/images/avatar.png'
export default {
data() {
return {
avatar: 'http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/0'
defaultImg: 'this.src="' + iconAvatar + '"' // 如果失败则显示默认图
}
},
computed: {
avatarLocal () {
const avatar = this.avatar
if (!avatar) {
return iconAvatar
}
// 提取url下的域名和最后面的图片大小
const obj = new URL(avatar)
const prefix = '/wechat_image'
// 去掉最后面的/0,改为/100,表示取100px的小图
const newPath = obj.pathname ? obj.pathname.substr(0, obj.pathname.length - 2) + '/100' : obj.pathname
// http://www.a.com/xxxx/0 的url会转换为: /wechat_image/xxxx/100?url=http://www.a.com/xxxx/100
// nginx做/wechat_image拦截,以解决跨域问题
return `${prefix}${newPath}?url=${obj.origin}${newPath}&host=${obj.hostname}`
},
}
}
</script>
2. nginx拦截配置
考虑到微信头像的域名可能有多个,所以我们的代理需要根据url动态改变。
但nginx有个安全限制,就是如果proxy_pass
后面是一个参数变量,它会报一个no resolver defined to resolve
的错误。因此需要在http
节点配置一下:resolver 8.8.8.8 ipv6=off;
http {
# 解决no resolver defined to resolve错误
resolver 8.8.8.8 ipv6=off;
server {
listen 80;
location /wechat_image {
proxy_set_header Host "$arg_host";
proxy_pass $arg_url;
}
# 其他的配置...
}
}
PS: 在nginx中,可以通过
$arg_name
来获取url后的query参数。
可以看到使用了两个参数$arg_url
和$arg_host
,它们就是上一步在url上拼接的参数,在这个例子里面,他们分别是http://wework.qpic.cn/bizmail/ia7iaXMXfXKuOiapqsvvaibY332FrQpdWPGrbbfGhIeObj2dmXZPJfFLOA/100
和 wework.qpic.cn
。
本来host参数也是可以在url上截取的,但在nginx中不好处理,所以索性传多一个host参数了。
实践证明, proxy_set_header Host "$arg_host";
是必不可少的,不然的话微信图片服务器不会返回真正的图片。
「一键投喂 软糖/蛋糕/布丁/牛奶/冰阔乐!」
(๑>ڡ<)☆谢谢老板~
使用微信扫描二维码完成支付