Fasil Hayat's blog

...C#, .Net, Sharepoint, BizTalk, JQuery...

Først skal IIS'en, der hoster Sharepoint 2010 sitet konfigureres for at WCF Services kommer til at fungere.

- Åbn IIS'en
- Find Sharepoint sitet og gå ind under 'Authentication'
- Sæt 'Anonymous Authentication' til disabled
- Sæt 'Basic Authentication' til enabled (hvis man sidder på en udviklerboks. Skal ellers være disabled).

Herefter laver man en Service reference til fx. 'http://MinBoks/sites/MitSite/_vti_bin/ListData.svc' i VS2010.
Man vil herefter få adgang til 'MitSiteDataContext', som skal benyttes for at få adgang til alle SP lister, der findes under 'MitSite'.

Man kan gøre brug af LINQ for at tilgå sharepoint 2010 lister på 'MitSite'. Inden da skal kontekst objekt sættes op. og Credentials skal sættes op. Bemærk kun credentials til en bruger, der har læse rettigheder til en SP 2010 liste, kan afvikle nedenstående kode.

   1:  var host = new Uri("http://MinBoks/sites/MitSite");
   2:  var kontekst = new MitSiteDataContext(new Uri
   3:              (string.Format("{0}/_vti_bin/ListData.svc", host)));
   4:  kontekst.Credentials = CredentialCache.DefaultCredentials;


LINQ udtryk der henter alle SPListItems fra 'MinSPListe' og danner en List<Artikel>:

   1:  var artikler = kontekst.MinSPListe.Where(
   2:                  p => p.Name == "John Doe").Select(p => new Artikel()
   3:                  {
   4:                      Title = p.Title,
   5:                      Id = p.Id,
   6:                      Kode = p.kode
   7:                  }).ToList();


Ved indhentning af et specifikt SPListItem:

   1:  var artikel = kontekst.MinSPListe.Where(
   2:                  p => p.Id == 5).Select(p => new Artikel()
   3:                  {
   4:                      Title = p.Title,
   5:                      Id = p.Id,
   6:                      Kode = p.kode,
   7:                      Name = p.Name,
   8:                      Path = p.Path
   9:                  }).FirstOrDefault();

 



SOAP beskeder består af et 'MessageBody' og et 'MessageHeader' segment, hvor man typisk i 'MessageBody' segmentet udveksler forretningsdata, kan man benytte sig af SOAP header til udveksling af sekundære data. Det kan være data, der altid skal sendes med over via SOAP headeren fx. den kaldende parts autorisations data.

Her er et simpelt eksempel på, hvordan man kan berige data udvekslingen med en SOAP header fra en klient, som bagefter kan udvindes hos service udbyderen.

Kontrakter:

   1:  namespace MitNamespace.Foo {
   2:      public class MinMessageHeader{
   3:          public string BrugerId { get; set; }
   4:          public string ApplikationId { get; set; }
   5:      }
   6:  }
   7:   
   8:  namespace MitNamespace.Foo {
   9:      public class HentEtEllerAndetRequest {
  10:          public MessageHeader<MinMessageHeader> Header { get; set; }
  11:          public string EnForretningsId { get; set; }
  12:      }
  13:  }


Klient kode:

   1:  var klient = new MinProxy.MinServiceClient();
   2:   
   3:  using (var scope = new OperationContextScope(klient.InnerChannel)) {
   4:      var req = new HentEtEllerAndetRequest();
   5:      req.EnforretningsId = "25";
   6:      req.Header = new MessageHeader<MinMessageHeader>(new MinMessageHeader {
   7:                      BrugerId = "FHA", 
   8:                      ApplikationId = "NavnPaaApplikation", 
   9:                  });
  10:      var messageHeader = req.Header;
  11:      var header = messageHeader.GetUntypedHeader("MinCustomHeader", 
  12:                                          "http://www.fasil.dk/Applikation");
  13:      OperationContext.Current.OutgoingMessageHeaders.Add(header);
  14:      var result = klient.HentNogleData(req);
  15:  }


Serverkode:

   1:  [OperationContract]
   2:  public HentNogleDataResponse HentNogleData(HentEtEllerAndetRequest request) {
   3:      MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
   4:      var headerData = headers.GetHeader<MinMessageHeader>("MinCustomHeader", 
   5:                                          "http://www.fasil.dk/Applikation");
   6:      ...
   7:      ...
   8:      ...
   9:  }


Man kan også benytte sig af data kontrakter istedet. Dette eksempel virker dog helt ubeklageligt. 
Klient koden kan naturligvis bestå af en 'abstract' klasse, hvor header oplysningerne sættes. Alle andre service kaldende klasser skal herefter implementere / nedarve fra denne 'abstract' klasse.

På server delen kan man naturligvis tjekke for headerdelen, inden et response sendes tilbage til klienten. Alternativt sendes en autentificerings- / autorisationsfejl tilbage til klienten, hvis header oplysningerne ikke er gyldige.



Der er ikke mange, der er klar over man sagtens kan få en WCF service til at respondere i JSON formatet. Dette har jeg typisk brugt i AJAX sammenhænge, hvor man kan undlade at bruge en *.aspx side, og i stedet benytte sig af en WCF service på UI laget, der asynkront kan kaldes fra fx. JQuery. Denne WCF service er udelukkende designet til at understøtte asynkrone kald. De metoder man fx. fra JQuery kalder består af nedenstående kontrakt definitioner.
Navnet på WCF service er i dette eksempel 'Service.svc'.

   1:  [ServiceContract(Namespace = "SilverAria.SAM.Service")]
   2:  [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
   3:  [AspNetCompatibilityRequirements(RequirementsMode=
   4:  AspNetCompatibilityRequirementsMode.Allowed)]
   5:  public class Service {
   6:    [OperationContract]
   7:    [WebGet(ResponseFormat=WebMessageFormat.Json, BodyStyle=
   8:  WebMessageBodyStyle.Bare)]
   9:    public Role GetConfiguredSkills(int roleId) {          
  10:       var control = new RenwarRoleConfigurationControl();
  11:       var role = control.PopulateConfiguredSkills(roleId);
  12:       var returnObj = new Role {
  13:           ConfiguredSkills = role.ConfiguredSkills,
  14:           UnconfiguredSkills = role.UnconfiguredSkills
  15:       };
  16:       return returnObj;
  17:    }
  18:  }


I JQuery vil kaldet af metoden se ud på følgende måde:

   1:  $(document).ready(function() {
   2:      $("#roleId").change(function() {
   3:          $("#requiredSkills").empty();
   4:          $("#skills").empty();
   5:          var idValue = $("#roleId").val();
   6:          $.getJSON("Service.svc/GetConfiguredSkills", { roleId: idValue },
   7:          function(data) {
   8:              $.each(data.ConfiguredSkills, function(i, 
   9:                  ConfiguredSkills) {
  10:                  $("<option>" + ConfiguredSkills.Name + ", Lvl " +
  11:                  ConfiguredSkills.Level + "</option>").attr("value",
  12:                  ConfiguredSkills.Id + "#" +
  13:                  ConfiguredSkills.Level).appendTo("#requiredSkills");
  14:              });
  15:              $.each(data.UnconfiguredSkills, 
  16:                function(a, UnconfiguredSkills) {
  17:                  $("<option>" + UnconfiguredSkills.Name + 
  18:                  "</option>").attr("value",
  19:                  UnconfiguredSkills.Id).appendTo("#skills");
  20:              });
  21:          });
  22:          return false;
  23:      });
  24:  });


WCF kaldet leverer data i JSON format, som man så ved hjælp af JQuery 'pakker' ud, og beriger html kontrollerne med. Meget mere elegant løsning istedet for at have en 'dummy' *.aspx side, der reagerer på querystring værdier.



For at imødekomme løs kobling mellem service klient og service host, er der mulighed for at introducere et yderligere abstraktionslag mellem parterne. Dette abstraktionslag udgør en ny facade ud mod de service konsumerende klienter. Microsoft's Managed Services Engine (MSE) bygger ovenpå WCF, og sørger for virtualisering og versionering af services. MSE imødekommer problemstillingen med 'dll hell' på service niveau, da MSE sørger for, operationer på services får versions numre, så fremtidige versioner af samme service kan eksistere side-om-side med de tidligere versioner. Dette forebygger klienter, der konsumerer ældre services ikke falder om som små domino brikker på grund af en opdatering.

Ydermere sørger MSE for at virtualisere WCF services. Dette betyder, at WCF services fysisk kan køre på helt andre maskiner, hvor MSE abstraherer disse services og udstiller dem på ny. Disse oplysninger gemmer MSE i 'MSE_RESPOSITORY' databasen på en SQL Server (2005 el. 2008). MSE er faktisk uafhængig af teknologi, så længe der findes en wsdl, så kan MSE versionere og virtualisere denne service. MSE er yderligere kompatibelt med diverse service bindings (NetTCP, WsHttp, BasicHttp) samt SOAP versioner, og der findes mange konfigurations muligheder.

Eksempel på virtualisering af en WCF service:
MSE Model Viewer startes op (default view er 'End to End Item Management').
Vælg tools -> Load Resources -> Web Service Metadata. Herefter starter en lille wizard. Lokationen på wsdl fil angives enten som enkeltstående fil eller som en del af service referene -> Next -> Herefter vælges 'Virtualize Service Operations...' -> Next -> Next -> Next -> Close. Herefter vil man kunne se operationer på den virtualiserede service under operations, og man vil ligeledes finde service specifikke egenskaber under 'Rescources', 'Instances' og 'Systems'. Under 'Configuration' / 'bindings' vil man kunne se den importerede binding.

MSE Model Viewer

Nu da en service er registreret i MSE, skal der defineres et 'Endpoint'. Dette gøres ved at højreklikke i 'Endpoints' området -> Vælge 'Add New Item'. Tast det nye navn på service (man kan vælge at beholde samme navn). Herefter klikker man på 'Unrelated' under 'Configuration' / 'Bindings' og trækker man den importerede binding, og slipper den i 'Binding' feltet, og der trykkes herefter på 'Apply'. Herefter skal operationen knyttes til det oprettede 'Endpoint'. Dette gøres ved at klikke op 'Unrelated' fanen under 'Operations' og trække operationen over det nye 'Endpoint' og slippes herefter (over endpoint ikonet).
Nu mangler den indregistrerede service et sted, den kan blive udstillet på, dvs. et sted den skal hostes. Ved installation af MSE er der pr. default oprettet en lokal runtime server. Den kan ses under 'Servers' og hedder 'LocalRS'. Der dobbeltklikkes på LocalRS og under 'Endpoints' aktiveres 'Unrelated' fanen. I denne finder man, det allerede oprettede 'Endpoint' og dette 'Endpoint' trækkes og slippes oventpå LocalRS (server ikon billedet). Der vil starte et lille vindue op, hvor man vil finde versions nummeret og angivelse af om den skal være aktiv. Herefter vil man kunne finde URL'en til den virtualiserede service under 'Endpoint URL'. Test evt. ved at højreklikke på URL'en og vælg 'Browse'. Lad en service konsumer herefter lave en service reference til denne service, og se at alt spiller nøjagtigt på samme måde, som ved en direkte reference til en WCF service.

Umiddelbart er der jo ikke så meget magi ved at virtualisere en service til at starte med. For at bevise den løse kobling og de service orienterede kræfter, kan vi fortsætte ved at ændre WCF servicen. Lad os ændre signaturen på en operation, og se om en service klient stadigvæk kan køre videre, som om intet er sket.

Lav en ændring af en fysisk operation på den relevante service. Når man herefter kører igennem wizarden under Tools -> Load Resources -> etc. Vil du bemærke, at de uberørte operationer ikke vil blive importeret. Sørg for at ændre versions nummeret på operationen til fx. '1.0.0.1'. Hægt den herefter på det allerede oprettede 'EndPoint' og man vil herfter kunne se at en version '1.0.0.0' allerede vil være synlig. Vælg 'published' og 'Active' på den nye operation. Vend tilbage til service klienten og bemærk løsningen kører videre uden problemer. I dette tilfælde var signaturen på operationen ændret, men klienten faldt ikke om af den grund.

Er der problemer med at få opdateret MSE runtime med nyeste opdateringer som fx. nye operationer, hvor WSDL ikke umiddelbart ser opdateret ud. Så kan det hjælpe at genstarte 'MSE Runtime Server' under 'Services'. Start -> Administrative Tools -> Services.

MSE Runtime Server

MSE kommer med et service tester tool, man hurtigt kan teste de virtualiserede services igennem.

MSE test tool

Er der ændringer på eksisterende skemaer effektureres rettelserne på følgende måde.

Åbn Modelviewer -> Tools -> Manage Service Model Components -> Data Entities. Tryk 'Next' og find schemaet under listen. Vælg Complex type og tryk 'Next'. Vælg 'Update Revision References - Check here to update entity...' og tryk 'Next'. Kør Wizarden færdig og genstart 'MSE Catalog Server' og 'MSE Runtime Server' under 'Services'. Start -> Administrative Tools -> Services. Genstart IIS og browse i WSDL filen og se data entiteten er ændret.

Kontakt mig gerne for yderligere spørgsmål.

Managed Services Engine (MSE) er gratis og kan hentes ned fra codeplex her.

Logfilen kan findes her -> C:\Temp\logs\mse_catalog_sb.svclog



Mine to seneste indlæg har beskrevet, hvordan man kan opnå sikkerhed mellem en WCF service host og en service konsumerende klient. Spørgsmålet er, hvilken sikkerhedsløsning egner sig bedst. Nøjagtigt ligesom mange andre tekniske overvejelser findes der ikke ét endegyldigt svar. Der findes kun en vægtning, der er baseret på forskellige scenarier, og hvor om det gælder at finde den bedst egnede løsning.

Løsningsmodellen med to X.509 certifikater, nemlig et klient certifikat og et server certifikat, vil umiddelbart være den mest sikre. Fx. forsikrer den, hvis en tredje part skulle komme i besiddelse af et af certifikaterne, ikke kan udnytte dette til at få utilsigtet adgang, da man mangler det andet certifikat. Dog vil denne løsningsmodel kræve mere administration af certifikaterne, da alle klienter har deres eget dedikeret certifikat, der skal administreres og indregistreres på WCF serveren.

Løsning1

Løsningsmodellen med et enkelt certifikat letter denne administrations byrde, men sænker samtidigt sikkerheds niveauet en anelse, da hvis en tredje part skulle komme i besiddelse af certfikatet, kan bruge dette til at tilkendegive sig selv og få utilsigtet adgang til serveren. Der er dog naturligvis andre sikkerheds foranstaltninger, der forhindrer denne fremmede indtrængen.

Løsning2

Begge er dog en god og hurtig måde at få implementeret WCF message sikkerhed på.

Er der et ønske om at kryptere kommunikationen mellem service klient og service host, kan man signere data trafikken med certifikaterne. Kommunikationen vil herefter ske over HTTPS istedet for HTTP protokollen.
Der vil være et indlæg, der behandler dette emne på et senere tidspunkt.



Det forrige indlæg handlede om at gøre brug af to X.509 certifikater til implementering af WCF message sikkerhed. Nemlig et klient certifikat og et server certifikat. Dette indlæg kommer til at handle om at gøre brug af et enkelt fælles certifikat. Har man ikke lyst til at rode rundt med for mange certifikater, kan denne løsning benyttes. Konfiguration og fremgangsmåden minder meget om det forrige indlæg, dog er der et lille trick ved eksport af X.509 certifikatet, der vil blive beskrevet senere.

Vi starter med at danne et fælles certifikat, dette kan gøres på WCF service host maskinen:

makecert -pe -ss My -sr LocalMachine -a sha1 -sky exchange -n CN=Common

Tryk 'Start' -> Kør 'mmc'

Dette vil starte et konsol vindue, og for at se certifikatet skal man gøre følgende:
Tryk på 'File' -> 'Add/Remove Snap-in...' vælge 'Certificates' under 'Available snap-ins' og trykke på 'Add' -> 'Computer account' -> Next -> Finnish

mmc konsol

Under 'Personal/Certificates' vil man finde certifikatet. Herefter skal "NETWORK SERVICE" tildeles læse rettigheder for at læse den private nøgle på certifikatet.
Højreklik på 'Common' certifikatet -> All Tasks -> 'Manage Private Keys...'.
Herefter gives "NETWORK SERVICE" Full control. Højreklik herefter på 'Common' certifikatet og vælg 'Copy'. Find 'Trusted people\Certificates' og kopier certifikatet herunder.

Certifikatet skal herefter eksporteres. Dette gøres på følgende måde:

Højreklik på 'MyServer' certifikatet -> All Tasks -> Export -> Next -> Vælg 'Yes, export the private key' -> 'Personal Information Exchange - PKCS #12 (.PFX)' -> Next -> Angive et kodeord -> Next -> 'Common' og gem certifikatet.

Kopier herefter certifikatet over på klient maskinen, så den er klar til senere brug.

Nu skal den maskine, der udstiller WCF service konfigureres.

I web.config filen skal følgende konfiguration sættes op.
Web.config filen udvides med konfiguration af certifikat delen:

Efterfølgende sættes klient maskinen op. Først skal 'Common' certifikatet fra tidligere importeres. Dette gøres ved at starte MMC på samme måde som før og tilføje certificates snap-in, 'Computer Account' og herefter højreklikke på 'Personal' -> All Task og vælge import. Højreklik herefter på 'Common' certifikatet og vælg 'Copy'. Find 'Trusted people\Certificates' og kopier certifikatet herunder.

Tildel "NETWORK SERVICE" læse rettigheder på samme måde som før.

Trick!
Eksportér 'Common' certifikatet på følgende måde:
Højreklik på 'Common' certifikatet -> All Tasks -> Export -> Next -> Vælg 'No, do not export the private key' -> Base-64 encoded X.509(.CER) -> 'MyServer' og gem certifikatet. Importér 'Common' certifikatet igen. Dette vil erstatte *.pfx certifikatet med *.cer versionen.

Nu sættes klientens web.config op.

Test applikationen...og det hele burde spille fint uden problemer.

WCF sikkerheds autentificering af klient og server vha. et enkelt X.509 certifikat.



Fasil Malik Hayat

Developer
.Net, WCF, Sharepoint, MOSS, Biztalk, JQuery.

linkedin facebook twitter plaxo google+ grooveshark boxee
fasil

Education

fasil

Bachelor of Science (Honours)
De Montfort University, Leicester.

Fag: Java, MVC, Information Strategy, ITIL

Microsoft Certified Technology Specialist

Skills

.net vs2010 - C# sharepoint sql server jquery ubuntu java netbeans


  

Sign in