I am using spring-security-ldap to add LDAP authentication to my application. It is configured like so:
auth.ldapAuthentication()
.userSearchBase(ldapConfigProperties.getUserSearchBase())
.userSearchFilter(ldapConfigProperties.getUserSearchFilter())
.contextSource()
.managerDn(ldapConfigProperties.getManagerDn())
.managerPassword(ldapConfigProperties.getManagerPassword())
.url(ldapConfigProperties.getUrl())
.and()
.userDetailsContextMapper(ldapContextMapper);
The ldapContextMapper is an instance of a custom class called LdapUserContextMapper that implements UserDetailsContextMapper.
Inside mapUserFromContext() I use the DirContextOperations to retrieve several attributes from the LDAP user to construct a concrete User POJO. However, I just can't get access to operational attributes like memberOf. I tried every solution I could possible find on the web, but nothing seems to have worked.
For instance, ctx.getObjectAttributes("memberOf") returns null. Attempting to call ctx.search("", "", searchControls) with custom SearchControls with SUBTREE_SCOPE yields a not implemented exception from DirContextAdapter.
Any ideas?
I eventually ended up instantiating my own ContextSource and then using a custom ObjectPostProcessor, just as described in this issue.
Related
I've recently upgraded a project from using spring-security 6.0.0-M6 to 6.0.0, gradle config if you want to see it.
This project does not use spring-boot.
Context
My securityFilterChain is configured via code and looks approximately like this:
http.
authenticationManager(authnManager).
securityContext().securityContextRepository(securityRepo).
and().
authorizeRequests(). // <-- DEPRECATED
requestMatchers(RAID_V2_API + "/**").fullyAuthenticated().
The full codebase, starting with the FilterChain config, is publicly available.
Note that usage of WebSecurityConfigurerAdapter is deprecated, and I have not been using it since the original usage of 6.0.0-M6. So calling stuff like WebSecurityConfigurerAdapter.authenticationManagerBean() won't work.
This code works fine, but the call to authorizeRequests() causes a deprecation warning that I want to get rid of.
Problem
The deprecation tag says that I should use authorizeHttpRequests() instead, but when I do that - requests that require authorization (via the fullyAuthenticated() specification above) will be denied with a 403 error.
Analysis
It seems this happens because my AuthenticationProvider instances aren't being called,
because the ProviderManager isn't being called. Since the AuthnProviders don't get called, the security context still contains the pre-auth token instead of a verified post-auth token, so the eventual call to AuthorizationStrategy.isGranted() ends up calling isAuthenticated() on the pre-auth token, which (correctly) returns false and the request is denied.
Question
How do I use the authorizeHttpRequests() method but still have the ProviderManager be called so that my security config works?
My workaround is just to ignore the deprecation warning.
First, your security configuration does not specify any kind of authentication, like httpBasic, formLogin, etc. The AuthenticationManager is invoked by the filters created by those authentication mechanisms in order to authenticate credentials.
Second, the application is probably unwittingly relying on FilterSecurityInterceptor (authorizeRequests) to authenticate the user, which is not supported with authorizeHttpRequests. You need to declare an auth mechanism that collects credentials from the request and authenticates the user.
Because you are using JWT, you might want to consider Spring Security's OAuth2 Resource Server support. You can also refer to our samples repository in order to help you with sample configurations.
Here's a rough outline of what I did to to implement the "just use the resource server" suggestion from the answer.
include the oauth2-resource-server libraries in the build.
create an AuthenticationManagerResolver that replaces what the SecuritycontextRepository and the FilterSecurityInterceptor used to do:
#Bean
public AuthenticationManagerResolver<HttpServletRequest>
tokenAuthenticationManagerResolver(
AuthenticationProvider authProvider
) {
return (request)-> {
return authProvider::authenticate;
};
}
change AuthenticationProvider implementations to use the BearerTokenAuthenticationToken class as the pre-auth token, it still works basically the same way it used to: verifying the pre-auth token and returning a post-auth token.
hook up the new resolver class in the securityFilterChain config by replacing the old securityContextRepository() config with the new authenticationManagerResolver() config, which passes in the resolver created in step 2:
http.oauth2ResourceServer(oauth2 ->
oauth2.authenticationManagerResolver(tokenAuthenticationManagerResolver) );
I like this new approach because it makes it more obvious how the security chain works.
It's nice to replace the custom pre-auth token implementation with the built-in class too.
Note that it's likely this config can be simplified, but I needed the custom resolver since the project uses different types of bearer token depending on the endpoint called. Pretty sure the auth providers don't need to be AuthenticationProvider any more; the lambda function returned from the resolver serves that purpose - they can probably just be random spring components and as long as the method is SAM-type compatible.
The spring-security multi-tenancy doco was helpful for this.
I am running a Spring-boot application which authenticates users via our internal LDAP with spring-security-ldap.
By default it binds with LDAP anonymously.
Property 'userDn' not set - anonymous context will be used for read-write operations
But I want the first bind to be with current username.
Where should I specify the userDn attribute?
Thank you for your advice
When using spring ldap maybe you started from one many tutorials on the web but main of them uses embedded ldap server; embdedded server uses ldif file and doesn't need the manager credetials.
When connecting to an external ldap server you need to specify userDn setting it via managerDn method. Here the snippet of code
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().contextSource().managerDn("uid=admin,ou=system")
.managerPassword("secret")
.......
}
Obviously you need to provide also all the other infos like url, port, etc (and userSearchBase like mvreijn told).
I am not the most knowledgeable person regarding Spring-boot, more so regarding LDAP.
That said, your LDAP configuration properties should be mentioned in your application.properties file and are named spring.ldap.*.
They are mentioned in the documentation here.
When initializing your authentication provider, you can pass important properties like the Base DN (root to search from) and the filter using:
.userSearchBase("ou=<your users container>").userSearchFilter("(uid={0})")
Most likely, your search filter will be uid={0} or cn={0}.
I'm having some trouble regarding the authorization endpoint of my Spring based OAuth2 provider. I need more information from the client than there is currently possible. This is what I want to achieve:
I need the custom parameter in the authentication process later on. Is there any simple way to extend the default parameters with my custom one or do I need to implement a certain class myself?
Did some research on how the authentication endpoint works in the current Spring code. I found that the Authorization Endpoint uses a method named authorize that takes all the parameter that are being set and converts then into an AuthorizationRequest. While looking further into the AuthorizationRequest class I found that it holds a map with extensions that is being filled throughout the authorization process. But it does not seem to get filled with my custom parameter (as shown above). This is in fact by only looking at the code, so I might be wrong.
Would it be a good idea to extend the AuthorizationEndpoint with my custom implementation or is there a better and cleaner way to do this?
Update #1 (07-10-2015)
The place where I'd like to use the custom parameter is in my own implementation of the AuthenticationProvider. I need to information to be available inside the authenticate method of this class.
Update #2 (07-10-2015)
It seems that the AuthorizationProvider gets called before the AuthorizationEndpoint. This means that the custom parameter is obtained after the class where I need it (so that's too late).
Maybe I can get the referral Url by either extending part of the Spring security classes or by obtaining it in the HTML through JavaScript. Is this a good idea or should I use another approach?
So I managed to fix the problem myself by searching some more on Google.
What you need to do is speak to the HttpSessionRequestCache to get the referral URL. This is how I solved it in my own implementation of the AuthenticationProvider
#Component
public class CustomProvider implements AuthenticationProvider {
#Autowired
private HttpServletRequest httpRequest;
#Autowired
private HttpServletResponse httpResponse;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(httpRequest, httpResponse);
logger.info("Referral URL: " + savedRequest.getRedirectUrl());
logger.info("Parameters: " + savedRequest.getParameterMap().keySet().toString());
}
}
This will print out the URL of the request that was called before heading to the login page of spring security. The second log method prints out the parameters that where found in this URL. This question and answer helped me in creating a solution for my problem.
I want to have a JS application in on client-side (no jsps) that will communicate with back-end only with REST calls. I want also to enable users to be able to login with FB, Twitter accounts. In addition, I also want to enable users to register their own accounts. For this purpose I want to use Spring-security and spring-social on backend and Javascript SDK in front to get access_token from the FB, which will be then passed to backend.
The question is: how do I create a REST controller that would authenticate using spring-social and spring-security facilities?
I read through the examples in:
https://github.com/spring-projects/spring-social-samples
but couldn't really find how I could make use of ProviderSignInController or SpringSocialConfigurer for this purpose. I guess I cannot use the SocialAuthenticationFilter in my case since the "/auth/{providerid}" url is not what I'm looking for. However, I guess the ProviderSingInController seems to be of use here neither. Please correct me if I'm wrong. Ideally I would like to benefit from all capabilities of Spring Security framework.
I will appreciate any suggestions.
Best regards
EDIT
I would like to follow a flow like here: http://porterhead.blogspot.com/2013/01/writing-rest-services-in-java-part-4.html but using the Spring Social and Spring Security combined.
The front-end application is written in AngularJS
2nd EDIT
It turns out that you can simply make use of all the Spring Social modules benefits out of the box. The only thing a client has to do is call a GET on the auth/facebook or whatever link to fire entire 0auth dance which will eventually return the authentication result. Then you can control the flow easily (register account or return some relevant information to the client to let know registration is needed). So the SpringSocialConfigurer works well in this case (apart from the fact that it doesn't support scope setting yet, however, this can be changed manually, check my pull request # github.com/spring-projects/spring-social/pull/141)
3rd EDIT - 14.10.2014
As requested, I will share how I managed to make it work.
Given I have configured my security filter in the following way:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
public void configure(final HttpSecurity http) throws Exception {
http.formLogin()
...
.and().apply(getSpringSocialConfigurer());
}
private SpringSocialConfigurer getSpringSocialConfigurer() {
final SpringSocialConfigurer config = new SpringSocialConfigurer();
config.alwaysUsePostLoginUrl(true);
config.postLoginUrl("http://somehost.com:1000/myApp");
return config;
}
Once my application is set up, the only thing I need to call is http://somehost.com:1000/myApp/auth/facebook
with GET request.
"In addition, I also want to enable users to register their own
accounts"
If you say that you want to allow users to login with their own credentials (without FB/twiter), you need to let them also to create account, and to support forgot password, etc...
If that is the case, maybe this SO thread might be helpful. The auth-flows package also supports REST API.
Create Account, Forgot Password and Change Password
I'm pretty new to spring, let along spring security, and I've been looking at the Schema files and noticed a <jee> tag that appears under the <http> tag which acts like some sort of preauth filter magic. Using the mappable-roles attribute, I can get the role that I want but, when I specify a different user-service-ref attribute, specifying a UserDetailsService object, I lose the role.
My guess is because when I specify the UserDetailsService object, Spring assumes I don't need the role anymore... but I do!
Any ideas on this? Can I capture those details somehow? If not with this simple <jee> tag, what does the jee tag expand to for custom-filters and pre-auth, etc?
The <jee> tag creates (among others) a PreAuthenticatedAuthenticationProvider bean which delegates to a configured strategy for loading user details.
The default implementation of this strategy is PreAuthenticatedGrantedAuthoritiesUserDetailsService which will simply copy the roles it finds in the authentication token.
By overriding this default strategy with your custom UserDetailsService using the user-service-ref attribute you take the responsibility for assigning roles to the user. If you want to keep the default behavior regarding user roles, you can simply copy the related line from the above mentioned class, as the Java EE roles are still mapped, and are accessible in the auth token to your own code as well.