Get the current session with spring-session - spring-session

Here's my question: I'm writing a platform which I will be giving to the customers to implement their projects with. So in my platform I have created a SessionService in which I have methods like getCurrentSession, getAttribute, setAttribute, etc. Before spring-session my getCurrentMethod looked like this:
#Override
public HttpSession getCurrentSession() {
if (this.session == null) {
final ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
return attr.getRequest().getSession(true); // true == allow create
}
return this.session;
}
which worked perfectly fine, although it looks ugly and have no backing like redis. Now I want to migrate to spring-session and I was hoping to use the SessionRepository to find the current session of the user, however I can only see a getSession(String id) in there. I believe the id is stored in the cookie, so to use it I will probably have to pass the HttpServletRequest object from my controllers, to my facades, to the service layer which is very near the db layer. This looks like a very bad idea to me, so my question would be: is there any way to get the currentSession near the db layer? One way I would think is to write an interceptor that will be invoked the controllers, which will set the current session in the repository, or the service maybe? I'm just not sure this is the right way to go.

Obtaining the Session Id from Service Layer
You can use the RequestContextHolder to retrieve the session id, set attributes, and remove attributes.
The RequestContextHolder is typically setup using RequestContextListener or RequestContextFilter. Spring Session does NOT work with RequestContextListener because there is no way for Spring Session to wrap the request before the RequestContextListener is invoked.
Unfortunately, this means for Spring Boot applications, RequestContextHolder does not work out of the box. To work around it you can create a RequestContextFilter Bean. See spring-boot/gh-2637 for updates on this issue.
Should I be putting this in session?
Just because it is easy to put a lot of objects in session and it is stored in Redis does not mean it is the right thing to do.
Keep in mind that the entire session is retrieved on every request. So while Redis is fast, this can have a significant impact if there are lots of objects in session. Obviously the implementation can be optimized for your situation, but I think the concept of session generally holds this property.
A general rule of thumb is, "Do I need this object for over 95% of my requests?" (read this as almost all of my requests). If so, it may be a candidate for session. In most cases, the object should be security related if it fits this criteria.
Should I access session id from ThreadLocal in the service layer?
This is certainly open for debate as code is as much of an art as it is a science.
However, I'd argue that you should not be obtaining the session id from thread locale variables throughout your architecture. Doing this feels a bit like obtaining a "Person id" and obtaining the current "Person id" from the HttpServletRequest in a ThreadLocale. Instead, values should be obtained from the controller and passed into your service layer.

Your code does not need changing. It will return the Spring Session session object.
Though it is generally better to inject the HttpSession from the controller, or use session-scoped beans and #SessionAttribute than to have such a session service in the first place.

Related

Where does the Grails Spring Security plugin save currentUser in HttpSession

it is famous to get the current user by calling :
springSecurityService.currentUser ;
Does Spring Ssecurity API save this object in HttpSession. if So, how to access to this object from session .
i.e: session['currentUser']
It doesn't.
As you showed in your answer the Principal is stored in the session, but that's the org.springframework.security.core.userdetails.UserDetails instance that was created by the org.springframework.security.core.userdetails.UserDetailsService. The default implementation of that in the plugin is grails.plugin.springsecurity.userdetails.GrailsUser but that's easily customized.
The UserDetails instance is typically a lightweight object, just containing the username and hashed password, a few locked/enabled booleans, and a collection of GrantedAuthority instances to store role names. I often recommend that users extend this to also contain data that's useful but unlikely to change during a login session, e.g. full name, to avoid going to the database to retrieve it. Since the UserDetails is stored in the session and easily accessible via springSecurityService.principal it's a great place to store data like this.
But it is not the same thing as what's returned from getCurrentUser()/currentUser - this is the GORM user/person domain class that was loaded by the UserDetailsService to create the UserDetails instance. It can have a lot more data associated with it, lazy-loaded hasMany collections, etc. It's often a rather large object that should not be stored in the session. Doing so does make its data conveniently available, but will affect scalability because you waste server memory and limit the number of concurrent sessions a server can have. And it's a disconnected Hibernate object, so to use it for most persistence-related actions requires that you reload the instance anyway, often with merge(). That loads the whole thing from the database, so it's a lot more efficient to store the extra data you need in the UserDetails, along with the id of the instance so you can easily retrieve the instance as needed. That's what getCurrentUser()/currentUser does - it uses the id if it's available for a get() call, or the username for the equivalent of a findByUsername() call (which should be around the same cost if the username has a unique index).
After HttpSession inspecting session.getAttributeNames(), i want to share my result :
session.getAttribute('SPRING_SECURITY_CONTEXT').authentication.principal

Using Spring Security ACL, how to secure a method that is passed a "String" key?

I understand how to apply Spring security annotations to methods that are passed the domain objects. For example, the following works:
#PreAuthorize("hasPermission(#node, 'write')")
void update(Node node);
How ever, I have another method shown below that I am trying to secure:
void delete(String nodeName)
Since domain object is not available to this method, I am not sure how to use "hasPermission" with this one. Any help is greatly appreciated. I am open to customizing the ACL implementation though in this particular case, any such customization should work with not just "Node" object but all other domain objects as well.
Thanks,
Raghu
In this case, you're deleting by name as opposed to by identity. The way you do that is you look up the object you wish to delete and then, once you have it, pass it to a real deletion method that follows the pattern of the first operation in your question. The check whether the object can be deleted by the user is done after lookup. You get a pattern a bit like this:
void delete(String name) {
Node node = nodeFinder.lookup(name);
if (node == null)
throw new NoSuchNodeException(name);
underlyingEngine.delete(node);
}
The nodeFinder and (especially) underlyingEngine should be injected beans so that Spring can wrap them with the required security checks. (The nodeFinder checks should be whether the user is allowed to know about the named node at all, the underlyingEngine should focus on whether the particular operation — deletion in this case — is permitted.)
Note that you can also apply the #PreAuthorize directly to the Node (provided you decide to make them into beans) through the use of #this in the expression.

Passing parameters between Request Scoped Managed Beans in JSF + EJB 3.1

Our problem is a very basic, simple implementation of editing the database using JSF + EJB.
Keeping things short:
two separate XHTML views, use two separate Managed Beans #RequestScope.
WebuserListBean and EditWebuserBean, and with #ManagedProperty we inject WebuserListBean, so we could obtain selected user data. So far no problems. Views are filled with data succesfully!
BUT!
We want to be able to edit the user!
And here (to my surprise) we cannot overcome the problem.
1st try:
Because the request scoped bean is dead after filling the view, on Save() method the #PostConstruct tries to launch again, of course it can't. So we couldn't even obtain it from database or anything.
2nd try:
Because the request scoped bean is dead after filling the view, if we do not set up user as field in #postconstruct, we lose our connection with user object which was linked on previous view (and injected, but now that view is dead too).
3rd try:
Cannot inject RequestScope in ViewScoped
Ok and our restrictions, because we think it's wrong way:
We dont want to create a SessionScoped Managed Bean for this
We dont want to use any params etc. We want to use EJB
We are not sure if we could store data in Stateful session bean which is our
endpoint for the module? Is it proper approach?
Thanks for any advice, we could paste some code but i guess it is pointless!
Cheers!
There are many ways to do it, but I recommend using the flash if the pages involved in the navigation are in the same folder (I recently found out reading a BalusC answer that there is a known issue with the flash, in which it will not hold values when navigating between pages in different folders!).
The flash is a way to hold parameters for a little longer than the context of a single request (concept taken from Ruby if I'm not mistaken, someone correct me if I'm wrong), allowing for the sent parameters to be fetched in a subsequent view, for example. Those values are discarded in the second request issued after saving them, if I'm not mistaken. You can inject the flash in your managed beans like this:
#ManagedProperty("#{flash}")
private Flash flash;
public void setFlash(Flash newFlash) {
flash = newFlash;
}
Then, you access it like a map with the put and get methods. If you use the put method in a bean, return a redirection rule and, on the second bean, use the get method your object should be there.
You can also find a highly comprehensible guide of communication in JSF (listing a really extensive list of options) here, in particular if you need to navigate between pages in different folders.

Persisting/caching data between requests - common approach

I'm developing an Asp.net (MVC but this doesn't really matter) application. I have a custom IHttpModule that's responsible for the PostAuthenticateRequest to change user principal & identity.
I'm storing UserID and UserName in authentication cookie when user logs-in. I have an IUser (implemented by DAO and Business Objects layer, each with their own additional members) that I need all over Business Service classes. When a user wants anything I have to provide IUser object instance (usually from Business Objects layer) so providing ID from the auth ticket isn't sufficient.
So I'm thinking of how and where would be best to persist logged in user's IUser data?
I don't want to fetch it every time from the DB (based on authentication ticket's UserID data)
I can't store it in Session since I have to work inside PostAuthenticateRequest, where Session isn't ready yet
I want all the functionality to be encapsulated within my custom IHttpModule
Choices that I see:
Cache
Cookie
(Session) - by moving from PostAuthenticateRequest to PostAcquireRequestState event and change principal/identity there, but I'd like to avoid this
Processes that seem to complicate things are:
User logs-in, user data is fetched from the DB and persisted somehow for later requests
User logs-out, user data has to be removed from persisted medium automagically
User changes own profile, user data has to be discarded and reread on next request from the DB
I wan't all these to be handled automatically by HttpModule (if possible) to eliminate developer's errors of forgetting to reset these things.
What I also don't want is to write/read some hardcoded variables/keys and manipulate them in other parts of the application. This would only present technical debt.
Questions
What would you suggest?
How does SO persist user data between requests?
Given your requirements, I suppose the best solution is to retrieve the ID from the cookie and use it to index into the Http Cache (HttpContext.Current.Cache).
If you want to maintain how users access it, wrap the Cache in a "UserCache" object. The object could be constructed by an HttpModule and stored as a (wait for it...) singleton within the cache itself or, better yet, just constructed when needed to pull from the http cache. This would depend on where you need to access it and whether HttpContext.Current.Cache is directly available. The lazy implementation is below.
Again, this is for clarity and is not how I'd actually implement it.
public class UserCache
{
public IUser GetUser(object userKey)
{
return HttpContext.Current.Cache[userKey];
}
public void AddUser(object userKey, IUser user)
{
/* this could pull the key from the user object as well. */
HttpContext.Current.Cache.Add(/* add the object with key and a sliding expiration that is slightly greater than session timeout */);
}
public void ExpireUser(object userKey)
{
HttpContext.Current.Cache.Remove(userKey);
}
/* If you don't want to do SQL cache dependency */
public void UpdateUser(object userKey, IUser user)
{
HttpContext.Current.Cache.Insert(/* ... */);
}
}
Using the default caching mechanisms (or better yet a caching mechanism supplied by DI so you're not tied to an implementation), you can set an expiration to automatically remove users from the cache as mentioned in the comment. You can setup the cache to be dependent on SQL server updates as well to handle the updates or manually update it as part of the service to save changes.
More information about the default cache is available here. More information about cache dependencies is available here.
In the HttpModule itself, I suppose you could do some magic in the EndRequest event to see if the request is authenticated and then log the user out based on the cookie, but I'm not sure if that would work as I've never tried it. You might want to have a look at this article on MSDN from WAY back in the 1.1 days and see if it answers some of the problems you are trying to solve.
As for the SO architecture and how they do it, I'd imagine they load it when needed because they keep most of the database in RAM at all times (http://highscalability.com/stack-overflow-architecture).

DataContextProvider

Going from the example given here...
http://ericswann.org/blog/archive/2009/04/06/linq-to-sql-datacontext-provider-revisited.aspx
I'm trying to use the datacontext between the MembershipProvider and the RoleProvider.
For instance, when I call Membership.GetUser(XXXXXX) in the RoleProvider, I get an error because it pulls an item from one datacontext and tries to use it (hence the need for the repository)
But I am ...really stupid, and this is some pretty advanced stuff. Seeing as how I do not get to 'instantiate' the Providers, does anyone have an idea how I might go about using this?
First of all pick your datacontext caching strategy. Use DataContextThreadCache if you're working in winforms, where the context is cached in your running thread context. Use DataContextWebCache if you're working with web apps where the context is cached in HTTP run-time cache.
To register your datacontext look at DataContextProvider.RegisterDataContext and its overloads. Here is an example:
DataContextProvider.RegisterDataContext<YourDataContextType, YourCacheStrategyType>(contextKey, contextConnectionString)
contextKey: is the key you will use to retrieve your data context with.
contextConnectionString: is the connection string for your data context.
This call will use the type of DataContext you specified to create an new instance of it. It will then cache it using the specific caching strategy you specified also. This should probably be called somewhere during the initialization phase of your application. Your repository can then use GetDataContext as outlined in that blog post to get the context instance.

Resources