PHP: Exposing web services - Part 1
Web services are useful for a number of things, e.g. we can provide companies with data without needing to give them direct access to our databases.
One of the most important uses (in my opinion) is the interoperability it provides, which allows different platforms, devices etc to interact with each other.
In this post we're going to expose a web service in PHP using NuSphere's Soap library, which has been in development since 2002 - which like the name suggests relies on the SOAP protocol (XML based) and consume it in a C# application using WCF.
First of all you will need to include the library and instantiate a new soap_server object, like this:
include "lib/nusoap.php"; $namespace = "http://www.cstruter.com"; $server = new soap_server(); $server->soap_defencoding = 'UTF-8'; $server->configureWSDL("TestService", $namespace); $server->wsdl->schemaTargetNamespace = $namespace;
It's quite important to set our soap_defencoding property to UTF-8, since this is the default encoding required by the WCF (Windows Communication Foundation)
We also need to register the functions we wish to expose against our soap_server object, like so:
$server->register('test', // function name array("name"=>"xsd:string"), // parameters array('return'=>'xsd:string'), // return value type $namespace); function test($name) { return "Hello $name"; }
Once we're done, we tell our object to handle actions sent to our script like this:
$POST_DATA = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : @file_get_contents("php://input"); $server->service($POST_DATA);
On the C# side (using Visual Studio 2008/10), right click on your project and click on Add Service Reference.
Notice the querystring "?wsdl" circled in the image above, passed along with the url to the reference, this tells the NuSoap library to output WSDL (Web Services Description Language) - which provides C# with all the information it needs to consume the web service (something I will discuss in the next part of this post).
Using the web service in C# looks something like this:
using System; using cstruter.com.Service; // Namespace defined in the Add Service Reference dialog namespace cstruter.com { class Program { static void Main(string[] args) { TestServicePortTypeClient TestService = new TestServicePortTypeClient(); String value = TestService.test("Julie"); Console.WriteLine(value); // Outputs Hello Julie } } }
Now thats pretty much the basics to get us up and running, but what If we want to do a bit more? Like passing/returning objects from/to the web service, we need to define types, like this:
$server->wsdl->addComplexType( 'post', // Name of the object 'complexType', // Object Type (Why? Since we've got an addSimpleType method?) 'struct', // Struct /Array (Multiple objects) 'all', // Composition '', // Restriction Namespace array( 'postID' => array('name' => 'postID', 'type' => 'xsd:int'), 'Title' => array('name' => 'Title', 'type' => 'xsd:string'), 'Body' => array('Body' => 'Body', 'type' => 'xsd:string') ) // Structure Defintion );
Using the new defined object is very straightforward e.g. "tns:post":
$server->register('test2', array("name"=>"tns:post"), array('return'=>'xsd:string'), $namespace); function test2($value) { return print_r($value, true); }
On the C# side of things you will notice an auto-generated version of the object (post) we defined in our PHP script becomes available:
TestServicePortTypeClient TestService = new TestServicePortTypeClient(); post p = new post { postID = 1, Title = "Test 1 2 3", Body = "Some test" }; String value = TestService.test2(p); Console.WriteLine(value);
If we wish to pass an array of objects e.g. post[], we need to add another complexType.
$server->wsdl->addComplexType( 'posts', // Name of the object 'complexType', 'array', // Array since we're passing a set of objects '', 'SOAP-ENC:Array', array(), array( array('ref' => 'SOAP-ENC:arrayType', 'wsdl:arrayType' => 'tns:post[]') ), 'tns:post' // Child object );
In the next snippet we pass an array of posts to the web service:
$server->register('test3', array(), // Blank array if we don't have any parameters. array('return'=>'tns:posts'), // The array of objects. $namespace); function test3() { return array(array('postID'=>1, 'Title'=>'test 1', 'Body'=>'abc abc abc'), array('postID'=>2, 'Title'=>'test 2', 'Body'=>'123 123 123') ); }
And this is how we retrieve the array via C#:
TestServicePortTypeClient TestService = new TestServicePortTypeClient(); post[] ps = TestService.test3(); foreach (post p in ps) { Console.WriteLine(String.Concat(p.postID, ":", p.Title, ":", p.Body)); }
In conclusion I feel its quite a nice, simple to use library written by the guys at NuSphere. There is quite a bit more to this library, but these examples should get you on your way.
In the next part of this post we're going to have a look at the SoapServer php extension.
Posted by - Christoff Truter
Date - 2010-05-06 22:30:42
Comments - 2
Date - 2010-05-06 22:30:42
Comments - 2
C# : Windows Impersonation
The WindowsImpersonationContext class provides us with the ability to impersonate an user.
In the following post we're going to look at how to write to a protected shared folder using impersonation.
You will notice that the WindowsImpersonationContext class doesn't have a constructor, nor any static methods defined - one can however get an instance of this class via the Impersonate static method in the WindowsIdentity class, observe:
using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token)) { // Some operation requiring impersonation }
Note, the WindowsImpersonationContext class implements the IDisposable interface which enables us to use the using clause - code out of scope (running outside) the using clause won't be executed within context of the impersonated user.
In order for our impersonation to work, we need to pass a primary token to our method, to retrieve the token, we'll need to import a method from the "Advanced Services" assembly (advapi32.dll) like this:
[DllImport("advapi32.dll", SetLastError = true)] public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
And an import to dispose of the token.
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr token);
You might have noticed the LogonType and LogonProvider parameters in the LogonUser method, we need to tell the API by which means authentication must happen - to simplify this, I created two enum's:
enum LogonType { Interactive = 2, Network = 3, Batch = 4, Service = 5, Unlock = 7, NetworkClearText = 8, NewCredentials = 9 } enum LogonProvider { Default = 0, WinNT35 = 1, WinNT40 = 2, WinNT50 = 3 }
I am not going to go into too much depth about these providers, but lets have a quick look at the Interactive and NewCredentials LogonTypes.
Using the Interactive LogonType will look something like this:
IntPtr token = IntPtr.Zero; bool valid = LogonUser("username", "yourdomain.com", "password", (int)LogonType.Interactive, (int)LogonProvider.Default, ref token); if (valid) { using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token)) { CloseHandle(token); File.WriteAllBytes(@"\\yourserver\someshare\test.txt", new byte[] { }); } }
Now this is all nice and all when we're impersonating someone thats on the same domain as us, but what about impersonating outside our domain?
This is where I found the NewCredentials LogonType useful, since it authenticates cross domain:
IntPtr token = IntPtr.Zero; LogonUser("username", "yourdomain.com", "password", (int)LogonType.NewCredentials, (int)LogonProvider.WinNT50, ref token); using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token)) { CloseHandle(token); File.WriteAllBytes(@"\\yourserver\someshare\test.txt", new byte[] { }); }
I didnt include the valid bool in the preceding snippet, this is because you'll find that the LogonUser method seems to always return true when using the NewCredentials LogonType since authentication only happens when you're accessing the resource, in this case the File.WriteAllBytes method.
Posted by - Christoff Truter
Date - 2010-05-04 17:23:11
Comments - 7
Date - 2010-05-04 17:23:11
Comments - 7
First 26 27 28 29 30 31 32 33 34 35 Last / 62 Pages (124 Entries)