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是故意的仍是無心的,這其中一共有三個問題
要想解決這三個問題,都得要從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); } } }
保存上述代碼便可解決第二個問題
下面解決最後一個問題,這個問題其實本質和第二個很類似,只須要在用戶註冊的時候調用一下登錄方法便可。