次の方法で共有


Impersonating another user with the Web Form Report Viewer control and SQL Server Reporting Services 2005

I covered how to do impersonation with the Winform ReportViewer control a few weeks ago, so here's how to do it with the WebForm version.

The first thing to keep in mind is that by default the WebForm ReportViewer will run under the security context of your application's AppPool identity. So, depending on the identity of the AppPool your app is running under, it's not unlikely that the control won't work at all (authorization-wise) when you first use it.

The easiest way to get impersonation working is to simply use web.config and set identity impersonate to true -- and optionally plug in a specific user / password, etc. etc.

See the following "how to" article if this stuff doesn't sound familiar:

306158 How to implement impersonation in an ASP.NET application
https://support.microsoft.com/?id=306158

The most interesting scenario is around how to dynamically swap the identity that the ReportViewer uses. This takes a bit more work.

First, you have to implement IReportServerCredentials. The two key things that we do are:

  • Retrieve the user credentials we want to use for impersonation (check out the overloaded properties where we set this stuff)
  • Use the credentials to create and return a WindowsIdentity object (examine the ImpersonationUser member)

namespace x
{
    public class ReportViewerCredentials : IReportServerCredentials
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        public extern static bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
            int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
       
        public ReportViewerCredentials()
        {
        }

        public ReportViewerCredentials(string username)
        {
            this.Username = username;
        }

        public ReportViewerCredentials(string username, string password)
        {
            this.Username = username;
            this.Password = password;
        }

        public ReportViewerCredentials(string username, string password, string domain)
        {
            this.Username = username;
            this.Password = password;
            this.Domain = domain;
        }

        public string Username
        {
            get
            {
                return this.username;
            }
            set
            {
                string username = value;
                if (username.Contains("\\"))
                {
                    this.domain = username.Substring(0, username.IndexOf("\\"));
                    this.username = username.Substring(username.IndexOf("\\") + 1);
                }
                else
                {
                    this.username = username;
                }
            }
        }
        private string username;

 

        public string Password
        {
            get
            {
                return this.password;
            }
            set
            {
                this.password = value;
            }
        }
        private string password;

        public string Domain
        {
            get
            {
                return this.domain;
            }
            set
            {
                this.domain = value;
            }
        }
        private string domain;

 

        #region IReportServerCredentials Members

        public bool GetBasicCredentials(out string basicUser, out string basicPassword, out string basicDomain)
        {
            basicUser = username;
            basicPassword = password;
            basicDomain = domain;
            return username != null && password != null && domain != null;
        }

        public bool GetFormsCredentials(out string formsUser, out string formsPassword, out string formsAuthority)
        {
            formsUser = username;
            formsPassword = password;
            formsAuthority = domain;
            return username != null && password != null && domain != null;

        }

        public bool GetFormsCredentials(out Cookie authCookie,
      out string user, out string password, out string authority)
        {
            authCookie = null;
            user = password = authority = null;
            return false;  // Not implemented
        }

        public WindowsIdentity ImpersonationUser
        {
            get
            {

                string[] args = new string[3] { this.Domain.ToString(), this.Username.ToString(), this.Password.ToString() };
                IntPtr tokenHandle = new IntPtr(0);
                IntPtr dupeTokenHandle = new IntPtr(0);

                 const int LOGON32_PROVIDER_DEFAULT = 0;
        //This parameter causes LogonUser to create a primary token.
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int SecurityImpersonation = 2;

        tokenHandle = IntPtr.Zero;
        dupeTokenHandle = IntPtr.Zero;
        try
        {
            // Call LogonUser to obtain an handle to an access token.
            bool returnValue = LogonUser(args[1], args[0], args[2],
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                ref tokenHandle);

            if (false == returnValue)
            {
                Console.WriteLine("LogonUser failed with error code : {0}",
                    Marshal.GetLastWin32Error());
                return null;
            }

            // Check the identity.
            System.Diagnostics.Trace.WriteLine("Before impersonation: "
                + WindowsIdentity.GetCurrent().Name);

            bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);
            if (false == retVal)
            {
                CloseHandle(tokenHandle);
                Console.WriteLine("Exception in token duplication.");
                return null;
            }

            // The token that is passed to the following constructor must
            // be a primary token to impersonate.
            WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
            WindowsImpersonationContext impersonatedUser = newId.Impersonate();

            // Free the tokens.
            if (tokenHandle != IntPtr.Zero)
                CloseHandle(tokenHandle);
            if (dupeTokenHandle != IntPtr.Zero)
                CloseHandle(dupeTokenHandle);

            // Check the identity.
            System.Diagnostics.Trace.WriteLine("After impersonation: "
                + WindowsIdentity.GetCurrent().Name);

            return newId;

        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception occurred. " + ex.Message);
        }
               
                return null;
            }
        }

        public ICredentials NetworkCredentials
        {
            get
            {
                return null;  // Not using NetworkCredentials to authenticate.
            }
        }

        #endregion
    }
}

OK, now that we've implemented IReportServerCredentials, all that's left is to use the implementation by creating an instance of ReportViewerCredentials, and then assigning this to the reportViewer1.ServerReport.ReportServerCredentials property.

//I have this in Page_Load, assuming that the other properties of the control have already been set:

x.ReportViewerCredentials rvwCreds = new x.ReportViewerCredentials("UserName", "Password", "Domain");
reportViewer1.ServerReport.ReportServerCredentials =  rvwCreds;

Comments

  • Anonymous
    January 11, 2006
    The comment has been removed

  • Anonymous
    January 13, 2006
    Raghu, I'm only aware of this error in conjunction with viewing a LINKED report inside the report viewer control..

    Did you check your event log to see if the app pool or aspnet_wp is recycling? Try to narrow things down by running a report in LOCAL mode to see if it really is a problem with SSRS, the ReportViewer control, or the ASP.NET app hosting the control.

  • Anonymous
    February 15, 2006
    This worked fot me. The only change I am forced to make this work is changing the web app session state to InProc.

    Thanks,
    vamsi.

  • Anonymous
    March 08, 2006
    Hello Russell, I've implemented your ReportViewerCredentials class into my web app, now everything works fine except that sometimes when I try to change the ServerURL or ReportPath properties of the ReportViewer, I get an "Operation is not valid due to the current state of the object" error. I've tested this on many reports and found that the error only occurs when the report I'm viewing is a linked report (i.e. the property IsDrillthroughReport==true). You mentioned in your earlier comment "error in conjunction with viewing a LINKED report inside the report viewer control", so I wonder if my problem has to do with the impersonation and if so, is there any way to solve this?

    Thanks!
    Nic

  • Anonymous
    March 15, 2006
    nic, I see what you are talking about. There are a few reports of this behavior, and if you were using the WebForm ReportViewer, all you'd need to do is call ReportViewer.Reset(), and you'd probably be in good shape...however we don't have this method in the web reportviewer. I'll poke around and see if I can find an equivalent technique.

  • Anonymous
    March 15, 2006
    nic, see this workaround:

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=212322&SiteID=1

  • Anonymous
    March 30, 2006
    The comment has been removed

  • Anonymous
    March 30, 2006
    It looks like the problem doesn't occur under Windows XP...but when we push it out to our build server that is Windows 2003 we start getting the messages.

  • Anonymous
    March 30, 2006
    Here is the applicaton error log message:

    Event code: 3005
    Event message: An unhandled exception has occurred.
    Event time: 3/30/2006 10:39:10 AM
    Event time (UTC): 3/30/2006 4:39:10 PM
    Event ID: cf181f6f9e4f4460a6e0828711ba51e9
    Event sequence: 6267
    Event occurrence: 12
    Event detail code: 0

    Application information:
       Application domain: /LM/W3SVC/52577887/root/ReportServer-2-127881193422582207
       Trust level: RosettaSrv
       Application Virtual Path: /ReportServer
       Application Path: C:Program FilesMicrosoft SQL ServerMSSQL.2Reporting ServicesReportServer
       Machine name: BUILD

    Process information:
       Process ID: 4500
       Process name: w3wp.exe
       Account name: NT AUTHORITYNETWORK SERVICE


    In our XP environment we are using ASPNET...in the windows 2003 environment it is using the NETWORK SERVICE.

  • Anonymous
    March 30, 2006
    Ok, we resolved it.  We commented out the code in ImpersonateUser and just had it return null.  That made it return the GetBasicCredentials that worked.

    We figured we could eventually get the ImpersonateUser to work but this is much cleaner because we aren't having to use unsafe code.  We also don't need to impersonate.  The user we are passing in the the user we want...so we don't see why it would need to impersonate.

  • Anonymous
    May 09, 2006
    Hi,
    I am using the above code but I need to connect to reportserver on the same domain(network). The way I did this is I return null from the ImpersonationUser method but now my NetworkCredentials property looks like the following:

         public ICredentials NetworkCredentials
           {
               ///Comment out for basic security
               //// Comment out for basic security
               get
               {
                   CredentialCache cc = new CredentialCache();
                   cc.Add(new Uri(ConfigurationManager.AppSettings["ReportServerEndPoint"]), "Basic", new NetworkCredential(this.Username.ToString(), this.Password.ToString()));
                   return cc;  // Not using NetworkCredentials to authenticate.
               }
           }

    Is this the right way?. Does the user need to be a local user on the machine that has the report server?

    Thanks

  • Anonymous
    May 18, 2006
    I have  a problem with this, I have some reports that have multiple dropdownlist, when you select one dropdownlist it must refresh another dropdownlist, it works if you navigate to the report with reportmanager, but not with reportviewer, after refreshing one dropdowncontrol I got the same 401 unauthorized error.

    Any help

  • Anonymous
    May 22, 2006
    The comment has been removed

  • Anonymous
    May 24, 2006
    When trying to view the report I am receiving the error:

    The request failed with HTTP status 401: Unauthorized.
    * same user and password works fine on windows application
    Also I have tried Administrator user.

    My code for the report viewer is:

    rvBlackListReport.ProcessingMode = ProcessingMode.Remote;

    rvBlackListReport.ServerReport.ReportServerUrl = new
    Uri("https://www.MyDomain.com/ReportServer");
    rvBlackListReport.ServerReport.ReportPath = "/MyReports/TestReport";

    ReportViewerCredentials rvc = new
    ReportViewerCredentials("TestUser", "TestPassword", "");
    rvBlackListReport.ServerReport.ReportServerCredentials = rvc;

    Regards,
    Asaf

  • Anonymous
    September 26, 2006
    I've reported a bug regarding using the credentials code seen above... apparently the keepalive code ReportViewer uses doesn't pay attention to it, so the session in ReportServerTempDB gets dumped after 10 mins instead of being kept alive:

    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=213131

    Please see the workaround I posted there (changing the app pool identity)... and post a workaround yourself if you've figured out a better way around this problem.

  • Anonymous
    October 02, 2006
    For the page navigation problem, page navigation is a postback.  Look at what you are doing in the PageLoad when IsPostBack == true.

  • Anonymous
    November 01, 2006
    Hi Russel,You just had a training in our site a couple of weeks ago and you gave you C# script for Impersonation but I got the script from the web below.My question is, Using the script below How do I clean up the Impersonation?  otherwise Is the user is still logged in using the Impersonated Account?Thanks, hope to hear from you.RaquelImports Microsoft.Reporting.WebFormsImports System.web.SecurityImports Microsoft.VisualBasicImports System.NetPublic Class ReportServerCredentials   Implements IReportServerCredentials   Private _userName As String   Private _password As String   Private _domain As String   Public Sub New(ByVal userName As String, ByVal password As String, ByVal domain As String)       _userName = userName       _password = password       _domain = domain   End Sub   Public ReadOnly Property ImpersonationUser() As System.Security.Principal.WindowsIdentity Implements Microsoft.Reporting.WebForms.IReportServerCredentials.ImpersonationUser       Get           Return Nothing       End Get   End Property   Public ReadOnly Property NetworkCredentials() As ICredentials Implements Microsoft.Reporting.WebForms.IReportServerCredentials.NetworkCredentials       Get           Return New NetworkCredential(_userName, _password, _domain)       End Get   End Property   Public Function GetFormsCredentials(ByRef authCookie As System.Net.Cookie, ByRef userName As String, ByRef password As String, ByRef authority As String) As Boolean Implements Microsoft.Reporting.WebForms.IReportServerCredentials.GetFormsCredentials       userName = _userName       password = _password       authority = _domain       Return Nothing   End FunctionEnd Class

  • Anonymous
    November 03, 2006
    Hi Raquel --It looks like you're trying to do two things at the same time here -- Get Forms Auth credentials, and then turn around and do Windows Impersonation...Is this correct?

  • Anonymous
    November 29, 2006
    Hi,russch:I had developed a reporting service home page for our intranet . And I deployed this home page at "domain.server1" (for example) and my sql server database  and reporting service is at another server "domain.server2"(for example). on the "domain.server1" , the Authentication methods is Integrated windows authentication. and on the "domain.server2" , with the reportingservice server properites and home folder, in the "permissions" tab, I had add the "domain.server1$" as system user role. so I can view my report via my home page and it works fine. If I remove the  "domain.server1$" from the reporting service, there could be encountered an error "The permissions granted to user 'domain.server1$' are insufficient for performing this operation".I used          rService.Credentials = System.Net.CredentialCache.DefaultCredentials;in my home page to list the reports. But this function return nothing.I think this must be a problem, so I change this code as           System.Net.NetworkCredential myCreds = new System.Net.NetworkCredential();           myCreds.Domain = "domain";           myCreds.UserName = "UserName";           myCreds.Password = "Password";           rService.Credentials = myCreds;and add this "UserName" to a local group "group1" on "domain.server2" , and grant this group "group1" to access the reporting service server. It works fine.Because I need to access my home page and reporting service as the same Integrated windows authentication.I do not need the user to type in the user name and password again and again. So if they login the window system and then they can access their own home page functions and their groups own report server folders.But now , I'm confused how to get the user's information .I can get the user domain and username by HttpContext,but I can not get the password infomation , even though encrypted.Do I can get the user's password information from the AD or there is another choice for this condition?could you give me some suggestion?T.I.AHenry

  • Anonymous
    December 09, 2006
    Hi Henry -- I'm a tiny bit unclear on how you are displaying the report in your homepage (inside an IFrame? Using the Report Viewer control?), but this shouldn't be too difficult.If you modify web.config on your homepage to use Impersonation, then display reports through the Report Viewer control, this should happen:User hits the homepage, and logs in if necessary (as domainuser1, lets say) From now on, everything they do happens under this security context. They run your report through the Report Viewer, and the Report Viewer picks up the credentials of the user (domainuser1) automatically, passing them along to domainserver2, where SSRS lives.

  • Anonymous
    December 16, 2006
    Hi,Russch:  Thank your for your suggestion.  I had fixed this issue a few day ago.  I used a IFrame in my homepage and also use a report viewer control to display my report.  May be I had not described clearly.:( I don't want the user type in the password when they hits the homepage, so I integrated the windows authentication. And this security context can not access to SSRS server before. I had configurated the domain.server1 as a trusted machine and use the Kerberos authentication and it work fine. Anyway, thanks a lot!:)Henry

  • Anonymous
    December 27, 2006
    Trying to access a remote server with ReportViewer and was getting the 401 error.  I tried your impersonation suggestion below but get the following message:"For security reasons DTD is prohibited in this XML document. To enable DTD processing set the ProhibitDtd property on XmlReaderSettings to false and pass the settings into XmlReader.Create method. "  I searched the web far and wide but could not find a reasonable solution.  There are no errors in my event log.I am using 2 machines on a peer to peer windows network (no domain).Any clues?Thanks!

  • Anonymous
    December 28, 2006
    The comment has been removed

  • Anonymous
    January 08, 2007
    Use following credential and specify "NTLM"CredentialCache cc = new CredentialCache();               cc.Add(uri, "NTLM", new NetworkCredential("administrator", "sierra", "dev3.agi"));               _reportViewer.ServerReport.ReportServerCredentials.NetworkCredentials = cc;

  • Anonymous
    February 06, 2007
    Do you have this code in VB?Do you have to do something similar to this in reporting services prior to 2005?

  • Anonymous
    February 06, 2007
    I don't, sorry.

  • Anonymous
    March 07, 2007
    The comment has been removed

  • Anonymous
    March 21, 2007
    Avoiding the '401 Unauthorized' Error when Using the ReportViewer in Your Web Application

  • Anonymous
    June 21, 2007
    Oggi mi sono trovato nella necessità di impersonare un utente per accedere a dei report tramite il ReportViewer

  • Anonymous
    July 31, 2007
    Hi thereWhich username and password is passed into the 'ReportViewerCredentials' method on your report viewer page? Is it the specific users account info or another account that has to be set up separately. If the latter, where does this account have to be set up?I tried the code, passing in my own windows account username and password but just got an access denied error.Also, in which web.config file do you set <identity impersonate="true"/> ? The one in your application or the one in the report server virtual directory (as it is in there by default)?Many thanksJohn

  • Anonymous
    August 23, 2007
    Hi Russch,I have been trying to search on Impersonation of through the report viewer control. I read your arctile and it was a help to some extent. I understand that we have implemented  IReportServerCredentials interface. it is working fine when i hard code my username, password and domain while instantiating the class, but what if i have to impersonate dynamically with the logged on user windows credientials so that my code access the report server with the current user's credentials. For that matter i have failed so far finding a way to get the password for the current user. I can just use the User.Identity property on the page to get username and domain but not the password and hence cannot successfully impersonate the current user. Do you or anybody have any thoughts how can i overcome this problem??

  • Anonymous
    December 05, 2007
    The comment has been removed

  • Anonymous
    December 27, 2007
    Hi,I have a report in rpviewer in remote mode,inside my report i am using drillthrogh subreports and the navigation through it work fine, but, when i am in the subreport and after to choose export or print when i press the go parent button i haver a error:Call back drilltroghtWhat can i do 4 resolve this issue?

  • Anonymous
    December 27, 2007
    Hi,I have a report in rpviewer in remote mode,inside my report i am using drillthrogh subreports and the navigation through it work fine, but, when i am in the subreport and after to choose export or print when i press the go back to the parent report button i haver a error:Back Call without drillthrough report.I think is the sessionalive problem but how can solve this

  • Anonymous
    February 15, 2008
    I tried with the ImpersonationUser returning null and  public ICredentials NetworkCredentials       {           get           {               //return null;  // Not using NetworkCredentials to authenticate.              CredentialCache cc = new CredentialCache();              cc.Add(new Uri("http://svr-devstation1/reportserver"),"Basic",                  new NetworkCredential( "kviswanathan", "Pranav0206","Federation"));              return cc;  // Not using NetworkCredentials to authenticate.          }       }i get HTTP 401 not authorized error.When i step through, i could see the correct domain,username and password in the ReportServerCredentials.Any help is greatly appreciated.thanks.KV

  • Anonymous
    February 15, 2008
    I forgot to mention that i use NT authentication.thanksKV

  • Anonymous
    March 06, 2008
    This example doesn't work with nested master pages. It says:"HTTP status 401: Unauthorized"?I am working under Windows XP, SqlServer 2005.Help please

  • Anonymous
    March 07, 2008
    It's ok now, my domain name was wrong.Thank you very much for this solution!!!

  • Anonymous
    April 30, 2008
    PingBack from http://bcesolutions.com/2008/04/30/running-reportviewer-control-on-windows-2000/

  • Anonymous
    May 20, 2009
    Is there any way to give the current user to the report? I can fix it to some user, but it should return user-specific data and thus send the windows user to the server. It works for local IIS to server Reporting Server, but not for server IIS to server Reporting Server. Everyone has the rights to run the report and it works from the reporting server.

  • Anonymous
    July 29, 2009
    Using info from the support page you linked to, I was able to access the report viewer perfectly when I added this to my web.config <identity impersonate="true" userName="accountname" password="password" /> Using the code Label1.Text = System.Security.Principal.WindowsIdentity.GetCurrent().Name; I can see I am logged into <machine><user> and it passed in all my parameters correctly. However, when I tried using your credentials code, the the page crashes when setting parameters and gives the error:    * The request failed with HTTP status 401: Unauthorized. If I comment that out then the report viewer itself gives the same error. Note that with your code it logs me in as "NT AUTHORITYNETWORK SERVICE" which I have given Browser rights. Any clue on what is going wrong here?

  • Anonymous
    March 23, 2010
    ReportServerCredentials is a ReadOnly Property, how can you assign to it ??? I am using WinForms...

  • Anonymous
    September 28, 2010
    Nice work, do you have a solution for and SQL Server Reporting Services 2008 ?

  • Anonymous
    September 28, 2010
    Nice work, do you have a solution for  ReportViewer control version 10.0.0.0 and SQL Server Reporting Services 2008? I have the same issue as danny, the ReportServerCredentials property is read only.

  • Anonymous
    July 19, 2011
    ( after all this time ). Does anyone know how to use the Property ImpersonationUser() As WindowsIdentity to get the current logged on Active Directory account so that I can then pass that credential to the report server? longstrd@osceola.k12.fl.us