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
Related
Got a really simple question. I'm doing the railstutorial by Michael Hartl and it talks about using the session method:
Logging a user in is simple with the help of the session method defined by Rails... We can treat session as if it were a hash, and assign to it as follows:
session[:user_id] = user.id
It says you can treat session as if it were a hash, but I'm confused because it is called the session method, so is anything actually being called? My guess is that by inserting into the session hash, there is a session function that looks into the hash to see if there is anything present? I'm not really sure how it works.
Would be rude not to mention the Session documentation:
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
Basically, each time someone visits your Rails app, it will create a small cookie in their browser, identifiable by a unique ID (not user ID).
This cookie is essentially a Ruby hash, hence why you can store hashed data inside it:
session[:your_hash] = "TEST"
This will allow you to store small snippets of data (such as user_id or others) through requests.
The main reason Rails has this is down to HTTP being a stateless protocol.
Stateless protocols are contrary to stateful protocols; they don't retain the state between requests, thus you have to reinvoke data, etc, each time a new instance of the application is accessed.
Simply, this translates into Rails being a "dumb" system - only remembering data you send it each request. Session variables have been used by developers for decades to provide base information about users / preferences etc, allowing you to "rebuild" a user with each request.
This is why you have to save the user_id as a session - each time you wish to reference a logged-in user's data, it has to be built from that id stored in the sessions hash.
It is a method that returns an object which supports (some of) the same methods a Hash object supports, such as [] and []=. Actually, it is an ActionDispatch::Request::Session object.
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.
With Grails there are several ways to do the same thing.
Finds all of domain class instances:
Book.findAll()
Book.getAll()
Book.list()
Retrieves an instance of the domain class for the specified id:
Book.findById(1)
Book.get(1)
When do you use each one? Are there significant differences in performance?
getAll is an enhanced version of get that takes multiple ids and returns a List of instances. The list size will be the same as the number of provided ids; any misses will result in a null at that slot. See http://grails.org/doc/latest/ref/Domain%20Classes/getAll.html
findAll lets you use HQL queries and supports pagination, but they're not limited to instances of the calling class so I use executeQuery instead. See http://grails.org/doc/latest/ref/Domain%20Classes/findAll.html
list finds all instances and supports pagination. See http://grails.org/doc/latest/ref/Domain%20Classes/list.html
get retrieves a single instance by id. It uses the instance cache, so multiple calls within the same Hibernate session will result in at most one database call (e.g. if the instance is in the 2nd-level cache and you've enabled it).
findById is a dynamic finder, like findByName, findByFoo, etc. As such it does not use the instance cache, but can be cached if you have query caching enabled (typically not a good idea). get should be preferred since its caching is a lot smarter; cached query results (even for a single instance like this) are pessimistically cleared more often than you would expect, but the instance cache doesn't need to be so pessimistic.
The one use case I would have for findById is as a security-related check, combined with another property. For example instead of retrieving a CreditCard instance using CreditCard.get(cardId), I'd find the currently logged-in user and use CreditCard.findByIdAndUser(cardId, user). This assumes that CreditCard has a User user property. That way both properties have to match, and this would block a hacker from accessing the card instance since the card id might match, but the user wouldn't.
Another difference between Domain.findByID(id) and Domain.get(id) is that if you're using a hibernate filter, you need to use Domain.findById(id). Domain.get(id) bypasses the filter.
AFAIK, these are all identical
Book.findAll()
Book.getAll()
Book.list()
These will return the same results
Book.findById(1)
Book.get(1)
but get(id) will use the cache (if enabled), so should be preferred to findById(1)
In fairly new to MVC and I would like to use a session. I have a base controller and all my other controller inherit from my base. I need the session checked every time a page is hit.
What is the best way to go about this?
Updated
My session will need to store an id to be able to build the pages correctly. If the session doesn't have the ID I need to look up the information in DB. I don't want to use cache because IDs could be different for different users.
I recommend going the cache route.
Create a class called 'CacheHelper' and within it, a method called 'GetId()'
In the GetId() method, setup a Dictionary object to store your values and use the username as the key.
Each time you call GetId, check to see if the Key exists in your dictionary
myDictionary.ContainsKey(username);
If not, look it up in the database, add it to the dictionary, then resave it to cache.
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).