I'm in the process of writing a Spring REST type interface to a database that will retrieve user specific results for various resources.
To hold the user I have a spring @Component annotated bean called CurrentUser as a temporary measure.
@Component
public class CurrentUser {
@Autowired
private UserDAO userDAO;
private String userId;
private String email;
private String notes;
public String getUserId() {
return userId;
}
public void setUserId(String userId) throws ApiException {
User user = userDAO.getUser(userId) // Database call to
if (!user.isValid()) {
throw ApiException(...) // The exception would crash back to the user as a error in the response
}
// Valud user so set these aspects.
this.userId = user.userId;
this.email = user.email;
}
}
This object is initialised in a Spring Interceptor on each call to any method in the API using the following interceptor.
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
@Autowired
private CurrentUser user;
@Autowired
private RequestParameters requestParameters;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ApiException {
user.setUserId(StringUtils.defaultString(request.getParameter("userId"), "defaultUser"));
return true;
}
}
This is only a place-holder to identify the users until proper authentication can be added.
I'm relatively new to Spring, and the reason for this post is to increase my understanding of the thread safety aspects of Spring in situations like this
I've recently discovered that Spring is not automatically thread safe, I may need to give more consideration to the scopes.
What I want to understand is the following:
For the above setup, is there any danger that 1000s of simultaneous requests, would potentially interfere and overwrite each other? e.g. A request for one user would potentially be overwritten with a different user from a separate http request, causing the requestor to receive the wrong data.
What would be the best approach to solving this. (Even though it will be replaced, I have other objects instantiated in similar ways) Options I'm looking at (if this is an issue), is setting prototype scope, or attaching to the request / session directly rather than allowing them their own autowired object.
Any information anyone could give me would be much appreciated, as I'm a fan of getting it right(er) to start with, than dealing with bad assumptions later on.
Answer 1: Yes, and you don't need 1000 requests to get into trouble. 2 requests in parallel are enough.
Answer 2: The main problem here is one of scoping:
Default scope of Spring managed beans is Singleton. That means that only one instance of your CurrentUser exists per Application.
That is obviously not what you want. (Since you have a severe security issue here, with only one CurrentUser instance per application).
Simple Answer:
I would probably use Spring Security ;-)
Even Simpler Answer:
If that is not an option:
Use a Filter instead of a HandlerInterceptor (more direct control over clean up)
Create a Thread Local to store the user (and use a finally in the Filter to clean it up) and set it in the Filter
Create a request scoped Service (use @ScopedProxy, to be able to wire it into Singletons), that accesses the ThreadLocal as a UserService (you will need an interface to make it work easily)
Since by specification each request in a Servlet environment is bound to a thread, and thread locals are inherently thread-safe, you are completely thread safe and it will scale well. The overhead of the scoped proxy is minimal.
(This is only one option, other options could make use of the request scope explicitly or use aspects in a slightly more elegant manner. However, it is a rather simple approach and gets the job done. For more complex needs I would seriously recommend looking into Spring Security).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With