Spring LDAP 3.1 Custom User Mapper - java

I'm developing an application with Spring Security and Spring LDAP.
This is part of my spring-security.xml:
<authentication-manager alias="authenticationManager">
<ldap-authentication-provider
user-search-filter="sAMAccountName={0}"
user-search-base="OU=UK,OU=Domain Objects,dc=test,dc=test1"
group-search-filter="member={0}"
group-search-base="OU=_Groups,OU=UK,OU=Domain Objects,dc=test,dc=test1"
group-role-attribute="cn"
role-prefix="ROLE_">
</ldap-authentication-provider>
</authentication-manager>
<ldap-server url="ldap://host:389/"
manager-dn="managerUser"
manager-password="ManagerPassword" />
Now I need to do some logic on a user attribute. I was wondering if there is a way to get that attribute during the login phase or I need to do a search on LDAP everytime I need that attribute.
Looking online now I'm a bit confused because I found online many ways to get custom attributes: extending the ContextMapper in the DAO or the AbstractContextMapper or extending LdapUserDetailsMapper.
Could you help me to find the correct solution? I think the best way would be to have an object where I can put the attribute I need during the login phase instead of querying the LDAP everytime I need that attribute.
Thanks

I'm not sure exactly what you're asking. What I can tell you is that once you're authenticated via LDAP spring security caches the user details so you won't need to make a call to LDAP with every subsequent request.
This means that any subsequent methods called after the user is logged in can get the user details like so:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetails ldapUser = (UserDetails) authentication.getPrincipal();
Or if you prefer you can cast it to a custom class you've created as long as you implement UserDetails.
public class MyUser implements UserDetails {
.....
}
Does this help at all?

Related

How do I implement a custom UserDetails object when authenticating against Active Directory via LDAP in Spring Security?

As in my previous questions, I continue to wade through the murky waters of integrating Spring Security into an existing application, which must ultimately authenticate against Active Directory via LDAP (over TLS), and populate the user's permissions based either on group membership or on a string stored in a custom AD field on the user object.
My current problem is that while I have successfully managed all of the above, I have not managed to get the User object with my custom permissions stored onto the session. This means that without additional work, every user will enjoy the permissions available to the most recently logged-in user.
I've worked around this issue by storing user permissions in a Hashtable<username, permissions> object, which I can secure by annotating its getters/setters with #PreAuthorize("isAuthenticated() and principal.name == #username"), but this is clearly not ideal. I'd much rather store the custom information gleaned from AD into the session as part of a custom user object. But how?
The relevant portions of my security.xml and ldap.xml configurations are as follows, respectively:
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthenticationProvider"/>
</security:authentication-manager>
and
<bean id="ldapAuthenticationProvider"
class="my.project.package.OverrideActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="test.server"/>
<constructor-arg value="ldap://192.168.0.2:389"/>
<property name="convertSubErrorCodesToExceptions" value="true"/>
</bean>
One of the problems I have foreseen is that when the loadUserAuthorities() method (overridden for my custom use of AD fiels) is called, SecurityContextHolder.getContext().getAuthentication() returns null. This is a problem -- it indicates (to me, as I understand Spring's handling of sessions) that the authentication process has not completed, and the session ID pertaining to the to-be-authenticated user has not been generated. That is, Spring clears the anonymous session upon authentication in favor of a new session ID, but that session is apparently unavailable when loadUserAuthorities() is run.
So what shall I do? As I said, my hack works, and it seems to be secure (provided the right annotation), but it is hardly ideal. How can I properly store my custom user information onto a session-scoped user object while also maintaining authentication against AD via LDAP (over TLS)?
As always, I love and appreciate all the help I receive from the SO community.

How to save ip address to a DB from authenticated user with Spring security?

I need to keep track of the ip address when users log in my spring application.
security.xml:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userService">
<password-encoder ref="passwordEncoder">
<salt-source user-property='username' />
</password-encoder>
</authentication-provider>
with bean:
<beans:bean id="passwordEncoder"
class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
<beans:constructor-arg value="512" />
</beans:bean>
I've a custom userService with a method loadUserByUsername() returning a custom UserDetails. This method get the UserDetails from a database, via a DAO. The UserDetails contains stuffs related to the user such as his username, password, authorities, email address, but also application-specific variables. I need to access these variables in my JSP pages.
I want to save into a database (via a call to a method in a custom service, which call a DAO method) the ip address, timestamp and user id when a user is authenticated successfully in my application.
I'm not sure what to do: should I implement a custom authentication provider? extends DaoAuthenticationProvider? or AbstractUserDetailsAuthenticationProvider? or something else?
More general questions:
A. Where can I add a method to call once a user provides the right credentials?
B. How can I retrieve the ip address of the user? (knowing that tomcat runs behind apache in a reverse-proxy).
I tried to look at related questions/answers, but it just made me more confused. If someone could provide a very simple step-by-step implementation, it would be awesome. thanks!
You can provide a custom authentication success handler that will be responsible for saving an IP of current user in DB. See authentication-success-handler-ref attribute of form-login tag. It will be good idea to extend one of existing implementations (for example SavedRequestAwareAuthenticationSuccessHandler) and add your functionality.
You can get IP after authentication from everywhere just by doing:
WebAuthenticationDetails details = (WebAuthenticationDetails)SecurityContextHolder.getContext().getAuthentication().getDetails();
String ip = details.getRemoteAddress();
Try it. If it gives you wrong IP address due to reverse proxy then consider adding client IP as request header.

Spring Security and Sessions over Web Services

Summary
How do you get the session of a web service client using spring web services and spring security?
Details
After submitting
<form method="POST" action="<c:url value="/j_spring_security_check" />">...</form>
I've noticed that you can:
public class MyUserDetailsService implements UserDetailsService {}
Which will allow you to override methods like loadUserByUsername(String username) therefore being able to retrieve the submitted username and do a database lookup to return a user object.
The issue I have, however, is that I'm unsure where SecurityContextHolder gets set. I'm able to get the user object by using this line of code:
User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
But I'm not sure how it gets set in the first place. I would like to know the flow after submitting the above-mentioned form so that I can identify how SecurityContextHolder gets set.
The reason why I want to know this is because I want to use it as a "session" for web service client authentication instead of having the client resubmit credentials with every request.
Spring Version: 3.0.2.RELEASE
/j_spring_security_check is handled by the UsernamePasswordAuthenticationFilter which extends AbstractAuthenticationFilter. The security context is set in the latter's successfulAuthentication method.
However, web-service clients are usually stateless and would be more likely to use something like Basic authentication with a shared secret. I'm not sure there would be much benefit in rolling your own session system based on the security context contents. If you are worried about performance then you could use a cache of authentication information on the server.

Can you use Java Annotations to evaluate something in a method?

I want to see if it is possible to use annotations to evaulate if a user is logged in or not.
Example
#AuthRequired
public String myProtectedArea() {
return View("view/protectedArea"); // If user is NOT authenticated, return "view/login"
}
As per your edit:
Check this SO Post:
Scanning Java annotations at runtime
I'd still recommend using Spring Security for this, it's tested and secure:
#PreAuthorize("hasRole('ROLE_USER')")
public String myProtectedArea() {
return View("view/protectedArea");
}
The annotation will check if the user is logged in and has the required credentials.
Another way with Spring Security is to intercept the URL pattern by setting this inside a spring.security-settings.xml:
<intercept-url pattern="/view/protectedArea/*" access="hasRole('ROLE_USER')" />
I'd recommend using both to maximize security.
In the security settings file you can then tell spring security where to redirect the user to login. If the user is already logged in, you can redirect him to yet another page:
<form-login login-page="/view/login.xhtml" default-target-url="/view/protectedArea/home.xhtml"
authentication-failure-url="/view/login.xhtml" />
It's a tested framework and thus secure and versatile. However it requires a bit of setting up if you want more than the standard behaviour.
The annotation doesn't check if the user is logged in or not--annotations are metadata on classes/methods. Something must still make use of the annotation.
Something in your code checks to see if the method is annotated with #AuthRequired, if it is, checks if logged in, then executes the method based on that.
For example, a web app might look for the annotation in a filter, base servlet, interceptor, etc. and decide whether or not the request process should continue.
Depending upon what type of application you are creating there are a number of options available to you for defining authentication levels for specific methods.
I would most likely recommend to you Spring Security for such a task.
Something like the below example would be the end result after configuration using Spring Security.
#Secured( {"USER_ROLE"} )
public String getSecretData() {
return "SECRET! SHHH!";
}
Then only users verified by Spring Security to have the role you provide to the annotation will have authorization to call the method.
There are a couple other annotation options in Spring Security you can utilize such as #PreAuthorize.
Instead of re-inventing the wheel, have a look at JAAS:
http://docs.oracle.com/javaee/6/tutorial/doc/bncbx.html#bncca
http://docs.oracle.com/javaee/6/tutorial/doc/bncas.html
http://docs.oracle.com/javaee/6/tutorial/doc/gijrp.html
http://docs.oracle.com/javaee/6/api/javax/annotation/security/package-summary.html

Programmatic use of Spring Security

I am using Wicket with the Wicket Auth Project for my presentation layer and I have therefore integrated it with Spring Security. This is the method which is called by Wicket for authentication for me:
#Override
public boolean authenticate(String username, String password) {
try {
Authentication request = new UsernamePasswordAuthenticationToken(
username, password);
Authentication result = authenticationManager.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
} catch (AuthenticationException e) {
return false;
}
return true;
}
The contents (inside ) of my Spring Security XML configuration are:
<http path-type="regex">
<form-login login-page="/signin"/>
<logout logout-url="/logout" />
</http>
<global-method-security secured-annotations="enabled" />
<authentication-manager alias="authenticationManager"/>
<authentication-provider user-service-ref="userService">
<password-encoder ref="bcryptpasswordencoder" />
</authentication-provider>
The section 2.3.6. Session Fixation Attack Protection of the reference documentation says:
Session fixation attacks are a potential risk where it is possible
for a malicious attacker to create a
session by accessing a site, then
persuade another user to log in with
the same session (by sending them a
link containing the session identifier
as a parameter, for example). Spring
Security protects against this
automatically by creating a new
session when a user logs in. If you
don't require this protection, or it
conflicts with some other requirement,
you can control the behaviour using
the session-fixation-protection
attribute on , which has three
options:
migrateSession - creates a new session and copies the existing
session attributes to the new session. This is the default.
none - Don't do anything. The original session will be retained.
newSession - Create a new "clean" session, without copying the
existing session data.
The authentication works, but I as I'm fairly new to Spring Security I have some questions which I need answers too:
Normally for login, I would POST the authentication information to j_spring_security_check and let Spring Security perform the actual authentication code. I would like to have protection against session fixation attacks, will I get it when I perform a programmatic login as I do? And if not, what would I have to do to get it?
How do I perform programmatic logout?
As I will use programmatic login and logout, how do I disable Spring from intercepting those URL's?
Update:
For session fixation attack protection it seems that I need to call the method in the SessionUtils class with the signature startNewSessionIfRequired(HttpServletRequest request, boolean migrateAttributes, SessionRegistry sessionRegistry).
How do I get the SessionRegistry instance which I need to pass in? I can't find any way to create an alias ID for it, or how to get it's ID or name.
Maybe it's not a full answer to your questions, but maybe it might help you.
The code being called when you do NOT use programmatic login, but a standard one is to be found here:
org.springframework.security.ui.webapp.AuthenticationProcessingFilter
I guess you were inspired by this in your code. It looks quite similar.
Similarly the code executed when you access the /j_spring_security_logout in the standard approach, is to be found here:
org.springframework.security.ui.logout.LogoutFilter
The LogoutFilter calls multiple handlers. The handler we are using is called:
org.springframework.security.ui.logout.SecurityContextLogoutHandler, so you might call the same code in your approach.
You will indeed be open to session fixations attacks. To remedy this you could again be "inspired" by the Spring code. To create a new session you'll obviously need access to the httpsession so you may have to do some refactoring.
If you see the method SessionUtils.startNewSessionIfRequired.
This will migrate the authentication to a new session. You might be able to call this method directly or else just refactor the code a little.
As for programmatic logout you can't go too far wrong by simply calling session.invalidate() when you need to log the person out. This will do everything necessary from a general security perspective but bear in mind though you might need to cleanup some things on the session. If you have a very complicated set of filters etc. and you need to ensure that that the user is logged out for the rest of the request then you could add:
SecurityContextHolder.getContext().setAuthentication(null);
As for interception of the url's you could just set them to something unused and ignore it! I'm not sure if you can turn off the interception in configuration - if you really want to remove it then have a look at the AuthenticationProcessingFilter - you could customise this. If you do this then you'll have to manually setup the spring security xml and not use the provided namespaces. It's not too hard though - look at some older documentation and you'll see how to do this.
Hope this helps!
1) Programmatic Logout
call HttpServletRequest.getSession(false).invalidate
call SecurityContextHolder.clearContext()
2) Tell Spring Security NOT to intercept certain URLs, this one kind of depends on how your application url space is setup. If all your pages (except /logIn and /logout) lived at the context /myApp then you could do this:
<http ....>
<intercept-url pattern="/myApp/**" ..>
....
</http>
I had an issue with programmatic login. I called all the authenticationManager.authenticate(...) and SecurityContextHolder.getContext().setAuthentication(...) methods but had some issues with the Session. I had to add the following lines to properly manage the session:
HttpSession session = request.getSession();
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
This was not clear from the example code posted above. For more look at http://forum.springsource.org/showthread.php?t=69761
To do programmatic logout it's also possible to throw an org.springframework.security.core.AuthenticationException. For example, SessionAuthenticationException. In this case ExceptionTranslationFilter initiate logout.
You can try this
try {
HttpSession session = request.getSession(false);
if (session != null) {
session.invalidate();
}
SecurityContextHolder.clearContext();
} catch (Exception e) {
logger.log(LogLevel.INFO, "Problem logging out.");
}

Categories

Resources