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.
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.
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 am authenticating using LDAP, and everything works fine for users within a specific domain. But I'm having difficulty understanding how I can authenticate users that are under a second domain. My current configuration shown below specifies the first domain in the user-search-base. I removed that parameter, in hopes that it would search all domains, but that didn't work. I also tried specifying the domain as part of the user name when prompted, such as domain\user, but this didn't work either.
<security:authentication-manager alias="authenticationManager">
<security:ldap-authentication-provider
user-search-filter="(samaccountname={0})"
user-search-base="dc=domain,dc=company,dc=com"
user-context-mapper-ref="userContextMapper" >
</security:ldap-authentication-provider>
</security:authentication-manager>
<security:ldap-server
url="ldap://some.url.com:3000"
manager-dn="CN=USER1,OU=FacMgr,OU=FAC,OU=Exchange,dc=domain,dc=company,dc=com"
manager-password="xxxx"/>
Will I need to create a custom search, and if so, can someone provide an example in this context?
It appears as though you're using Active Directory, in which case I wonder why you're not using the more basic ActiveDirectoryLdapAuthenticationProvider class.
At any rate, you should be able to accomplish what you need by extending either LdapAuthenticationProvider or ActiveDirectoryLdapAuthenticationProvider, and passing the appropriate domain to the superclass' method.
Create a constructor which accepts two different LdapAuthenticator objects, and add a second 'try' statement in the doAuthentication method's catch (UsernameNotFoundException notFound) statement (after the check against bad credentials). Use whatever approach you like to get the getAuthenticator method to try the second authenticator if the first one fails.
This approach should work, but if both domains have a username of jsmith, but the user in question resides on the second, you may encounter problems -- that is, this is not a particularly good solution, but it is a solution.
To build this all up properly, use a custom authentication filter (extend the UsernamePasswordAuthenticationFilter), and have the LdapAuthenticationProvider.getAuthenticator() method identify the domain from the value passed by the filter (in your custom login form).
You may need different manager accounts, but this is hopefully enough for you to go on.
The trick to making this work is to use ActiveDirectoryLdapAuthenticationProvider. To do this, just make the following changes:
In resources.groovy:
// Domain 1
ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider,
"mydomain.com",
"ldap://mydomain.com/"
)
// Domain 2
ldapAuthProvider2(ActiveDirectoryLdapAuthenticationProvider,
"mydomain2.com",
"ldap://mydomain2.com/"
)
In Config.groovy:
grails.plugin.springsecurity.providerNames = ['ldapAuthProvider1', 'ldapAuthProvider2']
This is all the code you need. You can pretty much remove all other grails.plugin.springsecurity.ldap.* settings in Config.groovy as they don't apply to this AD setup.
Documentation:
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ldap-active-directory
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.
I have an application that is authenticating against an external server in a filter. In that filter, I'm trying to set a couple of session attributes on the user by using Symfony's setAttribute() method:
$this->getContext()->getUser()->setAttribute( 'myAttribute', 'myValue' );
What I'm finding is that, if I dump $_SESSION immediately after setting the attribute. On the other hand, if I call getAttribute( 'myAttribute' ), I get back exactly what I put in.
All along, I've assumed that reading/writing to user attributes was synonymous with reading/writing to the session, but that seems to be an incorrect assumption. Is there a timing issue? I'm not getting any non-object errors, so it seems that the user is fully initialized.
Where is the disconnect here?
Thanks.
UPDATE
The reason this was happening is because I had some code in myUser::shutdown() that cleared out a bunch of stuff. Because myUser is loosely equivalent to $_SESSION (at least with respect to attributes), I assumed that the shutdown() method would be called at the end of each session. It's not. It seems to get called at the close of each request which is why my attributes never seemed to get set.
Now, though, I'm left wondering whether there's a session closing callback. Anyone know?
Depending on how you are doing your session handling the session storage class has two methods, sessionGC (called on garbage collection) and sessionDestroy.
You can create your own session class extending what ever one you are using currently, we use Doctrine/PDO. The default is sfSessionStorage.
To change what class you use edit factories.yml:
all:
storage:
class: mySuperNewSessionStorageClass