You can store all relevant session data at the client, or you can store that data on the server. There are tradeoffs both ways:
On the client, you lighten server load. This can be an issue for heavily trafficed applications. It's also a potential security problem if someone learns how to edit their cookies. In a closed environment (Intranet) this is usually not such a problem.
You can also store that data server-side. Either in a flat file or a database... flat-files are extremely easy (Just name the file with the session id you toss to the client in a cookie) and flexible - it's easy to add data to them. Databases scale up to better load, but require more planning and architecture. They're also less flexible.
I prefer the second solution. This is pseudocode, something I use for "flowcharting" during the development process:
get sessionId from browser;
if (no sessionId)
create session id;
hand to browser;
put relevant data in store (username, password, browser, etc)
get relevant data from datastore;
Pretty simple. Without knowing what kind of data you're needing to be persistent, that's about the best I can do. One other library you may be interested in is CGI::Application. It uses HTML::Template to separate code from the presentation layer (HTML), which may be a different paradigm than you're looking for, but it helps you maintain state between pages.
You can roll your own with DBI (and your favorite DBD::), Digest::MD5 (to generate your session ids... just one way to do it), and Data::Serializer. I use these together in a custom session object that follows the same flow as you have.
create and store session id with browser(through cookie, url, or hidden form fields)
use session id as map to key/identity in your db store
serialize your data structure with Data::Serializer right into the db
If you can't map the session id, assume its a new session and regen a new session id. You should track your session data by last access time (so your write/commit method might update a timestamp for example).
With Data::Serializer, you can get fancy and have deep nested hashrefs and blessed objects. Be careful though that your blessed objects are refreshed when thawed. Otherwise you will have a good long time debugging stale data. =]