Forms authentication na Reporting Services

Mám trochu času, tak sa môžete tešiť z toho, že oživím trochu svoj .NET blog. Tento krát napíšem ako som rozbehával ASP.NET forms authentication na Reporting Services. Toto som síce riešil dávnejšie, ale až teraz som si našiel čas to napísať.

Asi ešte nie som dostatočne google addicted, ale najprv som siahol po MSDN a až potom po google. Na MSDN síce bolo napísané, ktoré interfacy mám implementovať, ale len veľmi stručne tam bolo napísané že ako ich mám implementovať. Teda o niektorých operáciách interfacov som sa viac dozvedel z ich názvu ako zo samotného popisu v MSDN. Takže som prešiel na možnosť B, a teda google. Najlepšie by bolo nájsť článok o tom, ako to už niekto urobil. A aj som našiel. MS na codeplexe zverejňuje všetky oficiálne sample k SQL Server. Čiže je tam možné stiahnuť aj príkladovú databázu AdventureWorks, ktorá bola dodávaná s SQL Server 2005. No a našiel som tam aj sadu príkladov pre Reporting Services a v tejto sade príkladov je aj príklad na forms authentication. Všetko je to na stránke http://www.codeplex.com/MSFTRSProdSamples/ a príklad sa volá Security Extension Sample.

Takže som si daný príklad stiahol, otvoril a spravil svoju vlastnú implementáciu. Tu som si všimol, že kusy kódu, ktoré boli uvedené v MSDN ako príklad, sú vlastne z tohto príkladu. Väčšina práce spočívala v copy&paste. Zmenil som len to, že som použil vlastný membership provider a pri autorizácii je možné v ACL použiť aj rolu, nie len užívateľa. Nasadil som to na Report Server a ono to normálne fungovalo. Ale žiaľ nie až tak normálne. Problém bol v tom, že ak som zadal do prehliadača adresu Report Serveru, tak zobrazenie login stránky trvalo asi 1 minútu. Potom, keď sa užívateľ prihlásil, tak všetko fungovalo tak ako má. Fungovalo nastavovanie práv a bolo to rychle ako Report Server, ktorý používa windows autentifikáciu.

Pozrel som log Report Server a tam boli NullReferenceException. Čo ešte nie je dôvod minútové zdržanie. Potom som pozrel Report Web (teda front-end) log a tam boli time-outy. Tak som zase otvoril svoj obľúbený .NET Reflector a našiel som si funkcie, ktoré boli uvedené v stacku pri výnimkách. A problematická procedúra bola nasledovná. Najprv ešte uvediem implementáciu GetUserInfo operácie v IAuthenticationExtension, ktorá bola použitá v príklade.

public void GetUserInfo(out IIdentity userIdentity, out IntPtr userId)

{

// If the current user identity is not null,

// set the userIdentity parameter to that of the current user

if (HttpContext.Current != null && HttpContext.Current.User != null)

{

userIdentity = HttpContext.Current.User.Identity;

}

else

userIdentity = null;

// initialize a pointer to the current user id to zero

userId = IntPtr.Zero;

}

  1. Užívateľ zobrazí Default.aspx stránku.
  2. Každá stránka v RS front-ende je odvodená od triedy ReportingPage. Na tejto stránke sa odchytí udalosť Init.
  3. V tejto udalosti sa zisťuje, či sa má pre komunikáciu s back-endom použiť SSL alebo nie.
  4. Najprv sa zavolá ListSecureMethods() web metóda na back-ende bez použitia SSL. V konfigurácii som nezapol, že SSL je vynútené.
  5. Na back-ende sa odchytí udalosť HttpApplication.AuthenticateRequest.
  6. V tejto udalosti sa zistí, že sa používa forms authentication a uživateľ nie je prihlásený (HttpContext.Current.User == null), tak sa do response headers pridá položka RSNotAuthenticated.
  7. Ďalej sa v tejto udalosti zavolá GetUserInfo metóda našej authentication extension. Užívateľ ešte nie je prihlásený, takže ako userIdentity vráti null. Lenže ďalej sa volá IIdentity.Name, čo vyvolá NullReferenceException, ktorú som našiel v logu.
  8. Táto výnimka sa pošle na front-end. Pri prijímaní odpovede z back-endu sa nájde v hlavičkách položka RSNotAuthenticated, takže sa do response headers, ktoré pôjdu z front-endu na prehliadač, zapíše, že sa má presmerovať na login page. Ale neukončí sa spracovanie requestu.
  9. Doteraz by všetko ako-tak fungovalo, až na to zapisovanie NullReferenceException do logu. Táto výnimka sa odchytí na front-ende. Ten si myslí, že back-end nekomunikuje na nezabezpečenej linke, tak skúsi zavolať ListSecureMethods() web metódu cez SSL.
  10. 30 sekúnd sa čaká.
  11. Potom sa hodí WebException s tým, že nastal time out. Toto sa tiež zapísalo do logu.
  12. Táto výnimka sa odchytila v HttpApplication.Error, kde sa spustí HttpServerUtility.Transfer s tým, že sa spustí Error.aspx. Samozrejme, že aj táto stránka je odvodená od ReportingPage, takže celý cirkus od bodu 2 sa zopakuje. Čo je ďalších 30 sekúnd čakania.
  13. Nakoniec sa na klienta pošle asi stránka, že nastala neočakávaná chyba. Lenže táto sa ani nezobrazí, pretože browser si hneď v hlavičke prečíta, že sa má presmerovať na Login.aspx.

Najprv som si myslel, že sa problém vyrieši, keď sa na web server nainštaluje SSL certifikát. Lenže toto nepomohlo. Možno to nebolo úplne správne nainštalované, lebo ten certifikát samozrejme nebol overený certifikačnou autoritou. Tak som sa rozhodol, že metódu GetUserInfo z príkladu mierne upravím. A to nasledovne:

public const string GuestUserName = "_Guest_";

public void GetUserInfo(out IIdentity userIdentity, out IntPtr userId)

{

    if (HttpContext.Current != null && HttpContext.Current.User != null)

    {

        userIdentity = HttpContext.Current.User.Identity;

    }

    else

    {

        userIdentity = new GenericIdentity(GuestUserName);

    }

    userId = IntPtr.Zero;

}

Potom ešte samozrejme bolo potrebné upraviť AuthorizationExtension tak, že užívateľ _Guest_ má všetko zakázané bez ohľadu na to, čo je v ACL zoznamoch. A minútové čakanie zmizlo :)

Takže ak aj vy budete siahať po príklade na security extension pre Reporting Services, tak si dajte pozor na túto chybu.

Ďalší problém sa vyskytol, keď som na Report Server chcel pristupovať cez SQL Server Management Studio, tak mi stále tvrdil, že som nezadal správne meno alebo heslo. Problém bol v tom, že v našej aplikácii sú uživeteľské mená v tvare domain\username. Samozrejme, že domain nie je NT doména, ale jednoducho užívateľské meno sa skladá z dvoch častí oddelených opačným lomítkom. Lenže SQL Server Management Studio tú časť pred lomítkom zahodí. Teda možno to dáva do parametru domain pri prihlasovaní, ale ten sa pri forms authentication ignoruje. Takže som to vyriešil tak, že moja security extension pracuje s menami v tvare domain/username. Čiže používam normálne lomítko. A vždy pri použití membership providera zamením normálne lomítko za opačné.

Bookmark and Share
Zaradené do:

Komentáre

Bez komentárov

Prihlásiť | Registrovať | Pomoc