I have implemented a custom AbstractPreAuthenticatedProcessingFilter which returns a Principal (actually an Object as per the signature) in the method getPreAuthenticatedPrincipal(HttpServletRequest).
I am custom-implementing a UserDetailsService which needs to access the Principal somehow. I have tried using SecurityContextHolder.getContext().getAuthentication().getPrincipal(), which is throwing a NullPointerException since getAuthentication() is null.
How do I access the Principal otherwise?
The reason it is null is that spring security was not able to identify the principal up to that point. Extract from the javadoc of AbstractPreAuthenticatedProcessingFilter:
Base class for processing filters that handle pre-authenticated
authentication requests, where it is assumed that the principal has
already been authenticated by an external system.
UserDetailService is responsible for loading the user object. Then, after successful authentication, principal will be set containing the given user object.
Instead of org.springframework.security.core.userdetails.UserDetailsService, I implemented org.springframework.security.core.userdetails.AuthenticationUserDetailsService<Authentication>, which was the actual necessity for AbstractPreAuthenticatedProcessingFilter. The loadUserDetails() method in this gets the Authentication object as a parameter and hence my issue is resolved.
Related
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.
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
Is there ever a case for:
def user = User.get(springSecurityService.principal.id)
over
def user = springSecurityService.currentUser
All I can think of is preventing lazy inits or ensuring data you are currently operating on is not stale?
In practical terms, I don't see much difference between these two. I would be inclined to use
def user = springSecurityService.currentUser
Because it's slightly shorter that the other form, it's what the plugin docs recommend, and there might be some additional caching of the user within plugin (beyond the caching already provided by Hibernate).
Well, there is a slight difference between the two. The documentation points this out.
currentUser will always return the domain instance of the currently logged in user.
principal on the other hand, retrieves the currently logged in user's Principal. If authenticated, the principal will be a grails.plugin.springsecurity.userdetails.GrailsUser, unless you have created a custom UserDetailsService, in which case it will be whatever implementation of UserDetails you use there.
If not authenticated and the AnonymousAuthenticationFilter is active (true by default) then a standard org.springframework.security.core.userdetails.User is used.
Hope that helps clear things up.
We just encountered a case where code was using currentUser and failing because there was no User record for the User domain. In our case, principal.username worked because we had a custom UserDetailsService that was creating a GrailsUser on the fly if one didn't exist in the User table.
So the distinction is important.
I'm using spring-security 3.1.
I have to implement session concurrency strategy in a way that the maximum number of sessions is specified by user. Here is what I did :
Coded a class extending
org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy and overrode the method
protected int getMaximumSessionsForThisUser(Authentication authentication)
I configured it using namespace configuration :
<security:http>
...
<security:session-management session-authentication-strategy-ref="mySessionAuthenticationStrategy"/>
...
</security:http>
<bean id="mySessionAuthenticationStrategy" class="foo.bar.MySessionAuthenticationStrategy">
<constructor-arg ref="sessionRegistry"/>
</bean>
<bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
The problem is that "MySessionAuthenticationStrategy" is never called :(
I digged in spring api to see that the following line(70) in SessionManagementFilter is false (preventing any SessionAuthenticationStrategy to be invoked) :
if (!securityContextRepository.containsContext(request))
Why is that ?
I read the documentation where they suggest to set the session authentication strategy in the UsernamePasswordAuthenticationFilter, but it's not an option for me since I'm combining form login with SAML login plus a PreAuthentication mechanism validating authentication token (3 different authentication mechanisms).
Any of you can help ?
Short answer (which is a guess): The problem could be that your pre-auth filter (or other non-form login filter) creates a session without itself invoking the SessionAuthenticationStrategy first.
Long explanation: The line you mentioned is basically checking whether the request has just been authenticated in the current execution of the filter chain without the auth-filter creating a new session. The check inspects if there is a session, and if an auth object has already been saved to the session.
If it finds the session and the saved auth object, that means nothing has to be done: everything has already been arranged regarding authentication and session management either by some other filter, or by the same SessionManagementFilter during processing a previous request earlier in the same session.
The other case is when no session has been created or the (non-anonymous) auth object has not yet been saved in the existing session. Only in this case is it the SessionManagementFilter's responsibility to actully perform session management by invoking the SessionAuthenticationStrategy.
According to your description, this second case never occurs, which means that the session is already created, and the auth object is already saved at this point of execution. That should mean your custom auth filter must have created a session, which is not a problem in itself. The general rule however is that anyone creating a session must first consult the SessionAuthenticationStrategy itself. If your auth filter chooses to ignore it, nothing can be done by the SessionManagementFilter (it cannot undone the session creation, even if the SessionAuthenticationStrategy had raised a veto against the user's authenticatation).
Doublecheck if this is the case, and try avoid creating a session in your pre-auth filter. Note that session creation can also happen in a sneaky way by SaveToSessionResponseWrapper.saveContext() getting called e.g. upon a redirect.
I have a question regarding using Spring Security to protect against SQL injection. First of all, I know that use prepared statement can protect from any SQL injection. But In my project I want to show that use Spring Security could help to protect or mitigate against this kind of attack. what i did so far, i made connection using JDBC & Spring and I applied Spring Security and every thing is fine. My question is in my project i used two ways to protect against SQL injection. The first one is Santizing user input and the second one is using Spring Security. I could pass malicious input through Sanitizaing and I want to show that the role of spring security. for example, I pass this input:
TV' UNION SELECT credit_no From credit;--
In this case how I can tell Spring security that it doesnot give any users the credit number. By the way, I used method security level. Just I want to give me an easy way to analyze the user input to see If it has access to data which he asked such as credit.
I hope that clear
Well, your question is not 100% clear, and it may vary on your architecture, but pre post annotations can work well to grab user input.
You can create your own permission evaluator and check permission for pre authorization in your methods.
#PostFilter("hasPermission(filterObject, 'customoperation')")
public CreditCard getCreditCard(String userInput) {
//
}
and your hasPermission method (that you've read about in the link above) goes something like:
public boolean hasPermission(Authentication authentication,
Object target, Object permission) {
if ("customoperation".equals(permission)) {
//your logic here, returning true or false, filtering the object
}
return false;
}
You can also extend the expression handler to use custom functions. Check this answer.