深入研究 UCenter API

UCenter 通讯基本原理

UCenter和各个子站的通讯,主要就是通过 POST 的方式调用而已,没有什么技术含量。

深入研究 UCenter API_第1张图片

表单参数都是经过 Base64 算法,加一个通信密钥进行加密和解密的。

返回的数据是单个参数(例如:0或者1),也可能是xml序列化后的数据。

例如一个请求:code=e145fscn314BSKnwxBvqLaQe2yrHJAnKO1M%2B8C4cAKQAtRRQfEqTh8mg665UVJPyrJIrPhDNnEM

解密后:action=test&time=1295631663

返回:1

以上是一个测试是否通讯成功的请求,上面是表单参数,解密的明文如上,返回1代表通讯成功!

而其中的难点就在于,Discuz 并没有公开所有的 API!而仅仅是提供了一个 php 版的函数库,帮你写好了这些通讯函数。

所以 php 的网站做起来很轻松,而别的网站就痛苦了。

这里的痛苦包括加密和解密函数、序列化和反序列化、还有期中各个参数的名称和格式…

怎么解决?只能翻阅 php 版本的源代码,把它的 API 一条条看过去,然后用自己需要用的语言重写一遍。

 

 

UCenter 多点登录的原理及过程

通讯原理就这么点,很简单,子站想联系 UCenter,就调用一个地址,传递一些参数就行了,反之亦然。

最难的其实是多点登录的过程。

因为一般的请求,比如在子站请求登录,调用一下,然后返回数据,就完成了;而多点登录没那么简单。

理解原理后才能知道应该怎么用多点登录。

 

先看一下多点登录的过程吧:

深入研究 UCenter API_第2张图片

子站调用封装好的登录函数(参数:帐号、密码),返回登录信息(包括:uid、用户名、E-mail)

子站调用同步登录函数(参数:uid),返回一段 javascript 代码

在子站前端页面执行这段 javascript 代码

该代码通知其他子站,调用它们的同步登录函数

 

解惑:

刚看完这个,我也很疑惑,为什么要这样?那我反问下,你想怎么样?

1、子站调用同步登录函数,UCenter 通知子站同步登录?

2、子站调用同步登录函数,得到别的子站的同步登录 API,直接调用?

 

为什么上面两个不行?什么叫同步登录?

那就是:用户的电脑需要在每个子站下保存了 Session,或者在用户电脑上保存了该每个子站的 Cookie

 

那前面两个方案:

方案一:在 UCenter 服务器保存了 Cookie,所有子站服务器上的 Session 记录记录的是 UCenter 服务器的,而不是用户的

方案二:同理可得,Cookie 保存在了一台子站的服务器上,Session 记录的是那个子站服务器的

所以,一定要用户的电脑通知各子站才行,那怎么通知呢?通过 javascript

 

关键:

所以,UCenter 在同步登录的过程中,最关键的就是需要把这个 API 返回的 javascript 代码在前端运行

可以这样写:

using DS.Web.UCenterAPI.UCClient;

var client = new UCClient();
var ucLoginReturn =  client.UC_User_Login("admin", "admin");
if(ucLoginReturn.Success)
{
    var js = client.UC_User_Synlogin(ucLoginReturn.User.Uid);
    Response.Write(js);
}

 

这样就可以实现同步登录了,我们来看一下 UC_User_Synlogin 函数返回的 js 代码吧:

深入研究 UCenter API_第3张图片

 

 

AuthCode函数

这个函数是什么?传说这是康盛公司对 php 发展做出的一个极大的贡献…

该函数可以实现可逆加密,在 php 中广为流传…

AuthCode

UCenter API 中的加密解密函数,被称为 php 领域的经典之作,也是康盛公司为 php 做的一大贡献

这个函数,可以通过一个 KEY ,生成动态的密文,并可以再通过这个 KEY 来解密

我没有研究过什么加密算法,所以对这个的基础知识也不是很了解,或许在 C# 中会有更强大的算法,但是这个函数在做 UCenter API 的时候是必需的。

也是 UCenter API php 版翻译成 C# 版本中最难的一个部分。

 

PHP 版详解

// $string: 明文 或 密文
// $operation:DECODE表示解密,其它表示加密
// $key: 密匙
// $expiry:密文有效期
//字符串解密加密
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
     // 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
    $ckey_length = 4;   // 随机密钥长度 取值 0-32;
                // 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
                // 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
                // 当此值为 0 时,则不产生随机密钥
    // 密匙
    $key = md5($key ? $key : UC_KEY);
    // 密匙a会参与加解密
    $keya = md5(substr($key, 0, 16));
    // 密匙b会用来做数据完整性验证
    $keyb = md5(substr($key, 16, 16));
    // 密匙c用于变化生成的密文
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
    // 参与运算的密匙
    $cryptkey = $keya.md5($keya.$keyc);
    $key_length = strlen($cryptkey);   

    // 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
    // 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
    $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
    $string_length = strlen($string);   

    $result = '';
    $box = range(0, 255);   

    $rndkey = array();
    // 产生密匙簿
    for($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($cryptkey[$i % $key_length]);
     }
     // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
    for($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
     }
    // 核心加解密部分
    for($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        // 从密匙簿得出密匙进行异或,再转成字符
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
     }   

    if($operation == 'DECODE') {
        // 验证数据有效性,请看未加密明文的格式
        if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) {
            return substr($result, 26);
         } else {
            return '';
         }
     } else {
         // 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
         // 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
        return $keyc.str_replace('=', '', base64_encode($result));
     }
}

 

这份详解不是我写的,网上有很多,找不到原作者了

 

 

C# 版

/// <summary>
/// AuthCode解码&编码
/// </summary>
/// <param name="sourceStr">原始字符串</param>
/// <param name="operation">操作类型</param>
/// <param name="keyStr">API KEY</param>
/// <param name="expiry">过期时间 0代表永不过期</param>
/// <returns></returns>
private static string AuthCode(string sourceStr, AuthCodeMethod operation, string keyStr, int expiry = 0)
{
    var ckeyLength = 4;
    var source = Encode.GetBytes(sourceStr);
    var key = Encode.GetBytes(keyStr);

    key = Md5(key);

    var keya = Md5(SubBytes(key, 0, 0x10));
    var keyb = Md5(SubBytes(key, 0x10, 0x10));
    var keyc = (ckeyLength > 0)
                    ? ((operation == AuthCodeMethod.Decode)
                            ? SubBytes(source, 0, ckeyLength)
                            : RandomBytes(ckeyLength))
                    : new byte[0];

    var cryptkey = AddBytes(keya, Md5(AddBytes(keya, keyc)));
    var keyLength = cryptkey.Length;

    if (operation == AuthCodeMethod.Decode)
    {
        while (source.Length % 4 != 0)
        {
            source = AddBytes(source, Encode.GetBytes("="));
        }
        source = Convert.FromBase64String(BytesToString(SubBytes(source, ckeyLength)));
    }
    else
    {
        source =
            AddBytes(
                (expiry != 0
                        ? Encode.GetBytes((expiry + PhpTimeNow()).ToString())
                        : Encode.GetBytes("0000000000")),
                SubBytes(Md5(AddBytes(source, keyb)), 0, 0x10), source);
    }

    var sourceLength = source.Length;

    var box = new int[256];
    for (var k = 0; k < 256; k++)
    {
        box[k] = k;
    }

    var rndkey = new int[256];
    for (var i = 0; i < 256; i++)
    {
        rndkey[i] = cryptkey[i % keyLength];
    }

    for (int j = 0, i = 0; i < 256; i++)
    {
        j = (j + box[i] + rndkey[i]) % 256;
        var tmp = box[i];
        box[i] = box[j];
        box[j] = tmp;
    }

    var result = new byte[sourceLength];
    for (int a = 0, j = 0, i = 0; i < sourceLength; i++)
    {
        a = (a + 1) % 256;
        j = (j + box[a]) % 256;
        var tmp = box[a];
        box[a] = box[j];
        box[j] = tmp;

        result[i] = (byte)(source[i] ^ (box[(box[a] + box[j]) % 256]));
    }

    if (operation == AuthCodeMethod.Decode)
    {
        var time = long.Parse(BytesToString(SubBytes(result, 0, 10)));
        if ((time == 0 ||
                time - PhpTimeNow() > 0) &&
            BytesToString(SubBytes(result, 10, 16)) == BytesToString(SubBytes(Md5(AddBytes(SubBytes(result, 26), keyb)), 0, 16)))
        {
            return BytesToString(SubBytes(result, 26));
        }
        return "";
    }
    return BytesToString(keyc) + Convert.ToBase64String(result).Replace("=", "");
}

/// <summary>
/// Byte数组转字符串
/// </summary>
/// <param name="b">数组</param>
/// <returns></returns>
public static string BytesToString(byte[] b)
{
    return new string(Encode.GetChars(b));
}

/// <summary>
/// 计算Md5
/// </summary>
/// <param name="b">byte数组</param>
/// <returns>计算好的字符串</returns>
public static byte[] Md5(byte[] b)
{
    var cryptHandler = new MD5CryptoServiceProvider();
    var hash = cryptHandler.ComputeHash(b);
    var ret = "";
    foreach (var a in hash)
    {
        if (a < 16)
        { ret += "0" + a.ToString("x"); }
        else
        { ret += a.ToString("x"); }
    }
    return Encode.GetBytes(ret);
}

/// <summary>
/// Byte数组相加
/// </summary>
/// <param name="bytes">数组</param>
/// <returns></returns>
public static byte[] AddBytes(params byte[][] bytes)
{
    var index = 0;
    var length = 0;
    foreach(var b in bytes)
    {
        length += b.Length;
    }
    var result = new byte[length];

    foreach(var bs in bytes)
    {
        foreach (var b in bs)
        {
            result[index++] = b;
        }
    }
    return result;
}

/// <summary>
/// Byte数组分割
/// </summary>
/// <param name="b">数组</param>
/// <param name="start">开始</param>
/// <param name="length">结束</param>
/// <returns></returns>
public static byte[] SubBytes(byte[] b, int start, int length = int.MaxValue)
{
    if (start >= b.Length) return new byte[0];
    if (start < 0) start = 0;
    if (length < 0) length = 0;
    if (length>b.Length || start + length > b.Length) length = b.Length - start;
    var result = new byte[length];
    var index = 0;
    for(var k = start;k< start + length;k++)
    {
        result[index++] = b[k];
    }
    return result;
}

/// <summary>
/// 计算Php格式的当前时间
/// </summary>
/// <returns>Php格式的时间</returns>
public static long PhpTimeNow()
{
    return DateTimeToPhpTime(DateTime.UtcNow);
}

/// <summary>
/// PhpTime转DataTime
/// </summary>
/// <returns></returns>
public static DateTime PhpTimeToDateTime(long time)
{
    var timeStamp = new DateTime(1970, 1, 1); //得到1970年的时间戳
    var t = (time + 8 * 60 * 60) * 10000000 + timeStamp.Ticks;
    return new DateTime(t);
}

/// <summary>
/// DataTime转PhpTime
/// </summary>
/// <param name="datetime">时间</param>
/// <returns></returns>
public static long DateTimeToPhpTime(DateTime datetime)
{
    var timeStamp = new DateTime(1970, 1, 1);  //得到1970年的时间戳
    return (datetime.Ticks - timeStamp.Ticks) / 10000000;  //注意这里有时区问题,用now就要减掉8个小时
}

/// <summary>
/// 随机字符串
/// </summary>
/// <param name="lens">长度</param>
/// <returns></returns>
public static byte[] RandomBytes(int lens)
{
    var chArray = new[]
                        {
                            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
                            'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
                            'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                            'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
                        };
    var length = chArray.Length;
    var result = new byte[lens];
    var random = new Random();
    for (var i = 0; i < lens; i++)
    {
        result[i] = (byte) chArray[random.Next(length)];
    }
    return result;
}

/// <summary>
/// 操作类型
/// </summary>
enum AuthCodeMethod
{
    Encode,
    Decode,
}

C# 版是一行一行按照原版本翻译的,增加了一些 C# 中没有的函数

 

1、string -> byte[] 的问题

在这段算法中,经常会用到 Base64 算法,C# 中的 Base64 要求输入的是 byte[] 数组

在 php 程序中,都是直接用字符串的,而且也没有问题。

那在 C# 版中自然想到了 Encoding.Default.GetBytes() 函数

但这个函数有个很奇怪的问题:

Encoding.UTF8.GetBytes(((char) 200).ToString())[0].ToString() //最后的值是多少?

运行一下后发现它不是200,因为这个函数涉及到了编码问题

所以上述的操作,如果直接对字符串操作,那会出现很多问题,因为 php 和 C# 对字符串使用的默认编码不同。

所以就改成了对 byte[] 进行操作

康盛旗下产品的搭建

1、UCenter

这个当然是最基本的东西,安装起来也很简单,官方就有教程

http://faq.comsenz.com/userguide/x/install.html

深入研究 UCenter API_第4张图片

安装完成后,因为还没有安装别的应用,所以应用数量是:0

2、Discuz

如果仅仅是为了用 UCenter,那有点得不偿失了,一般都会配上论坛

这里采用的是 Discuz! 7.2

http://faq.comsenz.com/userguide/discuz/install.html

深入研究 UCenter API_第5张图片

这里没有什么难度,网上有许多教程

 

 

Asp.net 测试网站的搭建

新建网站

既然是 UCenter 和 Asp.net 通讯,那肯定要搭建一个 Asp.net 的网站了

为了 方便测试,我们最好把网站直接在 IIS 中调试

新建网站应用程序 — 打开属性页面

深入研究 UCenter API_第6张图片

这步非不要操作,但是可以模拟真实的场景,并且还可以在 IIS 里调试

 

设置完后我们看一下 IIS 里的情况(我把 UCenter 和 Discuz 挂在 IIS 下了)

 

在 UCenter 下新建应用程序

登录后点添加新应用

深入研究 UCenter API_第7张图片

 

按照这张图配置一下

深入研究 UCenter API_第8张图片

这里就一个地方和配置 php 的网站不同,就是“应用接口文件名称”

当然你也可以用 .php 然后配置 IIS,但是这个多麻烦?用 ashx 是最方便的,在后面会有详解,到时候你就知道为什么了

 

提交后复制一下配置信息,后面有用

深入研究 UCenter API_第9张图片

 

配置Asp.net网站

接下来我们需要把配置文件写入 Asp.net 的网站的 Web.config 中

乍一看,这配置是 php 格式的!

但这里有一份完整的配置信息,只要替换对应的地方就行:

 

<!--客户端版本-->
<add key="UC_CLIENT_VERSION" value="1.5.2"/>
<!--发行时间-->
<add key="UC_CLIENT_RELEASE" value="20101001"/>

<!--API 开关(value类型:True False 默认值:True)-->
<!--是否允许删除用户-->
<add key="API_DELETEUSER" value="True"/>
<!--是否允许重命名用户-->
<add key="API_RENAMEUSER" value="True"/>
<!--是否允许得到标签-->
<add key="API_GETTAG" value="True"/>
<!--是否允许同步登录-->
<add key="API_SYNLOGIN" value="True"/>
<!--是否允许同步登出-->
<add key="API_SYNLOGOUT" value="True"/>
<!--是否允许更改密码-->
<add key="API_UPDATEPW" value="True"/>
<!--是否允许更新关键字-->
<add key="API_UPDATEBADWORDS" value="True"/>
<!--是否允许更新域名解析缓存-->
<add key="API_UPDATEHOSTS" value="True"/>
<!--是否允许更新应用列表-->
<add key="API_UPDATEAPPS" value="True"/>
<!--是否允许更新客户端缓存-->
<add key="API_UPDATECLIENT" value="True"/>
<!--是否允许更新用户积分-->
<add key="API_UPDATECREDIT" value="True"/>
<!--是否允许向UCenter提供积分设置-->
<add key="API_GETCREDITSETTINGS" value="True"/>
<!--是否允许获取用户的某项积分-->
<add key="API_GETCREDIT" value="True"/>
<!--是否允许更新应用积分设置-->
<add key="API_UPDATECREDITSETTINGS" value="True"/>
<!--API 开关结束-->

<!--返回值设置-->
<!--返回成功(默认:1)-->
<add key="API_RETURN_SUCCEED" value="1"/>
<!--返回失败(默认:-1)-->
<add key="API_RETURN_FAILED" value="-1"/>
<!--返回禁用(默认:-2)-->
<add key="API_RETURN_FORBIDDEN" value="-2"/>
<!--返回值设置结束-->

<!--[必填]通信密钥-->
<add key="UC_KEY" value="FD144298AF7E4797A66ACC0C18C97EA3"/>
<!--[必填]UCenter地址-->
<add key="UC_API" value="http://localhost/ucenter"/>
<!--[必填]默认编码-->
<add key="UC_CHARSET" value="utf-8"/>
<!--[非必填]UCenter IP-->
<add key="UC_IP" value=""/>
<!--[必填]应用ID-->
<add key="UC_APPID" value="2"/>

其中,除了标记必填的,别的都可以不填,默认值就是这个

Asp.net 网站算是搭建成功了,但是现在还没有用到那个类库呢!

 

 

类库的使用方法

类库概况

深入研究 UCenter API_第10张图片

 

类库分为以下几个部分

  1. Api 用于提供给 UCenter 调用的结构

  2. Client 用于调用 UCenter 的接口

  3. Model 调用过程中的一些数据封装

  4. UcConfig 静态类,读取上面的配置文件信息

  5. UcUtility 一些常用函数

  6. App.config 配置文件示例

 

 

调用 UCenter API

这步非常简单,只要配置好前面的东西,然后简单地调用一个类就行了。

 

IUcClient client = new UcClient();
var user = client.UserLogin("admin", "admin");//登陆
if (user.Success)//判断是否登陆成功
{
    client.PmSend(0, 0, "公告", "测试公告", user.Uid);//给该用户发送系统消息
}

 

那具体有哪些函数可以被调用呢?可以看一下IUcClient接口

 

using System.Collections.Generic;

namespace DS.Web.UCenter.Client
{
    ///<summary>
    /// UcApi客户端
    ///</summary>
    public interface IUcClient
    {
        /// <summary>
        /// 用户注册
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="passWord">密码</param>
        /// <param name="email">Email</param>
        /// <param name="questionId">登陆问题</param>
        /// <param name="answer">答案</param>
        /// <returns></returns>
        UcUserRegister UserRegister(string userName, string passWord, string email, int questionId = 0, string answer = "");

        /// <summary>
        /// 用户登陆
        /// </summary>
        /// <param name="userName">用户名/Uid/Email</param>
        /// <param name="passWord">密码</param>
        /// <param name="loginMethod">登录方式</param>
        /// <param name="checkques">需要登陆问题</param>
        /// <param name="questionId">问题ID</param>
        /// <param name="answer">答案</param>
        /// <returns></returns>
        UcUserLogin UserLogin(string userName, string passWord, LoginMethod loginMethod = LoginMethod.UserName, bool checkques = false, int questionId = 0, string answer = "");

        /// <summary>
        /// 得到用户信息
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns></returns>
        UcUserInfo UserInfo(string userName);

        /// <summary>
        /// 得到用户信息
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        UcUserInfo UserInfo(int uid);

        /// <summary>
        /// 更新用户信息
        /// 更新资料需验证用户的原密码是否正确,除非指定 ignoreoldpw 为 1。
        /// 如果只修改 Email 不修改密码,可让 newpw 为空;
        /// 同理如果只修改密码不修改 Email,可让 email 为空。
        /// </summary>
        /// <returns></returns>
        UcUserEdit UserEdit(string userName, string oldPw, string newPw, string email, bool ignoreOldPw = false, int questionId = 0, string answer = "");

        /// <summary>
        /// 删除用户
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        bool UserDelete(params int[] uid);

        /// <summary>
        /// 删除用户头像
        /// </summary>
        /// <param name="uid">Uid</param>
        void UserDeleteAvatar(params int[] uid);

        /// <summary>
        /// 同步登陆
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns>同步登陆的 Html 代码</returns>
        string UserSynlogin(int uid);

        /// <summary>
        /// 同步登出
        /// </summary>
        /// <returns>同步登出的 Html 代码</returns>
        string UserSynLogout();

        /// <summary>
        /// 检查 Email 格式
        /// </summary>
        /// <param name="email">Email</param>
        /// <returns></returns>
        UcUserCheckEmail UserCheckEmail(string email);

        /// <summary>
        /// 增加受保护用户
        /// </summary>
        /// <param name="admin">操作管理员</param>
        /// <param name="userName">用户名</param>
        /// <returns></returns>
        bool UserAddProtected(string admin, params string[] userName);

        /// <summary>
        /// 删除受保护用户
        /// </summary>
        /// <param name="admin">操作管理员</param>
        /// <param name="userName">用户名</param>
        /// <returns></returns>
        bool UserDeleteProtected(string admin, params string[] userName);

        /// <summary>
        /// 得到受保护用户
        /// </summary>
        /// <returns></returns>
        UcUserProtecteds UserGetProtected();

        /// <summary>
        /// 合并用户
        /// </summary>
        /// <param name="oldUserName">老用户名</param>
        /// <param name="newUserName">新用户名</param>
        /// <param name="uid">Uid</param>
        /// <param name="passWord">密码</param>
        /// <param name="email">Email</param>
        /// <returns></returns>
        UcUserMerge UserMerge(string oldUserName, string newUserName, int uid, string passWord, string email);

        /// <summary>
        /// 移除重名用户记录
        /// </summary>
        /// <param name="userName">用户名</param>
        void UserMergeRemove(string userName);

        /// <summary>
        /// 得到用户积分
        /// </summary>
        /// <param name="appId">应用程序Id</param>
        /// <param name="uid">Uid</param>
        /// <param name="credit">积分编号</param>
        /// <returns></returns>
        int UserGetCredit(int appId, int uid, int credit);

        /// <summary>
        /// 检查新消息
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        UcPmCheckNew PmCheckNew(int uid);

        /// <summary>
        /// 发送短消息
        /// </summary>
        /// <param name="fromUid">发件人用户 ID,0 为系统消息</param>
        /// <param name="replyPmId">回复的消息 ID,0:发送新的短消息,大于 0:回复指定的短消息</param>
        /// <param name="subject">消息标题</param>
        /// <param name="message">消息内容</param>
        /// <param name="msgTo">收件人ID</param>
        /// <returns></returns>
        UcPmSend PmSend(int fromUid, int replyPmId, string subject, string message, params int[] msgTo);

        /// <summary>
        /// 发送短消息
        /// </summary>
        /// <param name="fromUid">发件人用户 ID,0 为系统消息</param>
        /// <param name="replyPmId">回复的消息 ID,0:发送新的短消息,大于 0:回复指定的短消息</param>
        /// <param name="subject">消息标题</param>
        /// <param name="message">消息内容</param>
        /// <param name="msgTo">收件人用户名</param>
        /// <returns></returns>
        UcPmSend PmSend(int fromUid, int replyPmId, string subject, string message, params string[] msgTo);

        /// <summary>
        /// 删除短消息
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="folder">文件夹</param>
        /// <param name="pmIds">短消息ID</param>
        /// <returns>删除的数量</returns>
        int PmDelete(int uid, PmDeleteFolder folder, params int[] pmIds);

        /// <summary>
        /// 删除会话
        /// </summary>
        /// <param name="uid">发件人</param>
        /// <param name="toUids">收件人</param>
        /// <returns>删除的数量</returns>
        int PmDelete(int uid, params int[] toUids);

        /// <summary>
        /// 修改阅读状态
        /// </summary>
        /// <param name="uid">发件人</param>
        /// <param name="toUids">收件人</param>
        /// <param name="pmIds">短消息ID</param>
        /// <param name="readStatus">阅读状态</param>
        void PmReadStatus(int uid, int toUids, int pmIds = 0, ReadStatus readStatus = ReadStatus.Readed);

        /// <summary>
        /// 修改阅读状态
        /// </summary>
        /// <param name="uid">发件人</param>
        /// <param name="toUids">收件人数组</param>
        /// <param name="pmIds">短消息ID数组</param>
        /// <param name="readStatus">阅读状态</param>
        void PmReadStatus(int uid, IEnumerable<int> toUids, IEnumerable<int> pmIds, ReadStatus readStatus = ReadStatus.Readed);

        /// <summary>
        /// 获取短消息列表
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="page">当前页编号,默认值 1</param>
        /// <param name="pageSize">每页最大条目数,默认值 10</param>
        /// <param name="folder">短消息所在的文件夹</param>
        /// <param name="filter">过滤方式</param>
        /// <param name="msgLen">截取短消息内容文字的长度,0 为不截取,默认值 0</param>
        /// <returns></returns>
        UcPmList PmList(int uid, int page = 1, int pageSize = 10, PmReadFolder folder = PmReadFolder.NewBox, PmReadFilter filter = PmReadFilter.NewPm, int msgLen = 0);

        /// <summary>
        /// 获取短消息内容
        /// 本接口函数用于返回指定用户的指定消息 ID 的消息,返回的数据中包含针对这个消息的回复。
        /// 如果指定 touid 参数,那么短消息将列出所有 uid 和 touid 之间的短消息,daterange 可以指定返回消息的日期范围。
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="pmId">短消息ID</param>
        /// <param name="toUid">收件人ID</param>
        /// <param name="dateRange">日期范围</param>
        /// <returns></returns>
        UcPmView PmView(int uid, int pmId, int toUid = 0, DateRange dateRange = DateRange.Today);

        /// <summary>
        /// 获取单条短消息内容
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="type">类型</param>
        /// <param name="pmId">短消息ID</param>
        /// <returns></returns>
        UcPm PmViewNode(int uid, ViewType type = ViewType.Specified, int pmId = 0);

        /// <summary>
        /// 忽略未读消息提示
        /// </summary>
        /// <param name="uid">Uid</param>
        void PmIgnore(int uid);

        /// <summary>
        /// 得到黑名单
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        UcPmBlacklsGet PmBlacklsGet(int uid);

        /// <summary>
        /// 设置黑名单为禁止所有人(清空原数据)
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        bool PmBlacklsSetAll(int uid);

        /// <summary>
        /// 设置黑名单(清空原数据)
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="userName">黑名单用户名</param>
        /// <returns></returns>
        bool PmBlacklsSet(int uid, params string[] userName);

        /// <summary>
        /// 添加黑名单为禁止所有人
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        bool PmBlacklsAddAll(int uid);

        /// <summary>
        /// 增加黑名单
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="userName">黑名单用户名</param>
        /// <returns></returns>
        bool PmBlacklsAdd(int uid, params string[] userName);

        /// <summary>
        /// 删除黑名单中的禁止所有人
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <returns></returns>
        void PmBlacklsDeleteAll(int uid);

        /// <summary>
        /// 删除黑名单
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="userName">黑名单用户名</param>
        void PmBlacklsDelete(int uid, params string[] userName);

        /// <summary>
        /// 增加好友
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="friendId">好友ID</param>
        /// <param name="comment">备注</param>
        /// <returns></returns>
        bool UcFriendAdd(int uid, int friendId, string comment = "");

        /// <summary>
        /// 删除好友
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="friendIds">好友ID</param>
        /// <returns></returns>
        bool UcFriendDelete(int uid, params int[] friendIds);

        /// <summary>
        /// 获取好友总数
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="direction">方向</param>
        /// <returns>好友数目</returns>
        int UcFriendTotalNum(int uid, FriendDirection direction = FriendDirection.All);

        /// <summary>
        /// 好友列表
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="page">当前页编号</param>
        /// <param name="pageSize">每页最大条目数</param>
        /// <param name="totalNum">好友总数</param>
        /// <param name="direction">方向</param>
        /// <returns></returns>
        UcFriends UcFriendList(int uid, int page = 1, int pageSize = 10, int totalNum = 10, FriendDirection direction = FriendDirection.All);

        /// <summary>
        /// 积分兑换请求
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="from">原积分</param>
        /// <param name="to">目标积分</param>
        /// <param name="toAppId">目标应用ID</param>
        /// <param name="amount">积分数额</param>
        /// <returns></returns>
        bool UcCreditExchangeRequest(int uid, int from, int to, int toAppId, int amount);

        ///<summary>
        /// 修改头像
        ///</summary>
        ///<param name="uid">Uid</param>
        ///<param name="type"></param>
        ///<returns></returns>
        string Avatar(int uid, AvatarType type = AvatarType.Virtual);

        /// <summary>
        /// 得到头像地址
        /// </summary>
        /// <param name="uid">Uid</param>
        /// <param name="size">大小</param>
        /// <param name="type">类型</param>
        /// <returns></returns>
        string AvatarUrl(int uid,AvatarSize size,AvatarType type = AvatarType.Virtual);

        /// <summary>
        /// 检查头像是否存在
        /// </summary>
        /// <param name="uid"></param>
        /// <param name="size"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        bool AvatarCheck(int uid, AvatarSize size = AvatarSize.Middle, AvatarType type = AvatarType.Virtual);

        /// <summary>
        /// 获取标签数据
        /// </summary>
        /// <param name="tagName">标签名</param>
        /// <param name="number">应用程序ID对应的数量</param>
        /// <returns></returns>
        UcTags TagGet(string tagName, IEnumerable<KeyValuePair<string, string>> number);

        /// <summary>
        /// 添加事件
        /// </summary>
        /// <param name="icon">图标类型,如:thread、post、video、goods、reward、debate、blog、album、comment、wall、friend</param>
        /// <param name="uid">Uid</param>
        /// <param name="userName">用户名</param>
        /// <param name="titleTemplate">标题模板</param>
        /// <param name="titleData">标题数据数组</param>
        /// <param name="bodyTemplate">内容模板</param>
        /// <param name="bodyData">模板数据</param>
        /// <param name="bodyGeneral">相同事件合并时用到的数据:特定的数组,只有两项:name、link,保留</param>
        /// <param name="targetIds">保留</param>
        /// <param name="images">相关图片的 URL 和链接地址。一个图片地址,一个链接地址</param>
        /// <returns></returns>
        int FeedAdd(FeedIcon icon, int uid, string userName, string titleTemplate, string titleData, string bodyTemplate, string bodyData, string bodyGeneral, string targetIds, params string[] images);

        /// <summary>
        /// 得到Feed
        /// </summary>
        /// <param name="limit">数量限制</param>
        /// <returns></returns>
        UcFeeds FeedGet(int limit);

        /// <summary>
        /// 得到应用列表
        /// </summary>
        /// <returns></returns>
        UcApps AppList();

        /// <summary>
        /// 添加邮件到队列
        /// </summary>
        /// <param name="subject">标题</param>
        /// <param name="message">内容</param>
        /// <param name="uids">Uid</param>
        /// <returns></returns>
        UcMailQueue MailQueue(string subject, string message,params int[] uids);

        /// <summary>
        /// 添加邮件到队列
        /// </summary>
        /// <param name="subject">标题</param>
        /// <param name="message">内容</param>
        /// <param name="emails">目标Email</param>
        /// <returns></returns>
        UcMailQueue MailQueue(string subject, string message, params string[] emails);

        /// <summary>
        /// 添加邮件到队列
        /// </summary>
        /// <param name="subject">标题</param>
        /// <param name="message">内容</param>
        /// <param name="uids">Uid</param>
        /// <param name="emails">目标email</param>
        /// <returns></returns>
        UcMailQueue MailQueue(string subject, string message, int[] uids, string[] emails);

        /// <summary>
        /// 添加邮件到队列
        /// </summary>
        /// <param name="subject">标题</param>
        /// <param name="message">内容</param>
        /// <param name="fromMail">发信人,可选参数,默认为空,uc后台设置的邮件来源作为发信人地址</param>
        /// <param name="charset">邮件字符集,可选参数,默认为gbk</param>
        /// <param name="htmlOn">是否是html格式的邮件,可选参数,默认为FALSE,即文本邮件</param>
        /// <param name="level">邮件级别,可选参数,默认为1,数字大的优先发送,取值为0的时候立即发送,邮件不入队列</param>
        /// <param name="uids">Uid</param>
        /// <param name="emails">目标email</param>
        /// <returns></returns>
        UcMailQueue MailQueue(string subject,string message,string fromMail,string charset,bool htmlOn,int level,int[] uids,string[] emails);
    }
}

这份 API 是根据 UCenter API 开发手册开发的

 

 

所有的API都在里面了,不用考虑实现细节,配置好以后直接调用即可!

 

供 UCenter 调用的接口

这里,我们现在网站下新建一个叫 API 的文件夹(一定要叫 API)

然后再创建一个 ashx 文件(文件名和前面的配置对应即可,上面用的是 uc.ashx ,只要对应即刻,没必要用 uc.php)

 

结构如下:

深入研究 UCenter API_第11张图片

uc.ashx 修改如下:

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DS.Web.UCenter.Api;

namespace DS.Web.UCenter.Website.API
{
    /// <summary>
    /// Summary description for uc
    /// </summary>
    public class uc:UcApiBase
    {
        public override ApiReturn DeleteUser(IEnumerable<int> ids)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn RenameUser(int uid, string oldUserName, string newUserName)
        {
            throw new NotImplementedException();
        }

        public override UcTagReturns GetTag(string tagName)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn SynLogin(int uid)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn SynLogout()
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdatePw(string userName, string passWord)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdateBadWords(UcBadWords badWords)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdateHosts(UcHosts hosts)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdateApps(UcApps apps)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdateClient(UcClientSetting client)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdateCredit(int uid, int credit, int amount)
        {
            throw new NotImplementedException();
        }

        public override UcCreditSettingReturns GetCreditSettings()
        {
            throw new NotImplementedException();
        }

        public override ApiReturn GetCredit(int uid, int credit)
        {
            throw new NotImplementedException();
        }

        public override ApiReturn UpdateCreditSettings(UcCreditSettings creditSettings)
        {
            throw new NotImplementedException();
        }
    }
}

本来呢,ashx 继承的是 IHttpHandler ,但是呢,我们需要修改一下,让它继承 UcApiBase

它是一个抽象类,重写抽象方法即可。

 

但是具体怎么用呢?

这些函数不是给你调用的,是给 UCenter 调用的,你要做的就是写一些逻辑代码。比如 UCenter 告诉你 有人登陆了 (SynLogin函数)

那你应该做点什么呢? 写 Cookie ?写 Session ? 都行~

同样,当 UCenter 同步登出的时候,你也需要写一些逻辑代码,清理 Cookie 或者 Session

另外几个函数是干嘛的呢? 参考 UCenter 接口开发手册中的 API接口

MVC 网站下的用法

前一段时间在 MVC 的网站中使用了自己的 UCenter API

但是出现了一个问题:

MVC 下可以建立静态文件,路由的时候如果存在静态文件则直接访问,包括 aspx, asxh 等文件。

像原来一样,建立了 uc.ashx 文件,但是在使用的时候却出现了一个问题:无法访问 Session

HttpContext 里的 Session 对象是 null

就算继承了 IRequiresSessionStat 接口后还是一样

那如何解决呢?

本来想从底层想办法,但是发现 Controller 差异太大,所以放弃。

后来发现,其实有个很简单的方法,直接在 Controller 里新建该对象即可。

 

namespace MVC.Controllers
{
    public class APIController : Controller
    {
        public ActionResult Uc()
        {
            new UcBase().ProcessRequest(System.Web.HttpContext.Current);
            return Content("");
        }

    }
}


你可能感兴趣的:(深入研究 UCenter API)