This question already has answers here:
Spring Security : Multiple HTTP Config not working
(2 answers)
Closed 6 months ago.
I have problem with configuring multiple httpSecurities.
I have some api routes that are protected with JWT tokens.
I want my swagger-ui/index.html route to be protected with basic auth.
I want those API routes to still be protected with JWT token even after user is authenticated with basic auth.
I followed this documetation to create multiple SecurityFilterChains
My problem is whichever FilterChain has #Order(1) works and other FilterChain is completely ignored.
(if filterChain has order 1, routes for products and orders are protected, but swagger-ui/index.html is not protected with basic auth)
Here is my implementation.
#Bean
#Order(1)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider());
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.cors()
.and()
.csrf().disable();
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/products").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/api/orders").hasRole("ADMIN")
.antMatchers("/api/**").permitAll();
http
.addFilter(new JWTAuthenticationFilter(secret, authenticationManager(authConfig)));
http
.addFilterBefore(new JWTAuthorizationFilter(secret), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
#Bean
public SecurityFilterChain swaggerFilterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(authenticationProvider());
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/swagger-ui/index.html","/v3/api-docs/","/v3/api-docs")
.authenticated()
.and()
.httpBasic();
return http.build();
}
You should use one of the following methods of HttpSecurity at least in one of your filters:
antMatcher(String), mvcMatcher(String), regexMatcher(String), requestMatcher(RequestMatcher), requestMatchers().
This will help you to configure certain HttpSecurity to only be invoked when matching the provided patterns.
You've used the last method in the second filter, but did not provide any matchers to the configurer.
So, try to rewrite your second filterChain like this:
#Bean
public SecurityFilterChain swaggerFilterChain(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/swagger-ui/index.html","/v3/api-docs/","/v3/api-docs")
.and()
.authenticationProvider(authenticationProvider())
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
return http.build();
}
Also mind that your swaggerFilterChain might be invoked first if you don't want to harcode all other endpoints' urls in the other filter chain - if a request matches a filter with first order it will be the only filter to be applied, so others will be ignored.
So you also need to change the order - place #Order(1) to your swaggerFilterChain and remove this annotation from the other filter chain.
try this, move .antMatchers("/api/**") to the beginning like the doc
#Bean
#Order(1)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authenticationProvider(authenticationProvider());
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.cors().and().csrf().disable();
http.antMatchers("/api/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/products").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/api/orders").hasRole("ADMIN")
.anyRequest().permitAll();
http.addFilter(new JWTAuthenticationFilter(secret, authenticationManager(authConfig)));
http.addFilterBefore(new JWTAuthorizationFilter(secret), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
Related
I'm trying to apply latest version of spring configuration. I want to permit all to the h2-console but the application still wants me to authorise.
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests()
.shouldFilterAllDispatcherTypes(false)
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/h2-console/**").permitAll()
.and()
.authorizeHttpRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.headers().frameOptions().disable();
return http.build();
}
I've tried to even change the url of h2-console but it didn't help. The behaviour is weird because .requestMatchers("/api/v1/auth/**").permitAll() works fine.
Please re-write the code like this and try again.
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeRequests()
.antMatchers("/h2-console/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.build();
}
To permit different paths you should use:
http
.authorizeHttpRequests()
.antMatchers("/api/v1/auth/**", "/h2-console/**").permitAll()
.and()
...
Keep in Mind: The purpose of requestMatchers() is to specify which requests the spring security configuration will be applied to.
When I launch my webapp I want spring to redirect to my login.jsp in order to authenticate before it goes to my home.jsp but when the app starts it immediately goes to my home.jsp. I created this SecurityFilterChain which I had thought would default to my login.jsp for authentication.
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login", "/register").permitAll()
.and()
.formLogin()
.loginPage("/login").permitAll();
return http.build();
}
}
If more information is needed please let me know.
Edit
As pointed out by jzheaux down below. The naming convention of the method doesn’t matter. The fix was adding the .anyRequest().authenticated() which makes sure any other page besides the ones I had specified above it require authentication before you’re able to go to them.
I found what was wrong. I was using filterChain rather than the securityFilterChain. I also modified the method a little bit as shown here
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.antMatchers("/login", "/register").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.loginProcessingUrl("/submit-login")
.permitAll()
)
.logout(LogoutConfigurer::permitAll);
return http.build();
}
Once making these changes the app defaulted to opening up my login.jsp for authentication before continuing on. Thank you all for the help.
How can i disable basic authentication for one request and leave it for all any requests.
I try do it, but these not work for mi.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/registration").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
Basic authentication still work for "/registration".
I assume the /registration is a web page which you have created. Then it should be
http.csrf()
.disable()
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET,"/registration")
.permitAll()
.anyRequest()
.authenticated()
You should use HttpMethod.POST if it is an API endpoint and not a webpage, or for some reason if you have a POST request for the /registration as well then remove the HttpMethod.GET all together and just leave /registration in the antMatchers
My aim is to add security class to my Java project except paths like "api/public/*".
When I request in POSTMAN
http://localhost:8080/api/public/signup
with a json body, I get 401. Here's my security class which permits all matchers of api/public/*:
What am I missing?
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because our token is invulnerable
.cors()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
// don't create session
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
JwtAuthorizationTokenFilter authenticationTokenFilter = new JwtAuthorizationTokenFilter(userDetailsService(), jwtTokenUtil);
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity
.headers()
.frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
.cacheControl();
}
#Override
public void configure(WebSecurity web) throws Exception {
// AuthenticationTokenFilter will ignore the below paths
web
.ignoring()
.antMatchers("/api/public/*");
}
Mvn clean solved my problem. It seems build somehow stuck in a previous state.
mvn clean
I created a web application using a sample project in GitHub. However, it required authentication for all crud operations. I want to restrict this security checking for all read DB operations. What changes do I need?
These are the related classes:
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability")
.permitAll()
.antMatchers(HttpMethod.GET, "/api/polls/**", "/api/users/**")
.permitAll()
.anyRequest()
.authenticated();
// Add our custom JWT security filter
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
As far as i know there is no way to magically create a read only user.However, you can create a role such as ROLE_UPDATE and make all of your methods that perform creates/updates/deletes be secured via #Secured("ROLE_UPDATE"). Then, if a user is not granted the ROLE_UPDATE authority, they will not be able to call any of the 'writing' methods, and therefore it will be restricted to only call 'read' methods.
Generally, Spring Security doesn't have such feature. You can do as #Alien suggested create some role (ex. ROLE_WRITE and then check on the resources if the user who is trying to access the resource has the correct role
#PreAuthorize("hasRole('ROLE_WRITE')")
public String someWriteOperation() {
}
The other way (but it's only applying when your JPA framework allows you such feature) it's create a Filter in spring and then before processing your request further in the chain create transaction read-only:
#Component
#Order(1)
public class TransactionFilter implements Filter {
#Override
public void doFilterServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//Create read only transaction
// ex. if(isUserReadOnly(Security ....getUser())) {DBSession.setReadOnly(true);}
//Remember it will work only if your JPA framework have the feature - explore your code/framework before
chain.doFilter(request, response);
}
}
Remember filter order should be after Spring Security Filter