ThinkPHP
最近做了一个项目,要实现需求客户端上传图片或者视频到阿里云OSS,使用STS认证方式(类似于客户端向服务端要认证签名),在做OSS的STS认证时发现关于php实现这块的技术播客写的普遍笼统,于是决定写一遍相对完整的。
阿里云 STS(Security Token Service)是阿里云提供的一种临时访问权限管理服务。
通过 STS 服务,您所授权的身份主体(RAM 用户、RAM 用户组或 RAM 角色)可以获取一个自定义时效和访问权限的临时访问令牌。STS令牌持有者可以通过以下方式访问阿里云资源:
通过编程方式访问被授权的阿里云服务 API。
登录阿里云控制台操作被授权的云资源。
*RAM (Resource Access Management) 是阿里云为客户提供的用户身份管理与资源访问控制服务。
RAM和STS需要解决的一个核心问题是如何在不暴露主账号的AccessKey的情况下安全的授权别人访问。因为一旦主账号的AccessKey暴露出去的话会带来极大的安全风险,别人可以随意操作该账号下所有的资源,盗取重要信息等。
RAM提供一种长期有效的权限控制机制,通过分出不同权限的子账号,将不同的权限分给不同的用户,这样一旦子账号泄露也不会造成全局的信息泄露。但是,由于子账号在一般情况下是长期有效的,因此,子账号的AccessKey也是不能泄露的。
相对于RAM提供的长效控制机制,STS提供的是一种临时访问授权。通过STS可以返回临时的AccessKey和Token,这些信息可以直接发给临时用户用来访问OSS/VOD服务等。一般来说,从STS获取的权限会受到更加严格的限制,并且拥有时间限制,因此这些信息泄露之后对于系统的影响也很小。
用于 API 访问的 STS 接入点:https://sts.aliyuncs.com。
OSS可以通过阿里云STS (Security Token Service) 进行临时授权访问。阿里云STS是为云计算用户提供临时访问令牌的Web服务。通过STS,您可以为第三方应用或子用户(即用户身份由您自己管理的用户)颁发一个自定义时效和权限的访问凭证。
STS鉴权模式具有以下优势:
无需透露您的长期密钥(AccessKey)给第三方应用,只需生成一个访问令牌并将令牌交给第三方应用。您可以自定义这个令牌的访问权限及有效期限。
无需关心权限撤销问题,访问令牌过期后访问权限会自动失效。
以APP应用为例,交互流程如图所示(来自阿里云官方文档):
以下的这个官方文档先看一下,可能理解起来会更好一点
快速搭建移动应用直传服务 https://help.aliyun.com/document_detail/31920.html
什么是STS https://help.aliyun.com/document_detail/28756.html
STS临时授权访问OSS https://help.aliyun.com/document_detail/100624.html
Browser.js SDK中快速使用OSS服务 https://help.aliyun.com/document_detail/32069.html
#直接步入正题,上代码
HTML代码
<div class="layui-form-item"> <div class="layui-word-aux">缩略图和视频只有一个生效,默认视频的优先级更高,需同时上传</div> <label class="layui-form-label">视频预览</label> <div class="layui-input-block"> <div class="layui-upload-drag xcvideo"> {notempty name="$data.video"} <video controls="controls" height="200"><source src="{$data.video}" type="video/mp4"/></video> {else /} <i class="layui-icon"></i> <p>视频预览~暂无视频</p> {/notempty} </div> </div> <style> .ui-upload input {position: absolute;font-size: 100px;right: 0;top: 0;opacity: 0;filter: alpha(opacity=0);cursor: pointer } </style> <label class="layui-form-label" style=" padding:0;margin-top: 5px; border-style:none"> <a class="layui-btn ui-upload" id="ui-upload" style="width: 100%;"><input type="file" />视频上传</a> </label> <div class="layui-input-block" style="margin-top: 5px;"> <input type="text" name="video" value="{$data.video}" id="video_input" class="layui-input" placeholder="手动上传或输入网址"> </div> </div>
js代码
<!-- 阿里云OSS web端上传 START--> <script type="text/javascript" src="/static/lib/ali-oss/dist/aliyun-oss-sdk.js"></script> <script type="text/javascript" src="/static/admin/easyWeb/libs/jquery/jquery-3.2.1.min.js"></script> <script type="text/javascript"> //console.log("{:langSite('web_url')}{:url('Files/getStsToken')}") document.getElementById('ui-upload').addEventListener('change', function (e) { let file = e.target.files[0]; let storeAs = Date.parse(new Date())/1000+file.name.substr(file.name.lastIndexOf("."),file.name.length); console.log(file.name + ' => ' + storeAs); // OSS.urlib是SDK内部封装的发送请求的逻辑,开发者可以使用任何发送请求的库向sts-server发送请求。 OSS.urllib.request("{:url('Files/getStsToken')}", {method: 'GET'}, (err, response) => { if (err) { alert(err); } try { result = JSON.parse(response); } catch (e) { return alert('parse sts response info error: ' + e.message); } layer.load(); //上传loading let client = new OSS({ accessKeyId: result.accessKeyId, accessKeySecret: result.accessKeySecret, stsToken: result.securityToken, // region表示您申请OSS服务所在的地域,例如oss-cn-hangzhou。 region: result.region, bucket: result.bucket }); // storeAs可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。 // file可以自定义为File对象、Blob数据以及OSS Buffer。 client.multipartUpload(storeAs, file, {}).then(function (result) { layer.closeAll('loading'); var res = result.res; //上传完毕回调 if (res.status == 200){ var data_src = res.requestUrls[0].split("?"); $(".xcvideo").html('<video controls="controls" height="200"><source src="'+ data_src[0] +'" type="video/mp4"/></video>') $("#video_input").val(data_src[0]) } else{ layer.msg('上传失败',{icon:5,time:1500}) } }).catch(function (err) { console.log(err); }); }); }); $(function () { $("#video_input").bind("input propertychange",function(event){ $(".xcvideo").html('<video controls="controls" height="200"><source src="'+$("#video_input").val()+'" type="video/mp4"/></video>') }) }) </script> <!-- 阿里云OSS web端上传 END-->
PHP代码
<?php namespace app\admin\controller; use think\facade\Db; use think\facade\Request; class Files extends Base { protected $url; protected $accessKeyId; protected $accessKeySecret; protected $roleArn; //指定角色的 ARN ,角色策略权限 protected $roleSessionName; //用户自定义参数。此参数用来区分不同的 token,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$ protected $durationSeconds; //指定的过期时间 protected $region; //region表示您申请OSS服务所在的地域,例如oss-cn-hangzhou。 protected $bucket; //Bucket 表示访问域名带上文件路径,例如qdxcy public function __construct() { $this->url = 'https://sts.aliyuncs.com'; $this->accessKeyId = env('oss_sts.accessKeyId'); $this->accessKeySecret = env('oss_sts.accessKeySecret'); $this->roleArn = env('oss_sts.roleArn'); $this->roleSessionName = env('oss_sts.roleSessionName'); $this->durationSeconds = env('oss_sts.durationSeconds'); $this->region = env('oss_sts.region'); $this->bucket = env('oss_sts.bucket'); } #阿里云OSS web端上传 获取STS鉴权 START //获取stsToken public function getStsToken(){ $action = 'AssumeRole';//通过扮演角色接口获取令牌 date_default_timezone_set('UTC'); $param = array( 'Format' => 'JSON', 'Version' => '2015-04-01', 'AccessKeyId' => $this->accessKeyId, 'SignatureMethod' => 'HMAC-SHA1', 'SignatureVersion' => '1.0', 'SignatureNonce' => $this->getRandChar(8), 'Action' => $action, 'RoleArn' => $this->roleArn, 'RoleSessionName' => $this->roleSessionName, 'DurationSeconds' => $this->durationSeconds, 'Timestamp' => date('Y-m-d') . 'T' . date('H:i:s') . 'Z' ); $param['Signature'] = $this->computeSignature($param, 'POST'); $res = http_request($this->url, $param);//curl post请求 if ($res) { $res = json_decode($res, true); if (empty($res['Credentials'])) { return []; } return json([ 'accessKeySecret' => $res['Credentials']['AccessKeySecret'] ?? '', 'accessKeyId' => $res['Credentials']['AccessKeyId'] ?? '', 'expiration' => $res['Credentials']['Expiration'] ?? '', 'securityToken' => $res['Credentials']['SecurityToken'] ?? '', 'region' => $this->region, 'bucket' => $this->bucket, ]); } else { return []; } } //消息签名 protected function computeSignature($parameters, $setMethod) { ksort($parameters); $canonicalizedQueryString = ''; foreach ($parameters as $key => $value) { $canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value); } $stringToSign = $setMethod . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1)); $signature = $this->getSignature($stringToSign, $this->accessKeySecret. '&'); return $signature; } public function getSignature($source, $accessSecret) { return base64_encode(hash_hmac('sha1', $source, $accessSecret, true)); } protected function percentEncode($str) { $res = urlencode($str); $res = preg_replace('/\+/', '%20', $res); $res = preg_replace('/\*/', '%2A', $res); $res = preg_replace('/%7E/', '~', $res); return $res; } //唯一随机数。用于防止网络重放攻击。用户在不同请求间要使用不同的随机数值。 public function getRandChar($length) { $str = null; $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; $max = strlen($strPol) - 1; for ($i = 0; $i < $length; $i++) { $str .= $strPol[rand(0, $max)]; //rand($min,$max)生成介于min和max两个数之间的一个随机整数 } return $str; } #阿里云OSS web端上传 获取STS鉴权 END
获取STS返回结果
{ "Credentials": { "AccessKeyId": "STS.xxxxxxxxxxxxx****",//访问密钥标识 "AccessKeySecret": "xxxxxxxxxx****",//访问密钥 "Expiration": "2019-04-09T11:52:19Z",//失效时间 "SecurityToken": "********"//安全令牌 }, "AssumedRoleUser": { "arn": "acs:sts::123456765456****:assumed-role/AdminRole/client", "AssumedRoleUserId":"1234567121****:alice" }, "RequestId": "xxxxxxxxxxxxxxxx" }
相信大家看完前面的解析、代码及返回结果后大概对怎么使用STS鉴权模式有一定了解;返回结果中对于客户端APP来讲需要用的有AccessKeyId、AccessKeySecret、Expiration、SecurityToken,客户端拿到这四个参数后将其传给阿里云ossSDK,vodSDK等便可以正常使用oss上传服务或者短视频vod服务了。