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
