I have a spring project with role based system. When i send put request to secured resource and have access denied exception i expected redirect to html page, but i have exception org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'PUT' not supported
My spring config, csrf disabled
private void commonConfigure(HttpSecurity http) throws Exception {
setupCsrfFilter(http);
http.authorizeRequests().accessDecisionManager(accessDecisionManager())
.antMatchers("/**").hasAnyAuthority(ADMIN)
.and().httpBasic()
.and().jee().j2eePreAuthenticatedProcessingFilter(j2eePreAuthenticatedProcessingFilter())
.mappableAuthorities(ADMIN)
.and().exceptionHandling().accessDeniedPage("/unauthorized.html")
.and().headers().contentTypeOptions()
.and().xssProtection()
.and().frameOptions()
.and().httpStrictTransportSecurity()
.and().contentSecurityPolicy(CONTENT_SECURITY_POLICY);
}
Related
I have a spring boot app which provides HTML page service via / and also rest api via /api. The former requires login via a Login form and the later requires HTTP basic auth, and hereby, I configure two HttpSecurity section as follows:
#Configuration
#Order(1)
public static class ApiSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.cors().and().csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic(Customizer.withDefaults());
}
}
#Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.cors().disable()
.authorizeRequests()
.antMatchers("/login", "/js/**", "/css/**").permitAll()
.antMatchers("/**").hasRole("ADMIN")
.and().formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/index")
.and().logout().invalidateHttpSession(true)
.deleteCookies("JSESSIONID").logoutSuccessUrl("/login").permitAll();
}
}
The configuration works perfectly in normal case. However, if wrong credentials are provides in rest api clients, an HTML code of login page with HTTP status code 200 returns from the app instead of an excepted json message with HTTP status code 401 or 403.
I'm afraid it is because the url pattern of /api/** both matches "/api/**" and "/**", and therefore, the request will pass both the filter chain for rest api and HTML page. Finally, because of the lower order of formLogin, a login page returns in the case.
So, How can I get the excepted result for rest api clients? Is there an only solution to separate the two url patterns which should not match each other?
Addition 1:
I think there are three cases which will raise exceptions in the security filter chain:
No credentials provided;
Wrong credentials provided;
Right credentials provided but not matched the required roles
And the results for the cases are as follows:
Return HTTP status 401 with a json error message
Return HTTP status 302 and try to redirect to login page
Return HTTP status 403 with a json error message
Therefore, it seems that only the case of wrong credentials provided will
be routed to /error endpoint (as what Eleftheria said in the answer), and the difference between 1,3 and 2 is the exception type -- org.springframework.security.access.AccessDeniedException: Access is denied for 1 and 3; org.springframework.security.authentication.BadCredentialsException: Bad credentials for 2.
In the formLogin() case, if the BadCredentialsException rises, it will be routed to the failure-url, but how to configure the failure-url in the httpbasic case? (seems no such method in HttpBasicConfigurer)
This is happening because the failed authentication is throwing an exception and the "/error" page is secured by your second filter chain.
Try permitting all requests to "/error" in your second filter chain.
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/error").permitAll()
// ....
Note that the request will only be processed by one filter chain, and that is the first filter chain that it matches.
The request to "/api/123" is only processed by the first filter chain, however the second filter chain was invoked because there was an error.
What I wanted to do is build a Rest backend application that needs the Authorization header on every request, validate and return the data or 401 Unauthorized if the user or password is wrong.
I have Spring Security in my classpath with this simple configuration:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.withUser("user")
.password("pass")
.roles("USER");
}
}
But one thing is not working correctly: when I make a request with valid username and password with Postman, the server responds the data correctly, but if I change the password to a wrong password and keep the correct username, the server stills responds with the data and OK status, as if it is using a JSESSIONID Cookie to check the further requests.
Is that a way of using spring security only for checking the header Authorization (with no cookies, sessions nor saving user informations or login and logout pages), or is better to just use Filters for doing that instead?
Thank you for any help !
Just add the following to your configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
This will configure spring security to never create a cookie, every request must reauthenticate.
I am developing a rest api using Spring Boot. I have a class that extends UsernamePasswordAuthenticationFilter, where I override the attemptAuthentication method. In the process of authentication, Spring internally uses my CustomUserDetailsService that throws an exception in case no user is found. Spring internally handler that exception and throws InternalAuthenticationServiceException. The issue is, I am not able to catch that exception in my central place where I customize the response in case of errors. I am using #ControllerAdvice for that, but it seems Spring just ignore and always send default "Internal Server Error".
in order to capture a "failed authentication" just override the method unsuccessfulAuthentication in your filter
#Override
protected void unsuccessfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed)
throws IOException, ServletException {
//handle errors
}
I'm getting 403 forbidden error when using Spring boot security for basic authentication. I get this error when using the POST method.
My main class code is as follows,
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder)
{
return builder.sources(MyContext.class);
}
Please suggest a solution for this. Thanks
I get this error using Post method.
Above line gives hint that the issue is due to CSRF protection. If users will not be using your application in a web browser, then it is safe to disable CSRF protection. Otherwise you should ensure to include the CSRF token in the request.
To disable CSRF protection you can use the following:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.csrf().disable();
}
Refer spring-security-csrf
I have an application that exposes a REST API and is secured using Spring Security. Is there a way to automatically redirect the client (from the server side) to the login page if a request sent to my server results in 401 - unauthorised?
For spring-security application based on spring-boot.
Define a handler bean:
#Component
public class CommenceEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = 565662170056829238L;
// invoked when user tries to access a secured REST resource without supplying any credentials,
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
// send a json object, with http code 401,
// response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
// redirect to login page, for non-ajax request,
response.sendRedirect("/login.html");
}
}
In security config class (e.g WebSecurityConfig):
Autowire the bean:
#Autowired
private CommenceEntryPoint unauthorizedHandler; // handle unauthorized request,
Specify handler:
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // excepion handler,
Tips:
This is suitable only for non-ajax request,
For ajax request, better return 401 code, and let the frontend handle it.
If you want to use 401 response, in CommenceEntryPoint.commence() just use response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); instead of response.sendRedirect().
You can specify how your application handles exception or HTTP status codes by specifying it in error-page element of web.xml
eg: web.xml
<error-page>
<error-code>401</error-code>
<location>/login.html</location>
</error-page>
Same way you handle other HTTP status code viz 404 for page not found page.
I solved this issue by adding the following elements to my Spring Security XML under an http node:
<security:access-denied-handler error-page="/#/login" />
<security:session-management invalid-session-url="/#/login" session-fixation-protection="changeSessionId" />