.net mvc通过ucenter和 discuz的整合,nopcommerce ucenter 插件的方式实现

discuz无疑是目前市面上最好的论坛之一,对于大多数公司来说,用discuz搭建一个论坛肯定是最节约成本的选择,然而我们的会员想要和discuz进行整合的话,只有两种荀泽,一种直接选用discuz的数据库的用户表作为自己系统的用户表(这种不现实,我如果是mssql,或者是oricle怎么办呢?),第二种就是使用discuz为了解决大家这中需求而提出的ucenter技术,目前小米论坛就是采用的这种技术,下面我就为大家介绍一下.net下使用ucenter的注意细节。

首先引入的第三方已经开发好的ucenter sdk,其原理在其主页文章http://www.dozer.cc/2011/01/ucenter-api-in-depth-1st/

下面我介绍在nopcommerce中使用ucenter的步奏

第一步:你要得到和UCenter进行通信的类库,作者的主页有原始类库http://www.dozer.cc/2011/05/ucenter-api-for-net-on-codeplex/,是asp.net 4.0版的。

第二步,你在你的asp.net项目中引用该类库,引用类库的时候,Browse到dll就行,没有必要把整个项目原代码添加到你的解决方案中。
这个类库有两个重点的文件夹需要注意:DS.Web.UCenter.Api与DS.Web.UCenter.Client,
其中DS.Web.UCenter.Api用于响应由UCenter中心发出的通知消息;
然后DS.Web.UCenter.Client用于本地向UCenter发送消息;

第三步,你需要一个响应通知的页面了,这里为了尊重原作者,我们相应地址的倒数第二层一定要是api,所以我们可以注册一个路由,让该路由的倒数第二层为api

 protected override void RegisterPluginRoutes(RouteCollection routes)
        {
            RegisterPluginRoutesAdmin(routes, "UCenter");

            routes.MapRoute("apiuc",
               "api/uc",
               new { controller = "UCenterOpenApi", action = "OpenApi" },
               new[] { Constants.ControllersNamespace });

        }    

这样子我们就可以通过 http://localhost:305/api/uc 访问到指定控制器下的方法了,

接下来我们就要写UCenterOpenApiController控制器下的OpenApi方法了,代码如下

 public UCenterOpenApiController(UcApiService ucApiService,
            HttpSessionStateBase httpSession,
            UCenterSetting uCenterSetting)
{
this._ucApiService = ucApiService; this._httpSession = httpSession; this._uCenterSetting = uCenterSetting; } public ActionResult OpenApi() { _ucApiService.ProcessRequest(System.Web.HttpContext.Current); return Content(""); }

这段代码的意思就是依赖注入 UcApiService服务,调用UcApiService的PR方法,我想聪明的你一定猜到UcApiService一定是继承了.net中的IHttpHandler接口才能条用PR方法的对吧,

没错,你猜的很正确,下面看一下UcApiService的代码

  public class UcApiService : UcApiBase
    {
        #region Fields
        /// <summary>
        /// 认证服务
        /// </summary>
        private IAuthenticationService _authenticationService;

        //....字段略
    
     #region 同步登陆
        /// <summary>
        /// 同步登陆
        /// </summary>
        /// <param name="uid">uid</param>
        /// <returns>ApiReturn</returns>
        public override ApiReturn SynLogin(int uid)
        {
            //IUcClient client = new UcClient();
            var user = _uclient.UserInfo(uid);
            if (user != null && user.Success)
            {
                Customer customer = GetNopCustomer(user);
                if (customer != null)
                    _authenticationService.SignIn(customer, true);
                else
                {
                    customer = new Customer
                    {
                        CustomerGuid = Guid.NewGuid(),
                        Email = user.Mail,
                        Username = user.UserName,
                        Password = new Random().Next(100000).ToString(),
                        VendorId = 0,
                        Active = true,
                        CreatedOnUtc = DateTime.UtcNow,
                        LastActivityDateUtc = DateTime.UtcNow,
                    };
                    _customerService.InsertCustomer(customer);
                    //角色
                    _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.FirstName, user.UserName);
                    //add to ‘Registered‘ role
                    var registeredRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered);
                    if (registeredRole == null)
                        throw new NopException("‘Registered‘ role could not be loaded");
                    customer.CustomerRoles.Add(registeredRole);

                    //Add reward points for customer registration (if enabled)
                    if (_rewardPointsSettings.Enabled &&
                        _rewardPointsSettings.PointsForRegistration > 0)
                        customer.AddRewardPointsHistoryEntry(_rewardPointsSettings.PointsForRegistration, _localizationService.GetResource("RewardPoints.Message.EarnedForRegistration"));

                    _customerService.UpdateCustomer(customer);
                    _authenticationService.SignIn(customer, true);
                }
                return ApiReturn.Success;
            }
            else
                return ApiReturn.Failed;

        }
        #endregion

   //其他的实现略


}    

可以看出我们的自定义UcApiService继承了 一开始提到的sdk中的UcApiBase,而UcApiBase实现了IHttpHandler和IRequiresSessionState接口,所以在我们的方法中可以直接调用

UcApiService的PR方法。UcApiBase鸡肋中还有好多的方法等待实现,我就不一一列出了,具体可以自己去查看。我们就看这个同步登陆的方法,大体意思就是ucenter下属有其它站点登陆了,我们只要在这个方法中查找的这个用户,并且设置他在本站的状态也为登陆状态,说白了就是授权用户登录本站,只是不需要用户再次的输入账号密码而已。

第四步,上一步是ucenter通知我们有用户从其他站登录了,要求我们这里也登录,那么如果有会员通过我们这个站登录了,我们也是要告诉ucenter的,所以我们这里需要在会员登录成功的情况下,告诉ucenter,这个会员已经成功登录了,你可以让他在其他站也进行登录,具体的实现就是在生成用户凭证的时候调用sdk中IUcClient中的UserLogin方法,如果登录成功,改方法会返回一段js脚本,我们只需要把这段js脚本放到页面上即可。具体代码如下

    /// <summary>
    /// ucenter自定义 登陆认证和 退出认证 实现
    /// </summary>
    public class NopUiUcenterAuthenticationService : INopUiUcenterAuthenticationService
    {
        #region Fields
        /// <summary>
        /// HttpContextBase
        /// </summary>
        private HttpContextBase _httpContext;
        //private readonly ICustomerService _customerService;
        /// <summary>
        /// CustomerSettings
        /// </summary>
        private readonly CustomerSettings _customerSettings;
        /// <summary>
        /// UCenterSetting
        /// </summary>
        private UCenterSetting _uCenterSetting;
        /// <summary>
        /// IUcClient
        /// </summary>
        private IUcClient _uclient;
        /// <summary>
        /// 日志服务
        /// </summary>
        private ILogger _logger;
        // private ICustomerActivityService _customerActivityService; 
        #endregion

        #region Ctor
        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="httpContext">HttpContextBase</param>
        /// <param name="customerSettings">CustomerSettings</param>
        /// <param name="_uCenterSetting">UCenterSetting</param>
        /// <param name="_uclient">IUcClient</param>
        /// <param name="_logger">日志服务</param>
        public NopUiUcenterAuthenticationService(HttpContextBase httpContext,
            // ICustomerService customerService,
            CustomerSettings customerSettings,
            UCenterSetting _uCenterSetting,
            IUcClient _uclient, ILogger _logger
            //  ICustomerActivityService _customerActivityService
            )
        {
            this._httpContext = httpContext;
            // this._customerService = customerService;
            this._customerSettings = customerSettings;
            this._uCenterSetting = _uCenterSetting;
            this._uclient = _uclient;
            this._logger = _logger;
            //this._customerActivityService = _customerActivityService;
        } 
        #endregion

        #region Methods
        /// <summary>
        /// 认证
        /// </summary>
        /// <param name="customer">Customer</param>
        public void SignIn(Customer customer)
        {
            if (!_uCenterSetting.AvailableUcenter || _httpContext.Request.Form["Password"] == null)
                return;
            //同步登陆
            try
            {
                string Password = _httpContext.Request.Form["Password"].ToString().Trim();
                //IUcClient client = new UcClient();
                UcUserLogin user = null;
                if (_customerSettings.UsernamesEnabled)
                    user = _uclient.UserLogin(customer.Username, Password);//登陆
                else
                    user = _uclient.UserLogin(customer.Email, Password, LoginMethod.Mail);//登陆
                if (user != null && user.Success)//判断是否登陆成功
                {
                    switch (user.Result)
                    {
                        case LoginResult.Success:
                            //保存到会话中
                            // _customerActivityService.InsertActivity("uCenter", "客户从本地登陆,并且成功同步到uCenter中", customer);
                            _httpContext.Session[Constants.UcenterLoginSessionKey] = _uclient.UserSynlogin(user.Uid);
                            break;
                        case LoginResult.NotExist:
                            //用户不存在 那么就注册到ucenter吧
                            //_logger
                            var registerResult = _uclient.UserRegister(customer.Username, Password, customer.Email);
                            if (registerResult.Result == RegisterResult.Success)
                            {
                                // _customerActivityService.InsertActivity("uCenter", "客户在本地存在,但是在ucenter中不存在,此处注册此用户到uCenter中成功", customer);
                                _httpContext.Session[Constants.UcenterLoginSessionKey] = _uclient.UserSynlogin(registerResult.Uid);
                            }
                            else
                                _logger.Error(registerResult.Result.ToString() + "---同步登陆到uCenter时异常,描述:本地已经存在的用户但是ucenter中没有,试图向ucenter中注册此用户的时候,注册失败", null, customer);
                            break;
                        case LoginResult.PassWordError:
                            //密码不对,那就把用户中心的密码改成和本商城中一样的呗
                            var s = _uclient.UserEdit(customer.Username, null, Password, null, true);
                            if (s.Result == UserEditResult.Success)
                            {
                                //_customerActivityService.InsertActivity("uCenter", "客户在本地和ucenter中的密码不一样,但是以本商城的密码为主,所以到ucenter中修改此用户的登陆密码,成功", customer);
                                _httpContext.Session[Constants.UcenterLoginSessionKey] = _uclient.UserSynlogin(user.Uid);
                            }
                            break;
                        case LoginResult.QuestionError:
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    _logger.Error("ucteter同步异常,请查看ucenter配置页的配置是否正确", null, customer);
                }
            }
            catch (Exception ex)
            {

                _logger.Error("ucteter同步异常", ex, customer);
            }
        }

        /// <summary>
        /// 取消认证
        /// </summary>
        public void SignOut()
        {
            if (_uCenterSetting.AvailableUcenter)
                _httpContext.Session[Constants.UcenterLoginOutSessionKey] = _uclient.UserSynLogout();
        } 
        #endregion
    }

我们把返回的js脚本方法 会话中,再跳转的时候把这段脚本打到页面上,再删除这段会话即可。

第五步,这步是写配置信息,因为原始类库的配置信息都写到它自己的App.config中去了,但是我这里把作者从配置文件读出配置的方式改成从数据库读取了,配置信息如下图所示

技术分享

 

 

第六步,检查本地服务器和UCenter服务器是否能正常通信

第七步,去UCenter添加此应用,具体怎么填的图片如下:

技术分享

填写完以后到列表页面看到如下所示,则表示通信成功

技术分享

 现在我们就可以测试是否可以正常同步了。 

打开discuz登录一个用户后,再到我们商城中会发现,改用户已经登录了商城,就算该用户在我们商城不存在,我们也会在同步的时候自动注册该用户。

 技术分享技术分享

 

到此 基本上从三美到 discuz 的同步基本完成。

但是,我不知道discuz是故意的还是无意的,这其中一共有三个问题

  1. 我从discuz登录用户,却无法同步 到三美商城。
  2. 从三美商城注册的用户,无法即时同步到discuz,需要在discuz登录一下,以后就可以同步了,及需要激活!
  3. 从discuz注册的用户,无法即时同步 到三美商城。

要想解决这三个问题,都得要从discuz入手,首先解决第一个问题:

  打开discuz安装路径下 \bbs\uc_client\data\cache\apps.php:

技术分享

你会发现只有discuz x 本身应用,所以就不会同步登陆,我也不知道什么原因造成。

我们把三美的应用也加进去即可解决从discuz登录不同步到三美商城的问题

技术分享

接着解决第二个问题:

  第二个问题产生的主要原因是,我们注册的用户,同步登陆的时候,discuz并没有把这个用户添加到他的用户表里面,而是用户激活后才添加到他自己的用户表,找到原因后就很好解决了,

我们只需要在用户同步登陆的时候把用户添加到用户表中即可,

修改文件:/api/uc.php

技术分享
    function synlogin($get, $post) {
        global $_G;

        if(!API_SYNLOGIN) {
            return API_RETURN_FORBIDDEN;
        }

        header(‘P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"‘);

        $cookietime = 31536000;
        $uid = intval($get[‘uid‘]);
        if(($member = getuserbyuid($uid, 1))) {
            dsetcookie(‘auth‘, authcode("$member[password]\t$member[uid]", ‘ENCODE‘), $cookietime);
        }else{
             $query = DB::query("SELECT email FROM ".DB::table(‘ucenter_members‘)." WHERE uid=‘$uid‘");
             if($a = DB::fetch($query)){
                 $email = $a[‘email‘];
             }else{
                 $email = $get[‘email‘];
             }
            $username = $get[‘username‘];  
            $password = md5(time().rand(100000, 999999)); 
            //$email = $get[‘email‘];
            $ip = $_SERVER[‘REMOTE_ADDR‘];
            $time = time();
            $userdata = array(
              ‘uid‘ => $uid, 
              ‘username‘ => $username,
              ‘password‘ => $password,
              ‘email‘ => $email,
              ‘adminid‘ => 0,
              ‘groupid‘ => 10,
              ‘regdate‘ => $time,
              ‘credits‘ => 0, 
              ‘timeoffset‘ => 9999  
            );
            DB::insert(‘common_member‘, $userdata);  
            $status_data = array(
              ‘uid‘ => $uid,
              ‘regip‘ => $ip, 
              ‘lastip‘ => $ip,
              ‘lastvisit‘ => $time,
              ‘lastactivity‘ => $time,
              ‘lastpost‘ => 0, 
              ‘lastsendmail‘ => 0, 
            );
            DB::insert(‘common_member_status‘, $status_data); 
            DB::insert(‘common_member_profile‘, array(‘uid‘ => $uid)); 
            DB::insert(‘common_member_field_forum‘, array(‘uid‘ => $uid));
            DB::insert(‘common_member_field_home‘, array(‘uid‘ => $uid)); 
            DB::insert(‘common_member_count‘, array(‘uid‘ => $uid));
            if(($member = getuserbyuid($uid, 1))) {
                dsetcookie(‘auth‘, authcode("$member[password]\t$member[uid]", ‘ENCODE‘), $cookietime);
            }
        }
    }
View Code

保存上述代码即可解决第二个问题

 

下面解决最后一个问题,这个问题其实本质和第二个很相似,只需要在用户注册的时候调用一下登陆方法即可。

具体参考方法见:http://www.discuz.net/thread-2356743-1-1.html
 
 
至此 就可以实现商城和discuz的用户同步了,本篇文章是基于 nopcommerce3.5和discuz x3.2的同步解决方案。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。