The DefaultLdapAuthoritiesPopulator sets a search scope of "ONE_LEVEL", but I need to search "SUBSCOPE" to get the list of groups a user is a member of.
I've been following the "configuration" style Spring setup (code, not XML). While there's tons of examples of how to configure a custom LdapAuthoritiesPopulator in XML, I'm kind of stuck on how to do it in code.
Here's what I have so far:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.contextSource().url("ldap://ldap.company.org/")
.and()
.userSearchBase("o=company.org,c=us")
.userSearchFilter("(uid={0})")
.groupSearchBase("o=company.org,c=us")
.groupSearchFilter("(&(objectClass=groupOfUniqueNames)(uniqueMember={0}))");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().and().authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll();
}
}
What's missing is that I need to be able to set the search scope on the DefaultLdapAuthoritiesPopulator. The class itself exposes a "setSearchSubtree" method, but the LdapAuthenticationProviderConfigurer does not provide a way of configuring it.
Any suggestions?
Solution is to set this property in LdapAuthoritiesPopulator and pass it to LdapAuthenticationProvider
Refer Example 1 in : https://www.programcreek.com/java-api-examples/?api=org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator
#Bean
public LdapAuthoritiesPopulator authoritiesPopulator(){
DefaultLdapAuthoritiesPopulator populator = new DefaultLdapAuthoritiesPopulator(
contextSource(),
groupSearchBase);
populator.setGroupSearchFilter("(uniqueMember={0})");
populator.setGroupRoleAttribute("cn");
**populator.setSearchSubtree(true);**
populator.setRolePrefix("");
return populator;
}
You need to add something like:
final SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
To before you begin your search.
Why it is called a "control" is beyond me (an LDAP guy), but that is what Spring does.
-jim
Related
My application serves both API and browser. I've implemented API Token authentication with all custom providers and filter. The configuration now seems to interfere with the browser version.
I have two questions that I need advice on how to solve, as I'm not getting anywhere after digging through the documentation and other examples.
1) My StatelessAuthenticationFilter is being called despite a request
coming from the browser. I have e.g. specified the request matcher to "/api/**". Why is that?
2) The AuthenticationManager have not registered two AuthenticationProviders. This is my conclusion after debugging my StatelessAuthenticationFilter that's being called wrongly.
Here's the configuration classes that I have
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Order(1)
#Configuration
public static class A extends WebSecurityConfigurerAdapter {
#Autowired
TokenAuthenticationProvider tokenAuthenticationProvider;
#Autowired
ApiEntryPoint apiEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
StatelessAuthenticationFilter filter = new StatelessAuthenticationFilter();
AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/api/**");
filter.setRequiresAuthenticationRequestMatcher(requestMatcher);
filter.setAuthenticationManager(super.authenticationManager());
http.csrf().disable()
.exceptionHandling().authenticationEntryPoint(apiEntryPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/user/register");
}
}
#Configuration
public static class B extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new DaoAuthenticationProvider());
}
}
}
As you can see, B class doesn't specify anything, yet when I access localhost:8080 the StatelessAuthenticationFilter is called. What is going on here?
In class A you are configuring the StatelessAuthenticationFilter to use a requestMatcher. Whatever you do with that, spring does not know or care about that.
You must also restrict your security configuration using
http.antMatcher("/api/**")
otherwise its configured for every URI and the StatelessAuthenticationFilter will be invoked for every request, exactly as you described.
You should also annotate class A and B with #Order as shown in the example at multiple-httpsecurity
I have two configuration. The first would like to achieve that all requests from(/api/**) must come only from a determined ip.
like Following...
.authorizeRequests().antMatchers("/api/**").hasIpAddress("dynamic List of IPs");
It should be checked whether the IP is stored in the database, otherwise the access is to be denied.
And the secound config takes care of the rest.
#EnableWebSecurity
public class AppSecurityConfig {
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new CustomUserDetailsService()).passwordEncoder(new Md5PasswordEncoder());
}
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.headers().disable()
.authorizeRequests().antMatchers("/api/**").hasIpAddress("dynamic List of IPs");
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(1)
.expiredUrl("/error/expired.xhtml").and()
.invalidSessionUrl("/Anmeldung.xhtml?check=invalid");
http
.csrf().disable()
.headers().disable()
.formLogin().loginPage("/Anmeldung/").loginProcessingUrl("/j_spring_security_check").successHandler(new CustomAuthenticationSuccessHandler())
.failureUrl("/Anmeldung.xhtml?check=error").usernameParameter("j_username").passwordParameter("j_password")
.and()
.exceptionHandling().accessDeniedPage("/error/403.xhtml")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/Anmeldung.xhtml?check=logout").invalidateHttpSession(false).deleteCookies("JSESSIONID").permitAll();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry interceptUrlRegistry = http.authorizeRequests();
interceptUrlRegistry.antMatchers("/Administrator/*").hasAnyAuthority("ROLE_ADMIN");
interceptUrlRegistry.antMatchers("/*").hasAnyAuthority("ROLE_USER");
interceptUrlRegistry.antMatchers("/Anmeldung/index.xhtml").anonymous();
interceptUrlRegistry.antMatchers("/template/*").denyAll();
interceptUrlRegistry.antMatchers("/resources/**").permitAll();
}
}
}
Thanks for your help.
You can dynamically configure httpsecurity object inside for loop like the code referenced below.
for (Entry<String, String> entry : hasmapObject) {
String url = entry.getKey().trim();
String ips= entry.getValue().trim();
http.authorizeRequests().and().authorizeRequests().antMatchers(url).hasIpAddress(ips);
}
This worked for me. The hashmap object had the dynamic list of url's and their corresponding ips to give access.
"http.authorizeRequests().and()" this and() is needed to indent like we use in xml configuration to configure http child elements in XML.
Please let me know if this helps.
I have a web application which runs with following configuration.
public class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/open/**").permitAll()
.antMatchers("/api/data/**").authenticated()
.antMatchers("/api/user/**").hasRole("USER")
.antMatchers("/api/mgr/**").hasRole("MGR")
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and() .exceptionHandling().accessDeniedHandler(customBasicAuthenticationAccessDeniedHandler())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable() //TODO
.httpBasic().authenticationEntryPoint(customBasicAuthenticationEntryPoint());
}
...
}
I then added,
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
...
}
and
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
//Same as WebSecurityConfigurerAdapter configure()
...
}
Because of ResourceServerConfig class now everything has crewed up. Tried various ways to configure this. But it seems ResourceServerConfigurerAdapter behave completely different than WebSecurityConfigurerAdapter, but I don't have a single clue to get this to work.
Do I need to remove WebSecurityConfigurerAdapter and keep only ResourceServerConfigurerAdapter? Did that, but configure(HttpSecurity) behave differently than I thought.
Also some stackoverflow answers recommended to change the #Order of the WebSecurityConfigurerAdapter. But nothing works.
I need to know actually what is wrong and what is correct first, than writing a code.
Appreciate very very much if someone point me a right direction.
Thanks!
By sharing this, I am only intending to be helpful, not answer your query. I know documentation is not that good. Just sharing my 2 cents.
This is what worked for me. Using Spring-security-Oauth2 version 2.0.7
#EnableWebSecurity
public class SampleMultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class ComplexOauth2SpringSecurityConfiguration extends
WebSecurityConfigurerAdapter {
#Autowired
private OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter;
#Autowired
private OAuth2AuthenticationManager oAuth2AuthenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
...
}
}
#Configuration
public static class ComplexOauth2SpringSecurityConfiguration2 extends
WebSecurityConfigurerAdapter {
#Autowired
private OAuth2AuthenticationProcessingFilter oAuth2AuthenticationProcessingFilter;
#Autowired
private OAuth2AuthenticationManager oAuth2AuthenticationManager;
#Override
protected void configure(HttpSecurity http) throws Exception {
...
}
}
Thereafter, I simply added a component:scan on the package which is having this class.
This is primarily on the server side.
Also, note the injection of OAuth2AuthenticationProcessingFilter. This is based on RemoteTokenServices whose one of many jobs is to perform Token Validation with Authorization server.
<bean id="remoteTokenServices" class="org.springframework.security.oauth2.provider.token.RemoteTokenServices"
init-method="init" destroy-method="shutdown">
<property name="checkTokenEndpointUrl" value"..."/>
</bean>
I do agree that I did not implement resource server and Authorization server. They were already built for us. However, while testing we simply created couple of REST POST services to simulate the Token generation and Validation.
I am creating a Spring Security configuration to be used as a library by any developer who wants to create a Stormpath Spring application secured by Spring Security.
For that I have sub-classed WebSecurityConfigurerAdapter and defined the Stormpath Access Controls in configure(HttpSecurity) as well as the Stormpath AuthenticationProvider by means of configure(AuthenticationManagerBuilder). All this can be seen in this abstract class and its concrete sub-class:
#Order(99)
public abstract class AbstractStormpathWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Removed properties and beans for the sake of keeping focus on the important stuff
/**
* The pre-defined Stormpath access control settings are defined here.
*
* #param http the {#link HttpSecurity} to be modified
* #throws Exception if an error occurs
*/
protected void configure(HttpSecurity http, AuthenticationSuccessHandler successHandler, LogoutHandler logoutHandler)
throws Exception {
if (loginEnabled) {
http
.formLogin()
.loginPage(loginUri)
.defaultSuccessUrl(loginNextUri)
.successHandler(successHandler)
.usernameParameter("login")
.passwordParameter("password");
}
if (logoutEnabled) {
http
.logout()
.invalidateHttpSession(true)
.logoutUrl(logoutUri)
.logoutSuccessUrl(logoutNextUri)
.addLogoutHandler(logoutHandler);
}
if (!csrfProtectionEnabled) {
http.csrf().disable();
} else {
//Let's configure HttpSessionCsrfTokenRepository to play nicely with our Controllers' forms
http.csrf().csrfTokenRepository(stormpathCsrfTokenRepository());
}
}
/**
* Method to specify the {#link AuthenticationProvider} that Spring Security will use when processing authentications.
*
* #param auth the {#link AuthenticationManagerBuilder} to use
* #param authenticationProvider the {#link AuthenticationProvider} to whom Spring Security will delegate authentication attempts
* #throws Exception if an error occurs
*/
protected void configure(AuthenticationManagerBuilder auth, AuthenticationProvider authenticationProvider) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
#Configuration
public class StormpathWebSecurityConfiguration extends AbstractStormpathWebSecurityConfiguration {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
#Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
In short, we are basically defining our login and logout mechanisms and integrating our CSRF code to play nicely with Spring Security's one.
Up to this point everything works OK.
But this is just the "library" and we want users to build their own applications on top of it.
So, we have created a Sample application to demonstrate how a user will use our library.
Basically users will want to create their own WebSecurityConfigurerAdapter. Like this:
#EnableStormpathWebSecurity
#Configuration
#ComponentScan
#PropertySource("classpath:application.properties")
#Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
/**
* {#inheritDoc}
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
}
In case this is actually needed, the WebApplicationInitializer looks like this:
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringSecurityWebAppConfig.class);
context.register(StormpathMethodSecurityConfiguration.class);
sc.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Stormpath Filter
FilterRegistration.Dynamic filter = sc.addFilter("stormpathFilter", new DelegatingFilterProxy());
EnumSet<DispatcherType> types =
EnumSet.of(DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
filter.addMappingForUrlPatterns(types, false, "/*");
//Spring Security Filter
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
}
}
All this code boots up correctly. If I go to localhost:8080 I see the welcome screen. If I go to localhost:8080/login I see the login screen. But, if I go to localhost:8080/restricted I should be redirected to the login page since we have this line: http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();. However I am seeing the Access Denied page instead.
Then, if I add the login url in the App's access control, like this:
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login")
.and()
.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
It now redirects me to the login page but as soon as I submit the credentials I get an CSRF problem meaning that all our configuration is not actually part of this filter chain.
When I debug it all it seems that each WebApplicationInitializer is having its own instance with its own Filter Chain. I would expect them to be concatenated somehow but it seems that it is not actually happening...
Anyone has ever tried something like this?
BTW: As a workaround users can do public class SpringSecurityWebAppConfig extends StormpathWebSecurityConfiguration instead of SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter. This way it works but I want users to have pure Spring Security code and extending from our StormpathWebSecurityConfiguration diverges from that goal.
All the code can be seen here. The Stormpath Spring Security library for Spring is under extensions/spring/stormpath-spring-security-webmvc. The Sample App using the library is under examples/spring-security-webmvc.
It is very simple to run... You just need to register to Stormpath as explained here. Then you can checkout the spring_security_extension_redirect_to_login_not_working branch and start the sample app like this:
$ git clone git#github.com:mrioan/stormpath-sdk-java.git
$ git checkout spring_security_extension_redirect_to_login_not_working
$ mvn install -DskipTests=true
$ cd examples/spring-security-webmvc
$ mvn jetty:run
Then you can go to localhost:8080/restricted to see that you are not being redirected to the login page.
Any help is very much appreciated!
In my experience there are issues with having multiple WebSecurityConfigurers messing with the security configuration on startup.
The best way to solve this is to make your library configuration into SecurityConfigurerAdapters that can be applied where appropriate.
public class StormpathHttpSecurityConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
}
public class StormpathAuthenticationManagerConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
//Removed beans for the sake of keeping focus on the important stuff
#Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
You then have your users apply these in their own configuration:
#EnableStormpathWebSecurity
#Configuration
#ComponentScan
#PropertySource("classpath:application.properties")
#Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/restricted").fullyAuthenticated()
.and()
.apply(new StormPathHttpSecurityConfigurer(...))
;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new StormPathAuthenticationManagerConfigurer(...));
}
}
It's definitely the problem regarding either order of your Antmatchers or hasn't specified ROLES of users that you permit to access the URL.
What do you have anything above "/restricted"?
Is something completely blocking anything below that URL? You should specify more specific URLS first then, generalised URLs.
Try configuring above URL properly (or tell me what it is so I can help you out), perhaps apply "fullyAuthenticated" also "permitAll" ROLEs on the parent URL of "/restricted".
Using the following code to configure Spring security, I can see in ProviderManager, List AuthenticationProvider has two elements.
MyAuthenticationProvider at index 0 and AnonymousAuthenticationProvider at index 1.
My question is that is there a way to make AnonymousAuthenticationProvider at index 0?
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyAuthenticationProvider myAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().authenticationProvider(myAuthenticationProvider);
}
}
I can do http.anonymous().disable() and init and set AnonymousAuthenticationProvider manually but
I am not sure what key for constructor I need to provide.
it is more like a workaround.