利用WebService集成DNN和cnforums
利用WebService集成DNN和cnforums是个好主意,基本上是将论坛的登陆,登出,增删改用户用WebService包装,在DNN的相应操作后调用这些Webservice。国外有一些代码如下,希望国人加以改进验证(http://www.communityserver.org/ShowPost.aspx?PostID=460456):
We were able to able to integrate one of our sites login (with have about 3 different entry points) with the forums login. It pretty easy to setup and nothing really needs to be done to either site cookies. The primary forums site code does not need to be touched except for one setting in web.config (allow ws gets) and also a new webservice file. Also both apps cookies are seperate and not dependant on each other. So I think this should should also scale across for any other application as well. Couple quick notes before I post the code:
-we wrote this web service logic today and a lot of it we just created webservice versions of some of the existing procedures (good job guys, we left your comments in too
-this is one way login from our site login to the forums, I believe we'll just need to write a web service control to access our main site logins from the forums app in similiar fashion (or in your case DNN). in theory it should extremely similiar.
-we have a local crypto library that we use to encrypt some of the data before sending/receiving etc so I'll remove that for simplicity sakes. some other security checks are removed as well so I hope the logic still works and I didn't miss anything - I apologize if I did.
ok, here's what we did:
1. created a new web service (asmx page) in the AspNetForums project (ie: root) called ws_utilities.asmx (code below), copied the code below and recompiled the AspNetForums project. users can login, create new account, add roles, remove roles etc via web service...
2. in one of our main site logins after site successful login we access the ws_utilities.asmx using System.Net.HttpWebRequest (may change later)
3. web.config allow HTTP GET setting (AspNetForums web.config - use get for now, will probably change later)
1. ws_utilities.asmx code-behind:
using System;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using AspNetForums;
using AspNetForums.Components;
using AspNetForums.Enumerations;
using System.Web.Security;
namespace AspNetForums
{
/// <summary>
/// Summary description for WS_Utilities.
/// </summary>
[WebService(Namespace="http://www.mysite.org/webservices/")]
public class WS_Utilities : System.Web.Services.WebService
{
AccountActivation activation = Globals.GetSiteSettings().AccountActivation;
string output = "";
public WS_Utilities()
{
//CODEGEN: This call is required by the ASP.NET Web Services Designer
InitializeComponent();
}
#region Component Designer generated code
//Required by the Web Services Designer
private IContainer components = null;
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if(disposing && components != null)
{
components.Dispose();
}
base.Dispose(disposing);
}
#endregion
[WebMethod]
public string CreateAccount(string username, string password, string emailAddress) {
CreateUserStatus status = CreateUserStatus.UnknownFailure;
// try to create the new user account
User user = new User();
user.Username = username;
user.Email = emailAddress;
// What account activation mode are we in?
switch (activation)
{
case (AccountActivation.AdminApproval):
user.Password = String.Empty;
break;
case (AccountActivation.Email):
user.Password = String.Empty;
break;
case (AccountActivation.Automatic):
user.Password = password.Trim();
break;
default:
user.Password = String.Empty;
break;
}
// Does the user require approval?
if (activation == AccountActivation.AdminApproval)
user.AccountStatus = UserAccountStatus.ApprovalPending;
else
user.AccountStatus = UserAccountStatus.Approved;
// Set the Anonymous flag to false
user.IsAnonymous = false;
// Attempt to create the user
if (user.Username == "Anonymous")
{
status = CreateUserStatus.DuplicateUsername;
}
else
{
// EAD TODO 6/26/2004: We need to move this to the Admin panel to
//make the admin
// decide to send an email or not.
// For now, we're changing to "true" send email to everyone for any
// reason. Including new users with auto-activation. This logic is
// handled in the Users.Create() method.
status = Users.Create(user, true);
}
// Determine if the account was created successfully
switch (status)
{
// Username already exists!
case CreateUserStatus.DuplicateUsername:
output = AspNetForums.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_DuplicateUsername");
break;
// Email already exists!
case CreateUserStatus.DuplicateEmailAddress:
output = AspNetForums.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_DuplicateEmailAddress");
break;
// Unknown failure has occurred!
case CreateUserStatus.UnknownFailure:
output = AspNetForums.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_UnknownFailure");
break;
// Username is disallowed
case CreateUserStatus.DisallowedUsername:
output = AspNetForums.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_DisallowedUsername");
break;
// Everything went off fine, good
case CreateUserStatus.Created:
output = AspNetForums.Components.ResourceManager.GetString("CreateNewAccount_CreateUserStatus_Created");
break;
}
//return result
return output;
}
[WebMethod]
public string AddUserToRole(string userID, string roleID)
{
try
{
ForumsDataProvider dp = ForumsDataProvider.Instance();
dp.AddUserToRole(Convert.ToInt32(userID), Convert.ToInt32(roleID));
output = "success";
}
catch
{
output = "failed";
}
//return result
return output;
}
[WebMethod]
public string RemoveUserFromRole(string userID, string roleID)
{
try
{
ForumsDataProvider dp = ForumsDataProvider.Instance();
dp.RemoveUserFromRole(Convert.ToInt32(userID), Convert.ToInt32(roleID));
output = "success";
}
catch
{
output = "failed";
}
//return result
return output;
}
[WebMethod]
public string LoginUser(string username, string password, string persist)
{
try
{
User userToLogin = new User();
userToLogin.Username = username;
userToLogin.Password = password;
LoginUserStatus loginStatus = Users.ValidUser(userToLogin);
if( loginStatus == LoginUserStatus.Success )
{
//set message
output = "success";
// Are we allowing login?
// TODO -- this could be better optimized
if (!Globals.GetSiteSettings().AllowLogin)
{
bool allowed = false;
int userid = Users.FindUserByUsername( userToLogin.Username ).UserID;
ArrayList roles = Roles.GetRoles(userid);
foreach (Role role in roles)
{
if (role.Name == "Site Administrators" || role.Name == "Global Administrators")
{
allowed = true;
break;
}
}
// Check the user is in the administrator role
if (!allowed)
{
ForumException error = new ForumException(ForumExceptionType.UserLoginDisabled);
output = error.Message;
}
}
FormsAuthentication.SetAuthCookie(userToLogin.Username, Convert.ToBoolean(persist));
HttpCookie c = FormsAuthentication.GetAuthCookie(userToLogin.Username, Convert.ToBoolean(persist));
//modify any cookies settings as needed
HttpContext.Current.Response.AppendCookie(c);
}
else if(loginStatus == LoginUserStatus.InvalidCredentials)
{ // Invalid Credentials
ForumException error = new ForumException(ForumExceptionType.UserInvalidCredentials, userToLogin.Username);
output = error.Message;
}
else if(loginStatus == LoginUserStatus.AccountPending)
{ // Account not approved yet
ForumException error = new ForumException(ForumExceptionType.UserAccountPending);
output = error.Message;
}
else if(loginStatus == LoginUserStatus.AccountBanned)
{ // Account banned
ForumException error = new ForumException(ForumExceptionType.UserAccountBanned, userToLogin.Username);
output = error.Message;
}
else if(loginStatus == LoginUserStatus.AccountDisapproved)
{ // Account disapproved
ForumException error = new ForumException(ForumExceptionType.UserAccountDisapproved, userToLogin.Username);
output = error.Message;
}
else if(loginStatus == LoginUserStatus.UnknownError)
{ // Unknown error because of miss-syncronization of internal data
ForumException error = new ForumException(ForumExceptionType.UserUnknownLoginError);
output = error.Message;
}
}
else
output = "invalid security token";
}
catch(Exception ex)
{
output = ex.Message);
}
//return result
return output;
}
[WebMethod]
public string LogoutUser(string ws_key)
{
try
{
// log the user out
FormsAuthentication.SignOut();
// Nuke the roles cookie
Roles.SignOut();
//set message
output = "success";
}
catch
{
output = "failed";
}
//return result
return output;
}
}
}
2. main site login form code-behind, onSubmit event (removed unimportant logic and this ddefinately should be re-written but it was a quick first draft) - variation of below would go into DNN login onClick_Submit (or whatever it's called)...
public void Login_Click(Object sender, System.EventArgs e)
{
//bla blah blah user has been authenticated through main site
//
//go to next phase and check aspnetforums
//
//bla blah blah
//this logic can be written better, it was a quick test to see if it worked or not
//output container
string targetOutput = "";
//create target link
String targetURL = "http://www.mysite.org/forums/ws_utilities.asmx/LoginUser?username=" + userID.Text + "&password=" + password.Text + "&persist=true");//or persist=false if remember checkbox
//create request object, settings...
System.Net.HttpWebRequest objRequest = (HttpWebRequest) System.Net.HttpWebRequest.Create(targetURL);
objRequest.ContentType = "application/x-www-form-urlencoded";
//create cookie container to hold response cookies
CookieContainer myContainer = new CookieContainer();
objRequest.CookieContainer = myContainer;
//get response and read stream
HttpWebResponse objResponse = (HttpWebResponse) objRequest.GetResponse();
using (StreamReader sr = new StreamReader(objResponse.GetResponseStream()) )
{
targetOutput = sr.ReadToEnd();
sr.Close();
}
//need some type of check to return if AspNetForums login failure - quick hack check probably should have response returned as xml
if(targetOutput.IndexOf("success",0) > -1){
//get response cookies
objResponse.Cookies = objRequest.CookieContainer.GetCookies(objRequest.RequestUri);
//iterate all response cookie and store local copies
// this will store AspNetForums auth cookies
foreach (Cookie cook in objResponse.Cookies)
{
HttpCookie cookie = new HttpCookie(cook.Name);
cookie.Name = cook.Name;
cookie.Value = cook.Value;
HttpContext.Current.Response.AppendCookie(cookie);
}
//get redirect url
string returnUrl = (Request.QueryString["ReturnUrl"]);
//redirect
if(returnUrl!=null)
Response.Redirect(globalFunctions.releaseSSLRef(returnUrl), false);
}
else{
//login failed on AspNetForums, let user know
}
}
3. web.config allow HTTP GET setting
add in <system.web>, using HTTP GET for now, can use whatever method prefered later
<webServices>
<protocols>
<add name="HttpSoap"/>
<add name="HttpPost"/>
<add name="HttpGet"/>
<add name="Documentation"/>
<add name="HttpPostLocalhost"/>
</protocols>
</webServices>
==============
just tested this in DNN and it does works. username and password info should map out one-to-one in DNN and DotNetForums to work correctly (using only logic below unless you access CreateAccount ws in DNN registration). it's late and I haven't accessed the CreateAccount and any role ws methods from DNN yet but they work in another app. here's the DNN route I'll probably take next...
-use the DNN registration and hit the webservice CreateUser method from DNN similiar to LoginUser access below
-in forums point registrations to DNN registration (SiteUrls.config - haven't tested), so user cannot register through forums
-in forums point logins to DNN login (SiteUrls.config - haven't tested)
-special access and roles would still be accessed from forums admin but could be added through ws as well
that should be it in a nutshell... nothing too crazy...
below is the DNN logic (replace #2 from orig post with this) I placed in /admin/security/signin.ascx.vb file in the cmdLogin_Click event right before any of the redirects - I'll post the register logic when I get it done as well but gotta grabs some sleep now it's after 2 EST. you may also want to hike up security a little but this more or less shows one easy alternative integrating without ripping out a lot of code...
'create output string
Dim targetOutput As String = ""
'create ws info
Dim postData as String = "?username=" & txtUsername.Text & "&password=" & txtPassword.Text & "&persist=true"
//replace target with your url
Dim targetURL as String =
"http://192.168.1.101/forums/ws_utilities.asmx/LoginUser" & postData
'set request object and contenttype
Dim objRequest As HttpWebRequest = CType(objRequest.Create(targetURL), HttpWebRequest)
objRequest.ContentType = "application/x-www-form-urlencoded"
'set cookie container
Dim myContainer As New CookieContainer()
objRequest.CookieContainer = myContainer
'get output and stream
Dim objResponse As HttpWebResponse = CType(objRequest.GetResponse(), HttpWebResponse)
Dim sr As New StreamReader(objResponse.GetResponseStream())
targetOutput = sr.ReadToEnd()
sr.Close()
'iterate response cookie and store locally
objResponse.Cookies = objRequest.CookieContainer.GetCookies(objRequest.RequestUri)
Dim cook As Cookie
For Each cook In objResponse.Cookies
Dim cookie As New HttpCookie(cook.Name)
cookie.Name = cook.Name
cookie.Value = cook.Value
HttpContext.Current.Response.AppendCookie(cookie)
Next cook
======================
loubrou - that definately can be added as needed in the DNN logic (see below). On the ws end, a AspNetForums response string is returned, ie: "invalid login", "success", etc. A check could easily be added to DNN if the forums login went wrong - a Select/Case or If/Then on the returned string would be easy enough. I wanted to keep the concept as basic as possible for these posts and provide the least amount of working logic - we're also using encryption logic that I removed as well... if the accounts are one-to-one in DNN and AspNetForums the login should fail first on the DNN side and not even make it to the AspNetForums ws call. In order to get the accounts one-to-one, this could be done easily by iterating through existing DNN accounts and calling CreateAccount ws method (see next post).
'create output string
Dim targetOutput As String = ""
'create ws info
Dim postData as String = "?username=" & txtUsername.Text & "&password=" & txtPassword.Text & "&persist=true"
//replace target with your url
Dim targetURL as String =
"http://192.168.1.101/forums/ws_utilities.asmx/LoginUser" & postData
'try forums login
Try
'set request object and contenttype
Dim objRequest As HttpWebRequest = CType(objRequest.Create(targetURL), HttpWebRequest)
objRequest.ContentType = "application/x-www-form-urlencoded"
'set cookie container
Dim myContainer As New CookieContainer()
objRequest.CookieContainer = myContainer
'get output and stream
Dim objResponse As HttpWebResponse = CType(objRequest.GetResponse(), HttpWebResponse)
Dim sr As New StreamReader(objResponse.GetResponseStream())
targetOutput = sr.ReadToEnd()
sr.Close()
'iterate response cookie and store locally
objResponse.Cookies = objRequest.CookieContainer.GetCookies(objRequest.RequestUri)
Dim cook As Cookie
For Each cook In objResponse.Cookies
Dim cookie As New HttpCookie(cook.Name)
cookie.Name = cook.Name
cookie.Value = cook.Value
HttpContext.Current.Response.AppendCookie(cookie)
Next cook
Catch exc As Exception 'catch error, report to DNN
ProcessModuleLoadException(Me, exc)
End Try
'display output test - uncomment to display ws response string
'HttpContext.Current.Response.Write("output response: " & HttpContext.Current.Server.HtmlEncode(targetOutput))
'HttpContext.Current.Response.End()
======================
here's the forums account creation logic located in /admin/users/Register.ascx.vb
since this is a variation of the AspNetForums logic, the ws output will return "success", "failure", "account exists", "email address in use", etc messages (these are not exact messages, I don't recall exact wording off the top of my head). upon creation of account forums email is also sent to user.
make sure you reference the following libraries:
Imports System.Net
Imports System.Text
Imports System.IO
and then after DNN has created account in RegisterBtn_Click event:
'DNN add account logic
Dim UserId As Integer = objUser.AddUser(PortalId, txtFirstName.Text, txtLastName.Text, Address1.Unit, Address1.Street, Address1.City, Address1.Region, Address1.Postal, Address1.Country, Address1.Telephone, txtEmail.Text, txtUsername.Text, objSecurity.Encrypt(Convert.ToString(_portalSettings.HostSettings("EncryptionKey")), txtPassword.Text), Convert.ToBoolean(IIf(_portalSettings.UserRegistration = 1, False, True)), AffiliateId)
'DNN check if account created
If UserId >= 0 Then
'START CREATE ACCT HERE
'create output string
Dim targetOutput As String = ""
'create ws post info
Dim postData as String = "?username=" & txtUsername.Text & "&password=" & txtPassword.Text & "&emailAddress=" & txtEmail.Text
'create target url - change to your url or domain
Dim targetURL as String = "http://192.168.1.101/forums/ws_utilities.asmx/CreateAccount" & postData
'try to create account
Try
'set request object and contenttype
Dim objRequest As HttpWebRequest = CType(objRequest.Create(targetURL), HttpWebRequest)
objRequest.ContentType = "application/x-www-form-urlencoded"
'set cookie container
Dim myContainer As New CookieContainer()
objRequest.CookieContainer = myContainer
'get output and stream
Dim objResponse As HttpWebResponse = CType(objRequest.GetResponse(), HttpWebResponse)
Dim sr As New StreamReader(objResponse.GetResponseStream())
targetOutput = sr.ReadToEnd()
sr.Close()
'iterate response cookie and store locally - for CreateAccount I don't believe this is needed since auth cookie not written
objResponse.Cookies = objRequest.CookieContainer.GetCookies(objRequest.RequestUri)
Dim cook As Cookie
For Each cook In objResponse.Cookies
Dim cookie As New HttpCookie(cook.Name)
cookie.Name = cook.Name
cookie.Value = cook.Value
HttpContext.Current.Response.AppendCookie(cookie)
Next cook
Catch exc As Exception 'catch error report to DNN
ProcessModuleLoadException(Me, exc)
End Try
'display output test - uncomment to display ws response string
'HttpContext.Current.Response.Write("output response: " & HttpContext.Current.Server.HtmlEncode(targetOutput))
'HttpContext.Current.Response.End()
'END CREATE ACCT HERE
==================
and logoff located in /admin/security/logoff.aspx.vb before DNN redirect in Page_load event...
'create output string
Dim targetOutput As String = ""
'create target url - change to your url or domain
Dim targetURL as String = "http://192.168.1.101/forums/ws_utilities.asmx/LogoutUser"
'try to logout
Try
'set request object and contenttype
Dim objRequest As HttpWebRequest = CType(objRequest.Create(targetURL), HttpWebRequest)
objRequest.ContentType = "application/x-www-form-urlencoded"
'set cookie container
Dim myContainer As New CookieContainer()
objRequest.CookieContainer = myContainer
'get output and stream
Dim objResponse As HttpWebResponse = CType(objRequest.GetResponse(), HttpWebResponse)
Dim sr As New StreamReader(objResponse.GetResponseStream())
targetOutput = sr.ReadToEnd()
sr.Close()
Catch exc As Exception 'catch error report to DNN
ProcessModuleLoadException(Me, exc)
End Try
'display output test - uncomment to display ws response string
'HttpContext.Current.Response.Write("output response: " & HttpContext.Current.Server.HtmlEncode(targetOutput))
'HttpContext.Current.Response.End()
浙公网安备 33010602011771号