protected $appKey = '';
protected $appSecret = '';
protected $service = ''; //所使用的服务地址
protected $oauthServerUrl = ''; //授权地址
protected $notifyUrl = 'http://cs.*****.com/index/index/notify'; //回调地址
//先获取code,然后再用code换取openid
public function index()
{
import('aes.Aes',EXTEND_PATH); //引入AES类(文档后面会附上)
$aes = new Aes($this->appSecret);
$time = time()."000"; //学科网要求必须是毫秒单位,所以直接给他拼接了三个0(简单粗暴)
$encrypted = $aes->encrypt($time); //使用ECB-128-pkcs5 进行加密 后面会附上AES类,可使用:http://tool.chacuo.net/cryptaes 此网站验证加密的是否正确,加密后能解出来就是对的,输出为base64-utf8
//md5参数:这里其实还有一个参数:openid,是选填的,如果已经有这个openid,直接带上会跳转,不用再进行授权
$params = array(
"client_id" =>$this->appKey,
"timespan" =>$encrypted,
"redirect_uri" =>$this->notifyUrl,
"service" =>$this->oauthServerUrl,
);
$params["signature"] = $this->createSign($params,$this->appSecret);//md5规则:进行ascii进行参数key升序培训,排完再进行参数的value拼接,最后拼上appSecret,进行md5,取32位小写。后附上方法
$data = http_build_query($params);
$url = $this->oauthServerUrl."/oauth2/authorize?";
$url .= "&" . $data;
$this->redirect($url, 302);// tp5的重定向方法
}
至此,会跳转到学科网的登录页面,进行登录授权。,用户登录完,会返回code至 notifyUrl 上,如果未跳转,也会有相应的错误提示
获取成功示例:
{"access_token":"VEdULTI2Ny1WdXRv*************************MS56eHhrLmNvbSM4MDg=","expires":7200}
不太懂失败是什么样的...........
public function notify()
{
$param = $this->request->param();
if(empty($param)){
return false;
}
$uid = 1;
$params = array(
"client_id" =>$this->appKey,
"redirect_uri" =>$this->notifyUrl,
"code" =>$param["code"],
);
$params["signature"] = $this->createSign($params,$this->appSecret);
$url = $this->oauthServerUrl."/oauth2/accessToken?";
$data = http_build_query($params);
$url .= "&" . $data;
$rs = $this->getHtml($url,$data); //进行curl获取openid
$rs = json_decode($rs,true);
if(isset($rs["error"])){
//这里最好写个错误日志
return false;
}
$url = $this->oauthServerUrl . "/oauth2/profile?access_token={$rs["access_token"]}";
$rs = $this->getHtml($url, $data);
$rs = json_decode($rs, true);
if (isset($rs["error"])) {
return false;
}
//获取到openid后,最好绑定到uid上,那边技术说不会随便变更,下次如果有,可直接进行跳转服务连接,不用再授权
Db::name("user")->where("id",$uid)->setField("openid",$rs["open_id"]);
$url = $this->oauthServerUrl . "?service={$this->service}";
$this->redirect($url, 302);// 跳转到指定的学科网服务上
}
下面附上签名方法和curl方法
/**
* 签名生成算法
* @access public
* @param array data 加密参数
* @param string key 用户秘钥
* @return string sign 签名
*/
protected function createSign($data, $key)
{
if (!$data || !$key) {
return false;
}
$sort_key = array_keys($data);
//排序
sort($sort_key);
$str = "";
//拼接
foreach ($sort_key as $v) {
$str .= $data[$v];
}
//拼接key
$str .= $key;
//MD5加密
$str = md5($str);
return $str;
}
//curl
protected function getHtml($url, $data = '')
{
$curl = curl_init();
curl_setopt($curl,CURLOPT_URL,$url);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
curl_setopt($curl,CURLOPT_POST,1);
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
$data = curl_exec($curl);
// ob_end_clean();
curl_close($curl);
return $data;
}
下面附上AES类
tp5 文件路径:
<?php
class Aes
{
/**
* var string $method 加解密方法,可通过openssl_get_cipher_methods()获得
*/
protected $method;
/**
* var string $secret_key 加解密的密钥
*/
protected $secret_key;
/**
* var string $iv 加解密的向量,有些方法需要设置比如CBC
*/
protected $iv;
/**
* var string $options (不知道怎么解释,目前设置为0没什么问题)
*/
protected $options;
/**
* 构造函数
*
* @param string $key 密钥
* @param string $method 加密方式
* @param string $iv iv向量
* @param mixed $options 还不是很清楚
*
*/
public function __construct($key, $method = 'AES-128-ECB', $iv = '', $options = 0)
{
// key是必须要设置的
$this->secret_key = isset($key) ? $key : 'morefun';
$this->method = $method;
$this->iv = $iv;
$this->options = $options;
}
/**
* 加密方法,对数据进行加密,返回加密后的数据
*
* @param string $encryptStr 要加密的数据
*
* @return string
*
*/
public function encrypt($encryptStr) {
$localIV = $this->iv;
$encryptKey = $this->secret_key;
//Open module
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, $localIV);
mcrypt_generic_init($module, $encryptKey, $localIV);
//Padding
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
$pad = $block - (strlen($encryptStr) % $block); //Compute how many characters need to pad
$encryptStr .= str_repeat(chr($pad), $pad); // After pad, the str length must be equal to block or its integer multiples
//encrypt
$encrypted = mcrypt_generic($module, $encryptStr);
//Close
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
return base64_encode($encrypted);
}
/**
* 解密方法,对数据进行解密,返回解密后的数据
*
* @param string $encryptStr 要解密的数据
*
* @return string
*
*/
public function decrypt($encryptStr) {
$decrypted= mcrypt_decrypt(MCRYPT_RIJNDAEL_128,$this->secret_key,base64_decode($encryptStr), MCRYPT_MODE_ECB);
$dec_s = strlen($decrypted);
$padding = ord($decrypted[$dec_s-1]);
$decrypted = substr($decrypted, 0, -$padding);
return $decrypted;
}
}