This is a helpful thread - thanks for posting the code Jared.

This last bit of code is probably what I would consider a preferred
solution (until we can address this in the framework via SHIRO-266)
because it completely bypasses the default SessionManagement
infrastructure - the sessionDAO is never accessed, nor a session cache
(or overflow to disk involving file IO), nor is there a need for a
session validation thread to start up for orphan sessions (there will
never be an orphan).

The only caveat here is that you're pretty much forced to abandon a
'real' session environment in your application - which is perfectly
fine of course for 100% RESTful applications.  SHIRO-266 will
hopefully address this to allow people to turn on or off this feature
as needed, even at runtime (e.g. REST request -> HttpRequestSession,
MVC request -> normal Session).

Cheers,

-- 
Les Hazlewood
Founder, Katasoft, Inc.
Application Security Products & Professional Apache Shiro Support and Training:
http://www.katasoft.com

On Thu, Feb 17, 2011 at 7:23 AM, Jared Bunting
<[email protected]> wrote:
> Here's the code.  Two pretty simple classes.  The one thing that I
> wasn't sure about was the timeout methods in the session - I'm fairly
> certain that they're irrelevant in this situation, so I've basically not
> implemented them.  Hopefully the wrapping doesn't screw this up too much
> - if there's somewhere better to post this stuff, please let me know.
>
> Thanks,
> Jared
>
> /**
>  * Intended to keep session request-scoped and therefore not persist
> them across multiple requests - a user must login
>  * on each request. This necessarily means that a mechanism like
> form-based authentication isn't viable, but the
>  * intention is primarily for uses in stateless apis.
>  */
> public class HttpRequestSessionManager implements SessionManager
> {
>
>        static final String REQUEST_ATTRIBUTE_KEY = "__SHIRO_REQUEST_SESSION";
>
>        @Override
>        public Session start(SessionContext context) throws 
> AuthorizationException
>        {
>                if (!WebUtils.isHttp(context))
>                {
>                        String msg = "SessionContext must be an HTTP 
> compatible implementation.";
>                        throw new IllegalArgumentException(msg);
>                }
>
>                HttpServletRequest request = WebUtils.getHttpRequest(context);
>
>                String host = getHost(context);
>
>                Session session = createSession(request, host);
>                request.setAttribute(REQUEST_ATTRIBUTE_KEY, session);
>
>                return session;
>        }
>
>        @Override
>        public Session getSession(SessionKey key) throws SessionException
>        {
>                if (!WebUtils.isHttp(key))
>                {
>                        String msg = "SessionKey must be an HTTP compatible 
> implementation.";
>                        throw new IllegalArgumentException(msg);
>                }
>
>                HttpServletRequest request = WebUtils.getHttpRequest(key);
>
>                return (Session) request.getAttribute(REQUEST_ATTRIBUTE_KEY);
>        }
>
>        private String getHost(SessionContext context)
>        {
>                String host = context.getHost();
>                if (host == null)
>                {
>                        ServletRequest request = WebUtils.getRequest(context);
>                        if (request != null)
>                        {
>                                host = request.getRemoteHost();
>                        }
>                }
>                return host;
>
>        }
>
>        protected Session createSession(HttpServletRequest request, String 
> host)
>        {
>                return new HttpServletRequestSession(request, host);
>        }
>
> }
>
> /**
>  * Session that is only tied to an HttpServletRequest. This can be used
> for applications that prefer to remain stateless.
>  */
> public class HttpServletRequestSession implements Session
> {
>        private HttpServletRequest request;
>        private String host;
>        private UUID uuid;
>        private Date start;
>
>        public HttpServletRequestSession(HttpServletRequest request, String 
> host)
>        {
>                this.request = request;
>                this.host = host;
>                this.uuid = UUID.randomUUID();
>                this.start = new Date();
>        }
>
>        @Override
>        public Serializable getId()
>        {
>                return uuid;
>        }
>
>        @Override
>        public Date getStartTimestamp()
>        {
>                return start;
>        }
>
>        @Override
>        public Date getLastAccessTime()
>        {
>                // the user only makes one request that involves this session
>                return start;
>        }
>
>        @Override
>        public long getTimeout() throws InvalidSessionException
>        {
>                return -1;
>        }
>
>        @Override
>        public void setTimeout(long maxIdleTimeInMillis) throws
> InvalidSessionException
>        {
>                // ignore this - the session ends with the request and that's 
> that...
>        }
>
>        @Override
>        public String getHost()
>        {
>                return host;
>        }
>
>        @Override
>        public void touch() throws InvalidSessionException
>        {
>                // do nothing - we don't timeout
>        }
>
>        @Override
>        public void stop() throws InvalidSessionException
>        {
>                // do nothing - i don't have a use case for this and the 
> structure to
> support it, while not huge, adds
>                // significant complexity
>        }
>
>        @SuppressWarnings( { "unchecked" })
>        @Override
>        public Collection<Object> getAttributeKeys() throws 
> InvalidSessionException
>        {
>                return EnumerationUtils.toList(request.getAttributeNames());
>        }
>
>        @Override
>        public Object getAttribute(Object key) throws InvalidSessionException
>        {
>                return request.getAttribute(stringify(key));
>        }
>
>        @Override
>        public void setAttribute(Object key, Object value) throws
> InvalidSessionException
>        {
>                request.setAttribute(stringify(key), value);
>        }
>
>        @Override
>        public Object removeAttribute(Object objectKey) throws
> InvalidSessionException
>        {
>                String key = stringify(objectKey);
>                Object formerValue = request.getAttribute(key);
>                request.removeAttribute(key);
>                return formerValue;
>        }
>
>        private String stringify(Object key)
>        {
>                return key == null ? null : key.toString();
>        }
> }
>
>
>
> On 02/17/2011 09:12 AM, Tamás Cservenák wrote:
>> Hi Jared,
>>
>> yes, that's my conclusion -- at least in REST world -- that Shiro
>> sessions are <em>great</em> benefit for Java systems (from Javadoc
>> ;) and I do want to use them. And what you did intrigues my
>> imagination, since in "my" solution, a lot of moving parts (Http
>> Servlet container, etc) are triggered just for nothing, to achieve
>> exactly what you did: to have session lifespan equal to the
>> request's.
>>
>> This is something Shiro should support IMO.
>>
>> +1 for seeing that piece of code :)
>>
>>
>> Thanks, ~t~
>>
>> On Thu, Feb 17, 2011 at 3:30 PM, Jared Bunting
>> <[email protected]> wrote:
>>> Tamás,
>>>
>>> I've encountered the same problem and approached it in a slightly
>>> different way.  Basically, I created my own implementation of
>>> SessionManager and Session that are backed only by the
>>> HttpServletRequest.  Each session's lifecycle is tied to the
>>> request.
>>>
>>> I'd also be interested in criticism of this approach, or if anyone
>>> is interested in seeing the code I'd be happy to share it.
>>>
>>> Thanks, Jared

Reply via email to