vbaweb-servicescookiessession-cookiesjsessionid

How to set and get JSESSIONID cookie in VBA?


I'm writing a VBA web service client in Excel 2010 using MSXML2.XMLHTTP60 for my Java REST web services hosted on Tomcat 8.5.5.

In VBA, I want to snag the string JSESSIONID=E4E7666024C56427645D65BEB49ADC11 from a response and set it in a subsequent request.
(if Excel crashes, it seems that this cookie is lost and the user has to authenticate again. I want to set the last stored session ID for the user, so if the session is still alive on the server, they don't have to re-authenticate in the Excel client.)

I saw some online resources according to which the following will pull the JSESSIONID cookie, but the last line always prints empty:

Dim httpObj As New MSXML2.XMLHTTP60
With httpObj
    .Open "POST", URL, False
    .SetRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
    .SetRequestHeader "Connection", "keep-alive"
    .Send
End With
Debug.Print "Response header Cookie: " & httpObj.GetResponseHeader("Cookie")  'This should pull the JSESSIONID cookie but is empty

When I print httpObj.GetAllResponseHeaders I do not see any headers that hold JSESSIONID.

In the same resources, the following should set the desired cookie, but it doesn't (I print out the headers of the incoming request on the server and see that my attempt did not override the JSESSIONID value).

httpObj.SetRequestHeader "Cookie", "JSESSIONID=blahblah"

I may be missing the mechanism for how JSESSIONED is transmitted, and how and when VBA pulls it and sets it.


Solution

  • While omegastripes posted a great solution, I wanted to share the solution I ended up using.

    The original MSXML2.XMLHTTP60 object I used does not support cookies. So instead I used WinHttp.WinHttpRequest.

    This requires adding a reference to your code: In VBA IDE go to Tools-->References and make sure that Microsoft WinHTPP.Services version xxx is selected.

    Snagging the cookie:

    Code that grabs the cookie and stores it (assuming an object httpObj of type WinHttp.WinHttpRequest):

    ' Get the JESSIONID cookie
    Dim strCookie As String
    Dim jsessionidCookie As String
    
    strCookie = httpObj.GetResponseHeader("Set-Cookie")     ' --> "JSESSIONID=40DD2DFCAF24A2D64544F55194FCE04E;path=/pamsservices;HttpOnly"
    jsessionidCookie = GetJsessionIdCookie(strCookie)       ' Strips to  "JSESSIONID=40DD2DFCAF24A2D64544F55194FCE04E"
    
    'Store JSESSIONID cookie in the cache sheet
    

    Where the procedure GetJsessionIdCookie is:

    ' Takes a string of the form "JSESSIONID=40DD2DFCAF24A2D64544F55194FCE04E;path=/pamsservices;HttpOnly"
    ' and returns only the portion "JSESSIONID=40DD2DFCAF24A2D64544F55194FCE04E"
    Public Function GetJsessionIdCookie(setCookieStr As String) As String
        'JSESSIONID=40DD2DFCAF24A2D64544F55194FCE04E;path=/pamsservices;HttpOnly
    
        Dim jsessionidCookie As String
    
        Dim words() As String
        Dim word As Variant
    
        words = Split(setCookieStr, ";")
        For Each word In words
            If InStr(1, word, "JSESSIONID") > 0 Then
                jsessionidCookie = word
            End If
        Next word
    
        GetJsessionIdCookie = jsessionidCookie
    End Function
    

    Setting the cookie:

    Here's the method that creates an WinHttp.WinHttpRequest object and sets the cookie that was previously stored:

    Public Function GetHttpObj(httpMethod As String, uri As String, Optional async As Boolean = False, _
        Optional setJessionId As Boolean = True, _
        Optional contentType As String = "application/xml") As WinHttp.WinHttpRequest
        Dim cacheUtils As New CCacheUtils
        Dim httpObj As New WinHttp.WinHttpRequest
        With httpObj
            .Open httpMethod, uri, async
            .SetRequestHeader "origin", "pamsXL"
            .SetRequestHeader "User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
            .SetRequestHeader "Connection", "keep-alive"
            .SetRequestHeader "Content-type", contentType
            .SetRequestHeader "cache-control", "no-cache"
        End With
    
        ' --- Pull stored cookie and attach to request ---
        If setJessionId Then
            httpObj.SetRequestHeader "Cookie", cacheUtils.GetCachedValue(wsJsessionidAddr)
        End If
    
        Set GetHttpObj = httpObj
    End Function
    

    Where CCacheUtils is a class I implemented for storing and retrieving cached values such as the JSESSIONID cookie.