.net mvc經過ucenter和 discuz的整合,nopcommerce ucenter 插件的方式實現

discuz無疑是目前市面上最好的論壇之一,對於大多數公司來講,用discuz搭建一個論壇確定是最節約成本的選擇,然而咱們的會員想要和discuz進行整合的話,只有兩種荀澤,一種直接選用discuz的數據庫的用戶表做爲本身系統的用戶表(這種不現實,我若是是mssql,或者是oricle怎麼辦呢?),第二種就是使用discuz爲了解決你們這中需求而提出的ucenter技術,目前小米論壇就是採用的這種技術,下面我就爲你們介紹一下.net下使用ucenter的注意細節。php

首先引入的第三方已經開發好的ucenter sdk,其原理在其主頁文章http://www.dozer.cc/2011/01/ucenter-api-in-depth-1st/html

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

第一步:你要獲得和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,因此咱們能夠註冊一個路由,讓該路由的倒數第二層爲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 訪問到指定控制器下的方法了,cookie

接下來咱們就要寫UCenterOpenApiController控制器下的OpenApi方法了,代碼以下app

 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方法的對吧,asp.net

沒錯,你猜的很正確,下面看一下UcApiService的代碼dom

  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的同步解決方案。
相關文章
相關標籤/搜索