Authentication provider per url pattern - Spring Boot - spring-boot

I faced problem when configuring different auth providers per url pattern using Spring Boot security. I am trying to configure security in Spring Boot app and want to have swagger behind basic auth and all API is secured only by token. I have it almost working, but noticed that API except the fact that it is secured by token which is verified by IDAuthProvider class it also is secured by basic auth. I do not want that and also noticed that if I removed line:
sessionCreationPolicy(SessionCreationPolicy.STATELESS).
it seems to be working correctly, but still header Basic {token} is being added in request which is something I do not want...
Do you know how can I configure it to make all swagger stuff secured by basic auth and API stuff secured by token?
My configuration looks like below:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Configuration
#Order(1)
public class SwaggerSecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationProvider userPassAuthProvider;
#Autowired
SwaggerSecurityConfig(UserPassAuthProvider userPassAuthProvider) {
this.userPassAuthProvider = userPassAuthProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/swagger**").
authorizeRequests().
antMatchers("/swagger**").authenticated().
and().httpBasic().and().csrf().disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(userPassAuthProvider);
}
}
#Configuration
#Order(2)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationProvider idAuthProvider;
#Autowired
APISecurityConfig(IDAuthProvider idAuthProvider) {
this.idAuthProvider = idAuthProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/v1/**").
authorizeRequests().anyRequest().authenticated().
and().
addFilterBefore(idpAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class).sessionManagement().
and().
csrf().disable();
}
#Override
public void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(idAuthProvider);
}
IDPAuthenticationFilter idpAuthenticationFilter(AuthenticationManager auth) {
return new IDPAuthenticationFilter(auth, new OrRequestMatcher(new AntPathRequestMatcher(ApiRouter.API_PATH + "/**", HttpMethod.GET.toString()), new AntPathRequestMatcher(ApiRouter.API_PATH + "/**", HttpMethod.POST.toString()), new AntPathRequestMatcher(ApiRouter.API_PATH + "/**", HttpMethod.DELETE.toString()), new AntPathRequestMatcher("/swagger**", HttpMethod.GET.toString())));
}
}
}

Related

How to tune authenticationEntryPoint behaviour Spring Security

I have Spring Boot 2 based Security Gateway performing OAuth2 authentication sitting before GUI app and back-end.
It is configured like
#Configuration
#EnableOAuth2Client
#EnableWebSecurity
public class SecurityGatewayConfig extends WebSecurityConfigurerAdapter{
#Bean
public SecurityGatewayAuthenticationFilter filter() {
return new SecurityGatewayAuthenticationFilter("/login");
}
#Override
public void configure(HttpSecurity http) {
http
.addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.addFilterAfter(filter(), OAuth2ClientContextFilter.class)
.httpBasic().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.and()
...
It redirect requests to /login and SecurityGatewayAuthenticationFilter performs authentication against external OAuth2 provider.
It is good for GIU app. However, when accessing back-end services(they have /api/ in the path) I need different behaviour: If request is not authenticated, do not redirect, but immediately return 401 error.
Any idea, how to configure Spring Security for that?
If i got you questions right, what you can do is work with different ConfigurationAdapters. The basic idea looks like:
#Order(1)
#Configuration
#EnableOAuth2Sso
public static class SecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("defaultMatcher")
private RequestMatcher defaultMatcher;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(defaultMatcher)...
}
}
#Order(2)
#Configuration
public static class OtherConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(yourRequestMatcher())...
}
}
Spring will evalutate each request in the order you add with Order() if you use #EnableResourceServer, this will always have Order(3)
You can then build your request matchers as example like this (in this example it matches all, but excludes explicitly some other):
#Bean
public RequestMatcher defaultMatcher(#Qualifier("apiMatcher") RequestMatcher api, #Qualifier("anyother") RequestMatcher anyother) {
final RequestMatcher all = new AntPathRequestMatcher("/**");
final RequestMatcher nonApi = new NegatedRequestMatcher(new OrRequestMatcher(api, anyother));
return new AndRequestMatcher(all, nonApi);
}
Hope that helps.
best regards,
WiPu
you need add entry point filter
#Component
public final class CustomAuthenticationEntryPoint implements
AuthenticationEntryPoint {
#Override
public void commence(final HttpServletRequest request, final
HttpServletResponse response, final AuthenticationException
authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
When a client accesses resources without authentication...

Multiple authentication provider for specific url - Spring Boot Security

In Spring security I want to use Basic authentication for urls starting with api/** LDAP Rest Authentication for urls starting with /ldap/. The current code i have also allows ldap/ with basic authentication.
The question comes even if i use them as separate AuthenticationProviders like LdapAuthProvider and BasicAuthProvider how can i use it to point to the specific urls
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Configuration
#Order(1)
public class BasicAuthenticationProvider extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/swagger-ui*", "/info", "/health").permitAll()
.and().authorizeRequests().antMatchers("/order/**").fullyAuthenticated()
.and().httpBasic().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().csrf().disable()
.anonymous().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
}
#Configuration
#Order(2)
public class LdapAuthenticationProvider extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/ldap/**").fullyAuthenticated().and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.ldapAuthentication() code here......
}
}
}
As far as I understand, You have multiple entry points in one application and there are different types of users that can access different portions of the application.
You should look at this Baeldung tutorial: Multiple Entry Points in Spring Security

Spring Security does not intercept requests

I have a legacy application in which I have added Spring Web MVC libraries in order to expose a new Rest API.
I am struggling integrating spring-security in order to intercept the incoming requests. I have set up a security configuration class
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
and a security application initializer
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
}
following relevant guides.
Using debugger I verified that during initializing my configuration class is loaded. My problem is that my requests are not intercepted as expected.
Since you're already using Spring MVC, go to your class that initializes your application. If you're using Java Config, it most likely extends AbstractAnnotationConfigDispatcherServletInitializer.
Add your SecurityConfig to its "root config classes":
public class MySpringMmvcInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
...
#Override
protected abstract Class<?>[] getRootConfigClasses() {
return new Class[] { ..., SecurityConfig.class};
}
}
I think you forgot the #configuration annotation, try this
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}

EnableResourceServer breaks oAuth2 authorization server

I implemented oAuth2 authorization server using Spring Boot version 1.5.2.RELEASE. The authorization server supports implicit flow. With the WebSecurityConfig below the login form (http://localhost:8200/login) works well.
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JpaUserDetailsService userDetailsService;
#Bean
#Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return userDetailsService;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationProvider authenticationProvider() throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsServiceBean());
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new ProviderManager(singletonList(authenticationProvider()));
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers("/")
.antMatchers("/docs/**")
.antMatchers("/swagger/**")
.antMatchers("/token/**")
.antMatchers("/v2/*")
.antMatchers(HttpMethod.OPTIONS, "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/login**").permitAll().anyRequest().authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
.logout().permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
I want resource server be a part of the same application. The purpose is I need a /me endpoint that will provide me details of logged in user and endpoints for managing users. But as soon as I add ResourceServerConfig annotated with EnableResourceServer below I start getting an error "Full authentication is required to access this resource" when I request http://localhost:8200/login.
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = "proclaim-auth";
#Autowired
private ResourceServerTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.resourceId(RESOURCE_ID)
.tokenServices(tokenServices);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/ **").authenticated();
}
}
I suspect that resource server security chain precedes authorization server security chain. I tried to annotate WebSecurityConfig with annotation Order but it did not fix my problem:
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
What am I doing wrong? Please advise.
Thanks in advance!
EDIT 1
I added method configure(HttpSecurity http) into ResourceServerConfig and changed value of Order annotation to -1 on WebSecurityConfig. Now the security filted defined in WebSecurityConfig is applied and the one defined in ResourceServerConfig is ignored. So when I call /me endpoint with valid token I'm redirected to login page.
The cause of the problem was wrong configuration of http security in the ResourceServerConfig class. The correct configuration is as follows:
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/api/**").and()
.authorizeRequests().anyRequest().authenticated();
}
The requestMatchers will ensure that only requests on paths starting with "/api/" will be processed by this security chain. All other requests will be passed to the security chain defined in the WebSecurityConfig class. I was missing this in my config so all requests were processed by the ResourceServerConfig security chain and none request reached the WebSecurityConfig security chain.

How to turn on searchSubtree for LDAP on Spring boot?

I have a Spring boot application with LDAP authentication.
I need to have javax.naming.directory.SearchControls with SUBTREE_SCOPE to be able to retrieve the user groups, but I can't find a way to turn that on.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.userSearchFilter(ldap.getUserFilter())
.userSearchBase(ldap.getUserBaseDN())
.groupSearchFilter(ldap.getGroupFilter())
.groupSearchBase(ldap.getGroupBaseDN())
.groupRoleAttribute(ldap.getGroupNameAttribute())
.contextSource()
.url(ldap.getUrl())
.managerDn(ldap.getManagerDn())
.managerPassword(ldap.getManagerPassword());
}
public void setLdap(LdapProperties ldap) {
this.ldap = ldap;
}
}

Resources