Spring WebSecurity PermitAll not working for a specific URL - spring

I tried moving things around every imaginable way but
.antMatchers("/mpi/elastic/search").permitAll()
is always asking for authentication. I would like this URI be available to all users without any need for authentication.
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable().csrf().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()
.antMatchers("/api/auth/test1").permitAll()
// following is important as it's required for html files in /static and /public
// Also not that index.html in /static gets the preference
.antMatchers("/*").permitAll()
.antMatchers("/kjs/**").permitAll() // everything under kjs and also in subdirs
.antMatchers("/ainfo/**").permitAll()
.antMatchers("/aerror/**").permitAll()
// see StaticResourceLocation - must use ** will match /*/*/*
//.antMatchers("/**/favicon.ico").permitAll() // means anywhere you get favicon
//.antMatchers("/images/**").permitAll()
.antMatchers("/api/products/all").hasAuthority(AppRoles.ADMIN )
.antMatchers("/mpi/elastic/search").permitAll()
.anyRequest().authenticated().and().csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
#Bean
public PasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"Unauthorized");
}

I had a rule in nginx docker config for a specific path. I need to add another rule for the path I want to ignore.

Related

How to configure Spring-Security (Spring 6) for not having Filters executed on unsecured routes?

somewhat related to this other stackoverflow topic which doesn't give a proper solution nor is applicable to Spring 6 (Spring Boot 3).
I came up with a basic spring-boot app to make my case.
There is a controller with two end-points, where one must be secured and the other accessible.
#RestController
public class TestController {
#GetMapping("/secured-api")
public String securedApi() {
return "secured";
}
#GetMapping("/public/open-api")
public String openApi() {
return "open";
}
}
Security context as follow, imagine that MyFilter is doing something fancy, e.g: validating a JWT token and firing an exception if the token is invalid / expired.
#Configuration
public class ComponentSecurityContext {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.addFilterAt(new MyFilter(), BasicAuthenticationFilter.class)
.authorizeHttpRequests(customizer -> customizer
.requestMatchers(new AntPathRequestMatcher("/public/**"))
.permitAll()
.anyRequest()
.authenticated())
.build();
}
public static class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
System.out.println("Filter is called for uri: " + request.getRequestURI());
// performs some authentication
filterChain.doFilter(request, response);
}
}
}
Executing the following two curls on the server
curl http://localhost:9003/public/open-api
curl http://localhost:9003/secured-api
is triggering MyFilter
Filter is called for uri: /public/open-api
Filter is called for uri: /secured-api
I would expect MyFilter to be called only for secured end-points, I don't care if an expired token is used to access an unprotected end-point.
Any advise on how to properly wire spring-security to achieve just that?
Working solution where the filter is scoped by the securityMatcher:
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.securityMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/public/**")))
.addFilterAt(new MyFilter(), BasicAuthenticationFilter.class)
.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
.build();
}

How to remove AbstractHttpConfigurer from default HttpSecurity

I'm creating an internal lib, and I want to perform some autoconfiguration that involve removing security customizers that are added by default, for example LogoutConfigurer, as if it was not part of the default HttpSecurity prototype bean:
#Bean
public SecurityFilterChain authFilter(HttpSecurity http) throws Exception {
return http
// I want to make this unnecessary by not being part of the (adjusted) HttpSecurity
//.logout().disable()
.authorizeRequests()
.mvcMatchers("/secured").hasRole("ROLE")
.anyRequest().denyAll()
.and()
.build(); // (1)
}
The way to customize security in a cross-cutting way like that seems to be implementing AbstractHttpConfigurer beans, yet those are only triggered as part of the HttpScurity#build() method that generates a SecurityFilterChain as part of the main application security configuration code (marked as (1) above). That is too late, as my bean would need to be undoing the configuration done by another customizer just before it, which is complicated and maybe not possible (removing filters, etc).
The only alternative I found so far seems to be overriding the AbstractHttpConfigurer#setBuilder(B) to manipulate the given builder (the HttpSecurity object) into removing the customizers, given that this method is called right after HttpSecurity is created and before making it accessible as a prototype bean:
public class MyHttpConfigurer extends AbstractHttpConfigurer<MyHttpConfigurer, HttpSecurity> {
#Override
public final void setBuilder(HttpSecurity http) {
super.setBuilder(http);
try {
// Do this and/or whatever else you want to do
http.logout().disable();
} catch (Exception e) {
throw new RuntimeException("It failed", e);
}
}
#Override
public final void configure(final HttpSecurity http) throws Exception {}
}
It works as I want, but that looks unstable, abusing the API, and feels it might break without warning. I also found no way to replace the default HttpSecurity prototype builder as it is not conditional.
Is there a cleaner or documented way to achieve this?
I think that the cleanest approach for you to achieve your functionality would be to provide a BeanPostProcessor.
e.g.
#Configuration(proxyBeanMethods = false)
public class CustomSecurityConfiguration {
private static final String HTTP_SECURITY_DEFAULT_BEAN_NAME = "org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity";
#Bean
public static BeanPostProcessor httpSecurityBeanPostProcessor() {
return new BeanPostProcessor() {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HttpSecurity && HTTP_SECURITY_DEFAULT_BEAN_NAME.equals(beanName)) {
HttpSecurity http = (HttpSecurity) bean;
try {
http.logout().disable();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return bean;
}
};
}
}
This is really similar to the example you've proposed. In any case, I do not thing that it is abusing the API since it allows for accessing the HttpSecurity

Spring Authorization Server 0.2.2, how to disable a default authentication provider like (OAuth2TokenRevocation) and override it with a custom one?

I am using the new Spring Authorization Server 0.2.2 and I want to change the logic of the OAuth2TokenRevocationAuthenticationProvider and make my own implementation for the Token Revocation endpoint.
I added a new CustomRevocationAuthenticationProvider
public class CustomRevocationAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//My implementation
try {
//My implementation
} catch (Exception e) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
}
//My implementation
}
#Override
public boolean supports(Class<?> authentication) {
return OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication);
}
and I added this provider to the SecurityFilterChain like this:
#Bean
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.formLogin(Customizer.withDefaults())
.authenticationProvider(new CustomRevocationAuthenticationProvider())
.build();
}
It works good but when I throw a OAuth2AuthenticationException in my implementation, the default OAuth2TokenRevocationAuthenticationProvider get executed and return 200 OK response.
is there any way to disable the default oauth2 provider from handling my exception and getting executed?
Great question. Since we're working on reference documentation, this is a good topic and I'll make a note to cover it in the configuration overview.
Take a look at OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http). When customizing Spring Authorization Server, you will typically need to copy that code and use the configurer directly. Here's an example:
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();
authorizationServerConfigurer.tokenRevocationEndpoint(tokenRevocationEndpoint -> tokenRevocationEndpoint
.authenticationProvider(new CustomRevocationAuthenticationProvider())
);
// ...
http.apply(authorizationServerConfigurer);

Spring security permitall return 401

Spring Security Config
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/favicon.ico").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/manifest.json").permitAll()
.antMatchers("/logo192.png").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
I also tried this but did not produce any result
.antMatchers(HttpMethod.POST, "/api/auth/**").permitAll()
/api/auth/signup return
error: "Unauthorized"
message: "Full authentication is required to access this resource"
path: "/error"
status: 401
Request URL: https://mysuite.ru/api/auth/signup
How can I fix this problem?
UPDATE
#Configuration
public class MvcSecurityConfig implements WebMvcConfigurer {
#Value("${path.frontend}")
private String frontendPath;
#Value("${frontendStaticResourcesPathPatterns}")
private String[] frontendStaticResourcesPathPatterns;
private static final String BASE_API_PATH = "/";
public void addResourceHandlers(ResourceHandlerRegistry registry){
String pathToFrontend = "file:" + this.frontendPath;
String pathToIndexHTML = pathToFrontend + "/index.html";
registry
.addResourceHandler(frontendStaticResourcesPathPatterns)
.setCachePeriod(0)
.addResourceLocations(pathToFrontend);
registry.addResourceHandler("/", "/**")
.setCachePeriod(0)
.addResourceLocations(pathToIndexHTML)
.resourceChain(true)
.addResolver(new PathResourceResolver() {
#Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
if (resourcePath.startsWith(BASE_API_PATH) || resourcePath.startsWith(BASE_API_PATH.substring(1))) {
return null;
}
return location.exists() && location.isReadable() ? location : null;
}
});
}
}
This is my Spring MVC Config.
Could any of this cause the problem?
I also tried to do permitAll step by step along the path but it didn't work (api/, api/auth, api/autn/**)
By default, Spring Security comes with CSRF Protection enabled, so when you perform an unsafe request (POST, PUT, DELETE) you have to provide a CSRF Token.
In your configure method you can disable it to check if it will work.
http.csrf().disable()
I advise you that disabling CSRF protection can be harmful to your app and you should make sure if you need to use it or not.
Also, if you are using Spring Security's version 5.4 or higher, you can enable trace logs to help you debug it.
logging.level.org.springframework.security=TRACE
You can get more details in the reference docs.
In an Ant matcher, ** matches zero or more directories in a path. Given your request URL you just need to match zero or more characters. Having said that, try replacing your Ant matcher with the following:
.antMatchers(HttpMethod.POST, "/api/auth/*").permitAll()
By pass your filter because any API request throught Filter. Your API can not pass Filter so you get 401 response.
Try add this to your Spring Security Config:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/auth/**");
}
Or add this to OncePerRequestFilter:
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return new AntPathMatcher().match("/api/auth/**", request.getServletPath());
}

Spring security dynamic Role permission role and permission not working

I am implementing a spring security with roles and permission which i fetch from database. It works fine in the case of mapped url only. For unmapped url i an not getting 403. Below is my http configuration. Any help appreciated.
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfigure extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
List<Role> roleModules = roleActionRepository.findAll();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlRegistry = httpSecurity.authorizeRequests();
httpSecurity.csrf().disable();
urlRegistry.antMatchers(
"/authenticate",
"/public/**",
"/common/**"
).permitAll();
roleModules.forEach(roleAction -> {
urlRegistry.antMatchers(HttpMethod.valueOf(module.getType()), module.getName()).hasAuthority(roleAction.getName());
});
urlRegistry.anyRequest().authenticated()
.and().csrf().disable().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Lets say i have one url mapping /employee/** which i get from database. For that my code works fine.
But lets say i have another url like /user/** which is not configured for any role. So ideally on one can access that end point. But i am able to access that point without role assign. So how i can prevent this thing.
You can also find out the screen shot of the role mapping
when ever the urlRegistry.anyRequest().authenticated() called the 4th indenxing is added.

Resources