Author | Topic: WAA session manager | |
---|---|---|
Allen Lee | WAA session manager on Wed, 13 Apr 2016 23:00:55 -0700 Given that: 1. the WAA 1.90.331 session manager will assign a WAA session to the wrong user (PDR 6483) 2. under certain circumstances, information that is stored in a WAA session carries information that stems from a wrong connection 3. the problem occurs when the same WAA package is requested from more then one client within a short time frame Has anyone developed their own session manager that replaces the default WAA session manager? Can anyone, who experienced this problem in WAA, say that the problem no longer exists in CXP? | |
Thomas Braun | Re: WAA session manager on Thu, 14 Apr 2016 08:38:15 +0200 Allen Lee wrote: > Has anyone developed their own session manager that replaces the default > WAA session manager? Yes. I did. But AFAIK, the WAA session manage uses cookies to identify the client, while I don't, I use session keys that are posted with each web page, so it is no drop-in replacement. Thomas | |
Allen Lee | Re: WAA session manager on Thu, 14 Apr 2016 01:01:59 -0700 On 4/13/2016 11:38 PM, Thomas Braun wrote: > Allen Lee wrote: > >> Has anyone developed their own session manager that replaces the default >> WAA session manager? > > Yes. I did. > > But AFAIK, the WAA session manage uses cookies to identify the client, > while I don't, I use session keys that are posted with each web page, so it > is no drop-in replacement. > > Thomas > By session keys, do you mean that you store parameters in an array via oContext:setCargo and then pass the parameters to the next page via oContext:getCargo? If so, what method do you use to create a unique identifier for each user? | |
Thomas Braun | Re: WAA session manager on Thu, 14 Apr 2016 17:41:13 +0200 Allen Lee wrote: > By session keys, do you mean that you store parameters in an array via No, the session key is a unique identifier which is part of each page submitted: <input type="hidden" value="6VkLK5Hs4txCrMxc2Y3fKanDZQCLMKe9Z6A" name="SID"> If not part of the page, a new one is created and then stored in a "session.dbf" file at the first contact/login of the user: {"KEY", "C",64,0},; {"EXPIRES","C",12,0},; {"USERID","C",3,0},; {"DATA","M",4,0} } "DATA" holds the cargo contents. This is the code I use to replace setCargo/getCargo etc., maybe you can use parts of it: "base" is the base class for my own HTML3 class... at the bottom are some helper function you will need as well...hope this helps Thomas METHOD Base:openSession(nExpireMinutes, cUserId) local cCookie := ::GetVar("SID") local nArea := Select() local lFound := FALSE local cAlias := "SESSION" LOCAL cIpAddr := "" DEFAULT nExpireMinutes TO 60 DEFAULT cUserID TO "" IF ::context != NIL cIpAddr := ::context:getRemoteAddr() ENDIF if !Empty(cCookie) lFound := (cAlias)->(dbSeek(cCookie,.f.,"KEY")) ELSE cCookie := CreateNewSession( cIpAddr, nExpireMinutes, cUserId) endif if lFound .and. !Empty((cAlias)->DATA) ::cargoData := Bin2Var((cAlias)->DATA) else ::cargoData := {} endif ::SessionID := cCookie dbSelectArea(nArea) return lFound METHOD Base:CloseSession() if !Empty(::SessionID) DeleteSession(::SessionID) endif ::SessionID := "" return NIL METHOD Base:setCargo(x1,x2) local cCookie := ::GetVar("SID") local lRet := FALSE local nArea := Select() local cAlias := "SESSION" local i := 0 IF EMPTY(cCookie) cCookie := ::SessionID ENDIF if !(x1 == NIL) if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) if (cAlias)->(RecLock()) if x2 == NIL ::cargoData := x1 else x1 := ALLTRIM(x1) if !ValType(::cargoData) == 'A' ::cargoData := {} else i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } ) endif if i == 0 aadd(::cargoData, {x1,x2}) else ::cargoData[i][2] := x2 endif endif (cAlias)->DATA := Var2Bin(::cargoData) (cAlias)->(dbrUnlock()) lRet := TRUE endif endif endif dbSelectArea(nArea) return lRet METHOD Base:delCargo(x1) local cCookie := ::GetVar("SID") local lRet := FALSE local nArea := Select() local cAlias := "SESSION" local i := 0 IF EMPTY(cCookie) cCookie := ::SessionID ENDIF if !(x1 == NIL) if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) if (cAlias)->(RecLock()) x1 := ALLTRIM(x1) if !ValType(::cargoData) == 'A' ::cargoData := {} else i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } ) endif if i != 0 AREMOVE(::cargoData, i, 1) endif (cAlias)->DATA := Var2Bin(::cargoData) (cAlias)->(dbrUnlock()) lRet := TRUE endif endif endif dbSelectArea(nArea) return lRet METHOD Base:ClearCargo() local cCookie := ::GetVar("SID") local lRet := FALSE local nArea := Select() local cAlias := "SESSION" IF EMPTY(cCookie) cCookie := ::SessionID ENDIF if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) ::cargoData := {} if (cAlias)->(RecLock()) (cAlias)->DATA := Var2Bin(::cargoData) (cAlias)->(dbrUnlock()) lRet := TRUE endif ENDIF dbSelectArea(nArea) return lRet METHOD Base:setAllCargo() local cCookie := ::GetVar("SID") local lRet := FALSE local nArea := Select() local cAlias := "SESSION" local j:= 0, i := 0 LOCAL aVars := ::GetAllVars(), x1, x2 IF EMPTY(cCookie) cCookie := ::SessionID ENDIF if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) FOR j := 1 TO LEN(aVars) x1 := aVars[j,1] x2 := aVars[j,2,1] if !(x1 == NIL) x1 := ALLTRIM(x1) if !ValType(::cargoData) == 'A' ::cargoData := {} else i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } ) endif if i == 0 aadd(::cargoData, {x1,x2}) else ::cargoData[i][2] := x2 endif endif NEXT if (cAlias)->(RecLock()) (cAlias)->DATA := Var2Bin(::cargoData) (cAlias)->(dbrUnlock()) lRet := TRUE endif ENDIF dbSelectArea(nArea) return lRet METHOD Base:getCargo(cName) local xRet local i if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A' cName := ALLTRIM(cName) if (i := AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } )) > 0 xRet := ::cargoData[i][2] endif elseif cName == NIL xRet := ::cargoData endif return xRet METHOD Base:getAllCargo() return ::cargoData METHOD Base:IsCargo(cName) local lRet := FALSE if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A' lRet := (AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } ) > 0) endif return lRet -------------- HELPERS ------------------- FUNCTION CreateNewSession(cIpAddr, nExpiry, cUserId) LOCAL nSel := SELECT(), cSID := "" LOCAL lNewRec := .T., nNewRec := 0 LOCAL cNow := DTOS(DATE()) + STRTRAN(TIME(), ":", "" ) DEFAULT cIpAddr TO "" DEFAULT nExpiry TO EXPIRE_MINUTES DEFAULT cUserID TO "" SELECT SESSION IF FilLock(10) cSID := RandomPass(64) Falls Session-ID bereits vorhanden, solange versuchen, bis eine neue herauskommt. DO WHILE dbSeek( cSID, .F. , "KEY" ) cSID := RandomPass(64) ENDDO /* Session-Datensatz recyclen */ IF LASTREC() != 0 OrdSetFocus("EXPIRES") GO TOP IF ! EOF() .AND. SESSION->expires < cNow IF RecLock(5) lNewRec := .F. nNewRec := RECNO() ENDIF ENDIF OrdSetFocus("KEY") ENDIF IF lNewRec APPEND BLANK ELSE dbGoto(nNewRec) ENDIF SESSION->KEY := cSID SESSION->EXPIRES := CalcExpiration( nExpiry ) SESSION->ipaddr := cIpAddr SESSION->userid := cUserID SESSION->data := "" SESSION->loggedin := TRUE dbUnlock() ENDIF SELECT (nSel) RETURN cSID FUNCTION CalcExpiration(nMinutes) LOCAL cExpiration, nExpire LOCAL dExpire := DATE() nExpire := SECONDS() + nMinutes * 60 Ablaufzeit Expiration-Zeit bei jedem Zugriff neu berechnen, Tageswechsel berücksichtigen IF nExpire >= 86400 24 Stunden dExpire := dExpire + 1 nExpire := nExpire - 86400 ENDIF nExpire := ROUND(nExpire / 60,0) cExpiration := DTOS(dExpire) + STRZERO(INT(nExpire/60),2,0) + STRZERO(nExpire%60,2,0) RETURN cExpiration FUNCTION DeleteSession(cSid) LOCAL nSel := SELECT(), lRet := TRUE LOCAL cNow := DTOS(DATE()) + "0000" SELECT SESSION Session vorhanden? IF dbSeek( cSID, .F. , "KEY" ) Logout IF RecLock(5) SESSION->key := "" SESSION->userid := "" SESSION->loggedin := FALSE SESSION->ipaddr := "" SESSION->data := "" SESSION->expires := cNow dbrUnlock() ENDIF ENDIF SELECT (nSel) RETURN lRet FUNCTION SessionExpired(cSid, oCon) LOCAL nSel := SELECT(), lRet := TRUE LOCAL cNow := DTOS(DATE()) + SUBSTR(STRTRAN(TIME(), ":", "" ),1,4) SELECT SESSION Session vorhanden? IF dbSeek( cSID, .F. , "KEY" ) lRet := SESSION->expires < cNow .OR. !SESSION->loggedin .OR. !(TRIM(oCon:getRemoteAddr()) = TRIM(SESSION->ipaddr)) IF !lRet EVTUSER->(dbSeek(SESSION->userid,.F.,"ID")) Wenn OK, Session verlängern IF RecLock(5) SESSION->EXPIRES := CalcExpiration( EXPIRE_MINUTES ) dbrUnlock() ENDIF ELSE Logout DeleteSession(cSid, oCon) ENDIF ENDIF SELECT (nSel) RETURN lRet FUNCTION RandomPass(nLen, cFormat) LOCAL nI, cChar, nZahl := 64 LOCAL cRet := "" DEFAULT cFormat TO "" FOR nI := 1 TO nLen DO WHILE nZahl >= 58 .AND. nZahl <= 64 nZahl := Random(42)+48 /* Wegen Verwechslungsgefahr 1iI 0Oo entfernen */ IF nZahl == 48 .OR. nZahl == 49 .OR. nZahl == 73 .OR.; nZahl == 79 .OR. nZahl == 105 .OR. nZahl == 111 nZahl := 64 ENDIF ENDDO cChar := CHR(nZahl) IF Random(1)=0 cChar := LOWER(cChar) ENDIF cRet += cChar nZahl := 64 NEXT IF ! EMPTY(cFormat) cRet := Transform( cRet , cFormat ) ENDIF RETURN cRet | |
Allen Lee | Re: WAA session manager on Thu, 14 Apr 2016 11:06:47 -0700 Thank you very much, Thomas. I know this is going help a great deal. I'm going to begin testing an implementation based on your model. Your sharing is greatly appreciated. If you vacation in Vancouver, Canada then I invite you stay as a guest in my home. On 4/14/2016 8:41 AM, Thomas Braun wrote: > Allen Lee wrote: > >> By session keys, do you mean that you store parameters in an array via > > No, the session key is a unique identifier which is part of each page > submitted: > > <input type="hidden" value="6VkLK5Hs4txCrMxc2Y3fKanDZQCLMKe9Z6A" name="SID"> > > If not part of the page, a new one is created and then stored in a > "session.dbf" file at the first contact/login of the user: > > {"KEY", "C",64,0},; > {"EXPIRES","C",12,0},; > {"USERID","C",3,0},; > {"DATA","M",4,0} } > > "DATA" holds the cargo contents. > > This is the code I use to replace setCargo/getCargo etc., maybe you can use > parts of it: > > "base" is the base class for my own HTML3 class... at the bottom are some > helper function you will need as well...hope this helps > > Thomas > > METHOD Base:openSession(nExpireMinutes, cUserId) > local cCookie := ::GetVar("SID") > local nArea := Select() > local lFound := FALSE > local cAlias := "SESSION" > LOCAL cIpAddr := "" > > DEFAULT nExpireMinutes TO 60 > DEFAULT cUserID TO "" > > IF ::context != NIL > cIpAddr := ::context:getRemoteAddr() > ENDIF > > if !Empty(cCookie) > lFound := (cAlias)->(dbSeek(cCookie,.f.,"KEY")) > ELSE > cCookie := CreateNewSession( cIpAddr, nExpireMinutes, cUserId) > endif > > if lFound .and. !Empty((cAlias)->DATA) > ::cargoData := Bin2Var((cAlias)->DATA) > else > ::cargoData := {} > endif > > ::SessionID := cCookie > > dbSelectArea(nArea) > return lFound > > METHOD Base:CloseSession() > > if !Empty(::SessionID) > DeleteSession(::SessionID) > endif > > ::SessionID := "" > > return NIL > > > METHOD Base:setCargo(x1,x2) > local cCookie := ::GetVar("SID") > local lRet := FALSE > local nArea := Select() > local cAlias := "SESSION" > local i := 0 > > IF EMPTY(cCookie) > cCookie := ::SessionID > ENDIF > > if !(x1 == NIL) > if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) > if (cAlias)->(RecLock()) > if x2 == NIL > ::cargoData := x1 > else > x1 := ALLTRIM(x1) > if !ValType(::cargoData) == 'A' > ::cargoData := {} > else > i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } > ) > endif > if i == 0 > aadd(::cargoData, {x1,x2}) > else > ::cargoData[i][2] := x2 > endif > endif > (cAlias)->DATA := Var2Bin(::cargoData) > (cAlias)->(dbrUnlock()) > lRet := TRUE > endif > endif > endif > dbSelectArea(nArea) > return lRet > > > METHOD Base:delCargo(x1) > local cCookie := ::GetVar("SID") > local lRet := FALSE > local nArea := Select() > local cAlias := "SESSION" > local i := 0 > > IF EMPTY(cCookie) > cCookie := ::SessionID > ENDIF > > if !(x1 == NIL) > if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) > if (cAlias)->(RecLock()) > x1 := ALLTRIM(x1) > if !ValType(::cargoData) == 'A' > ::cargoData := {} > else > i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } ) > endif > if i != 0 > AREMOVE(::cargoData, i, 1) > endif > (cAlias)->DATA := Var2Bin(::cargoData) > (cAlias)->(dbrUnlock()) > lRet := TRUE > endif > endif > endif > dbSelectArea(nArea) > return lRet > > METHOD Base:ClearCargo() > local cCookie := ::GetVar("SID") > local lRet := FALSE > local nArea := Select() > local cAlias := "SESSION" > > IF EMPTY(cCookie) > cCookie := ::SessionID > ENDIF > > if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) > ::cargoData := {} > if (cAlias)->(RecLock()) > (cAlias)->DATA := Var2Bin(::cargoData) > (cAlias)->(dbrUnlock()) > lRet := TRUE > endif > ENDIF > > dbSelectArea(nArea) > return lRet > > > METHOD Base:setAllCargo() > local cCookie := ::GetVar("SID") > local lRet := FALSE > local nArea := Select() > local cAlias := "SESSION" > local j:= 0, i := 0 > LOCAL aVars := ::GetAllVars(), x1, x2 > > IF EMPTY(cCookie) > cCookie := ::SessionID > ENDIF > > if !Empty(cCookie) .and. (cAlias)->(dbSeek(cCookie)) > FOR j := 1 TO LEN(aVars) > x1 := aVars[j,1] > x2 := aVars[j,2,1] > if !(x1 == NIL) > x1 := ALLTRIM(x1) > if !ValType(::cargoData) == 'A' > ::cargoData := {} > else > i := AScan( ::cargoData, {|e| lower(e[1]) == lower(x1) } ) > endif > if i == 0 > aadd(::cargoData, {x1,x2}) > else > ::cargoData[i][2] := x2 > endif > endif > NEXT > > if (cAlias)->(RecLock()) > (cAlias)->DATA := Var2Bin(::cargoData) > (cAlias)->(dbrUnlock()) > lRet := TRUE > endif > ENDIF > > dbSelectArea(nArea) > return lRet > > > METHOD Base:getCargo(cName) > local xRet > local i > > if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A' > cName := ALLTRIM(cName) > if (i := AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } )) > 0 > xRet := ::cargoData[i][2] > endif > elseif cName == NIL > xRet := ::cargoData > endif > > return xRet > > METHOD Base:getAllCargo() > return ::cargoData > > METHOD Base:IsCargo(cName) > local lRet := FALSE > > if Valtype(cName) == 'C' .and. ValType(::cargoData) == 'A' > lRet := (AScan(::cargoData,{|e| lower(e[1]) == lower(cName) } ) > 0) > endif > return lRet > > -------------- HELPERS ------------------- > > > FUNCTION CreateNewSession(cIpAddr, nExpiry, cUserId) > LOCAL nSel := SELECT(), cSID := "" > LOCAL lNewRec := .T., nNewRec := 0 > LOCAL cNow := DTOS(DATE()) + STRTRAN(TIME(), ":", "" ) > > DEFAULT cIpAddr TO "" > DEFAULT nExpiry TO EXPIRE_MINUTES > DEFAULT cUserID TO "" > > SELECT SESSION > IF FilLock(10) > cSID := RandomPass(64) > > Falls Session-ID bereits vorhanden, solange versuchen, bis > eine neue herauskommt. > DO WHILE dbSeek( cSID, .F. , "KEY" ) > cSID := RandomPass(64) > ENDDO > > /* Session-Datensatz recyclen */ > IF LASTREC() != 0 > OrdSetFocus("EXPIRES") > GO TOP > IF ! EOF() .AND. SESSION->expires < cNow > IF RecLock(5) > lNewRec := .F. > nNewRec := RECNO() > ENDIF > ENDIF > OrdSetFocus("KEY") > ENDIF > > IF lNewRec > APPEND BLANK > ELSE > dbGoto(nNewRec) > ENDIF > > SESSION->KEY := cSID > SESSION->EXPIRES := CalcExpiration( nExpiry ) > SESSION->ipaddr := cIpAddr > SESSION->userid := cUserID > SESSION->data := "" > SESSION->loggedin := TRUE > dbUnlock() > > ENDIF > > SELECT (nSel) > > RETURN cSID > > FUNCTION CalcExpiration(nMinutes) > LOCAL cExpiration, nExpire > LOCAL dExpire := DATE() > > nExpire := SECONDS() + nMinutes * 60 Ablaufzeit > > Expiration-Zeit bei jedem Zugriff neu berechnen, Tageswechsel > berücksichtigen > IF nExpire >= 86400 24 Stunden > dExpire := dExpire + 1 > nExpire := nExpire - 86400 > ENDIF > > nExpire := ROUND(nExpire / 60,0) > > cExpiration := DTOS(dExpire) + STRZERO(INT(nExpire/60),2,0) + > STRZERO(nExpire%60,2,0) > > RETURN cExpiration > > FUNCTION DeleteSession(cSid) > LOCAL nSel := SELECT(), lRet := TRUE > LOCAL cNow := DTOS(DATE()) + "0000" > > SELECT SESSION > > Session vorhanden? > IF dbSeek( cSID, .F. , "KEY" ) > > Logout > IF RecLock(5) > SESSION->key := "" > SESSION->userid := "" > SESSION->loggedin := FALSE > SESSION->ipaddr := "" > SESSION->data := "" > SESSION->expires := cNow > dbrUnlock() > ENDIF > > ENDIF > > SELECT (nSel) > > RETURN lRet > > FUNCTION SessionExpired(cSid, oCon) > LOCAL nSel := SELECT(), lRet := TRUE > LOCAL cNow := DTOS(DATE()) + SUBSTR(STRTRAN(TIME(), ":", "" ),1,4) > > SELECT SESSION > > Session vorhanden? > IF dbSeek( cSID, .F. , "KEY" ) > > lRet := SESSION->expires < cNow .OR. !SESSION->loggedin .OR. > !(TRIM(oCon:getRemoteAddr()) = TRIM(SESSION->ipaddr)) > > IF !lRet > EVTUSER->(dbSeek(SESSION->userid,.F.,"ID")) > > Wenn OK, Session verlängern > IF RecLock(5) > SESSION->EXPIRES := CalcExpiration( EXPIRE_MINUTES ) > dbrUnlock() > ENDIF > ELSE > Logout > DeleteSession(cSid, oCon) > ENDIF > > ENDIF > > SELECT (nSel) > > RETURN lRet > > FUNCTION RandomPass(nLen, cFormat) > LOCAL nI, cChar, nZahl := 64 > LOCAL cRet := "" > > DEFAULT cFormat TO "" > > FOR nI := 1 TO nLen > > DO WHILE nZahl >= 58 .AND. nZahl <= 64 > nZahl := Random(42)+48 > /* > Wegen Verwechslungsgefahr 1iI 0Oo entfernen > */ > IF nZahl == 48 .OR. nZahl == 49 .OR. nZahl == 73 .OR.; > nZahl == 79 .OR. nZahl == 105 .OR. nZahl == 111 > > nZahl := 64 > > ENDIF > ENDDO > cChar := CHR(nZahl) > IF Random(1)=0 > cChar := LOWER(cChar) > ENDIF > cRet += cChar > nZahl := 64 > > NEXT > > IF ! EMPTY(cFormat) > cRet := Transform( cRet , cFormat ) > ENDIF > > RETURN cRet > |