"status": 403, "error": "Forbidden", "message": "Forbidden", "path": "/post/create" - spring-boot

I see this response when I try to add new post after authorization by admin.
I have Basic authorization which based on spring boot security:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//...declared fields
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user")
.password("userpass")
.roles("USER")
.and()
.withUser("admin")
.password("adminpass")
.roles("ADMIN", "USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/logout").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.and().logout().permitAll()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I get this message when try to add new post after authorization:
{
"timestamp": "2018-07-04T12:19:25.638+0000",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/post/create"
}
in my controller:
#RestController
public class PostController {
#Autowired
private PostDAO postDAO;
#GetMapping("/posts")
public Page<Post> getAllPosts(Pageable pageable) {
return postDAO.findAll(pageable);
}
#PostMapping("/post/create")
public Post createPost(#Valid #RequestBody Post post) {
return postDAO.save(post);
}
//other end-points........
}
However, read operations from my controller work well but to CRUD operation I haven't access.
There are my dependencies:
dependencies {
compile ('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.hibernate:hibernate-core')
compile('org.springframework.boot:spring-boot-starter-security')
runtime('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')
testCompile('junit:junit')
}
Any idea?
Thanks in advance!

This is due to CSRF enabled. CSRF protection is enabled by default in the Java configuration. We can still disable CSRF using the configuration given below.
http .csrf().disable() .authorizeRequests() .anyRequest().permitAll();
Starting from Spring Security 4.x – the CSRF protection is enabled by default in the XML configuration as well; we can of course still disable it if we need to:
<http>
...
<csrf disabled="true"/>
</http>
Note : CSRF is an attack which forces an end user to execute unwanted
actions in a web application in which is currently authenticated.

here's why:
csrf is automatically enabled in spring security,and I recommended you do not disable csrf.
normally your html form tag should include a hidden field which generates csrf token, however, thymeleaf automaticlly do that for you, you should check your html tag to see whether or not a "th:" was included, if not, include a "th:" before "action" in form tag, do this, thymeleaf generates csrf token invisibablly.

Related

Spring Security returns 403 Forbidden to expired jwt token. How can I change it to 401 Unauthorized response with custom body?

I have created application for generating JWT tokens for users that either logging in or registering.
I have another demo endpoint for getting some data from API.
But because the project is Spring Security it checks if token that has been passed in Bearer Header is valid or not. And It detects the token is expired giving this ExpiredJwtException. I want to catch that exception and return custom Response to Client.
Exception when jwt token is expired:
io.jsonwebtoken.ExpiredJwtException: JWT expired at
2023-02-16T10:54:29Z. Current time: 2023-02-16T12:44:41Z, a difference
of 6612302 milliseconds. Allowed clock skew: 0 milliseconds. at
io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:427)
~[jjwt-impl-0.11.5.jar:0.11.5] at
io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:529)
~[jjwt-impl-0.11.5.jar:0.11.5] at
io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:589)
~[jjwt-impl-0.11.5.jar:0.11.5] at
io.jsonwebtoken.impl.ImmutableJwtParser.parseClaimsJws(ImmutableJwtParser.java:173)
~[jjwt-impl-0.11.5.jar:0.11.5]
But It returns 403Forbidden with empty body.
How can I change this response?
This is my SecurityConfiguration class:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class SecurityConfiguration{
private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests()
.requestMatchers("/api/v1/auth/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
This is my ErrorHandler:
#RestControllerAdvice
#Log4j2
public class ErrorHandler extends ResponseEntityExceptionHandler {
private static final String LOG_ERROR = "Error. {} \n{}";
//There are other methods here that handle other exceptions
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#ExceptionHandler(ExpiredJwtException.class)
public ResponseEntity<ErrorResponse> handleJwtExpiredTokenException(ExpiredJwtException ex) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new ErrorResponse(ErrorCode.TOKEN_EXPIRED, ex.getMessage()));
}
}
I explained issue to ChatGpt. It recommended some code changes like adding new AuthenticationEntryPoint and others. But this didn't solve my problem

Spring Security Context Authentication is null

i am trying to add couple of filters in my request processing in spring boot security config.
Below is my code
#EnableWebSecurity
#Configuration
public class JwtSecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(this::configureEndpoints)
return http.build();
}
private void configureEndpoints(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry){
authorizationManagerRequestMatcherRegistry.mvcMatchers("/permit")
.permitAll()
.mvcMatchers("/block")
.denyAll()
.and()
.mvcMatcher("/api")
.addFilterBefore(new Filter1(), SecurityContextHolderAwareRequestFilter.class)
// register TenantFilter in the chain after the SecurityContext is made available by the respective filter
.mvcMatcher("/api")
.addFilterAfter(new Filter2(), SecurityContextHolderAwareRequestFilter.class)
.authorizeHttpRequests()
.mvcMatchers("/api")
.authenticated()
.and();
}
}
It seems the authentication does not happen and filters are never hit.
If i try to access the authentication in my runtime code i get SecurityContextHolder.getContext().getAuthentication() as null.
Seems to some problem in the security configuration only.

spring security implementation to secure REST APIs in my spring boot project

I have a RestController which has one API that is used to authenticate a user. I want this API to be accessible by anyone whether authenticated or not and irrespective of their roles. In other words, this API should be called when someone types in their username and password and presses submit button.
Here is the config(HttpSecurity http) method of security config java file
#Override
protected void configure(HttpSecurity http) throws Exception {
http/* .csrf().disable() */
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/customer/**").hasRole("CUSTOMER")
.antMatchers("/supplier/**").hasRole("SUPPLIER")
.antMatchers("/user/authenticate").permitAll()
.antMatchers("/user/**").authenticated()
.and()
.formLogin().permitAll()
.and()
.logout()
.permitAll();
}
In the above code, I've written ''antMatchers("/user/authenticate").permitAll()'' because I want this url to be accessible by everyone and whatever logic is written in the controller should be executed.
And here is my controller
#RestController
#RequestMapping("/user")
public class AuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#PostMapping("/authenticate")
public void authenticate(#RequestBody AuthenticationRequest request) {
Authentication token = authenticationManager.authenticate(new
UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
// more code
}
}
But I don't know why it is not working. When I send a POST request from POSTMAN, I get the following response back:
{
"timestamp": "2020-07-24T08:50:02.514+00:00",
"status": 403,
"error": "Forbidden",
"message": "Forbidden",
"path": "/user/authenticate"
}
Someone please suggest what I should do to make it hit my REST controller

Spring Boot Security Authentication from third API

I build a web app using spring boot + thymeleaf,
but this project is client (not backend / not using database), i'm consuming third API (login, store data,load data,update data, delete data),
i have a problem when implementation spring boot security, username and password authentication with third API,
this endpoint for login auth (third API)
http://kuala/app/directory/user/login?j_username=admin&j_password=admin
success response
{
"isAdmin": "true",
"username": "admin"}
failed response
{
"error": {
"date": "Fri Jan 24 10:29:26 ICT 2020",
"code": "401",
"message": ""
}}
this sample SecurityConfig
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.antMatchers("/user/**").hasAuthority("USER")
.anyRequest()
.authenticated().and().csrf().disable().formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.defaultSuccessUrl("/home")
.usernameParameter("username")
.passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
auth.userDetailsService(appUserDetailsService);
}}
any one help me,
thanks in advance
best regard
Khafidz
One way is to create your own AuthenticationManager bean, and delegate the authenticate call to the 3rd party (using a RestTemplate, for example):
#Component
public class CustomAuthenticationManager implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// Call your 3rd party API and return an Authentication object based
// on its response
ResponseEntity loginResponse = restTemplate.exchange(...);
if(loginResponse.getStatusCode() == HttpStatus.OK) {
// create a valid Authentication object with roles, etc
}
else {
// throw an exception such as BadCredentialsException
}
}
}

Problems using Spring login in REST with CORS

I am trying to implement a website using REST. My strategy to authenticate the users consist of sending a JWT token to the user in reply to a username/password combination sent via POST. The most relevant part of my security conf is shown below.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/images/**", "/scripts/**", "/styles/**", "favicon.ico");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.loginProcessingUrl("/login") //Not necesary because is the default
.permitAll().and()
.authorizeRequests()
.antMatchers("/api/getStatistics").permitAll()
.anyRequest().denyAll().and()
.addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler() {
return new RESTAuthenticationSuccessHandler(jWTTokenService());
}
#Bean
public JWTTokenService jWTTokenService() {
return new JWTTokenServiceImpl();
}
To allow the CORS access I have written the following lines in a class extending of WebMvcConfigurerAdapter
#Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
.allowedMethods("GET", "POST", "OPTIONS")
.allowCredentials(true).maxAge(3600);
registry.addMapping("/login")
.allowedOrigins("*")
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
.allowedMethods("POST", "OPTIONS")
.allowCredentials(true).maxAge(3600);
}
So when I make a call to /login sending the username and password it is supposed that Spring will catch the request, will process it and then will redirect to the success or failure handler.
Well, instead of that I have gotten an 403 Forbidden response during the CORS preflight. I decide to debug the program because I thought that when I wrote formLogin(), the UsernamePasswordAuthenticationFilter create a new AntPathRequestMatcher with the value ("/login", "POST").
What I found in the debug console was the following
Request 'OPTIONS /login' doesn't match 'POST /login
Of course it does not! Some hours later trying to solve the problem I discovered that everything works if I declare a empty method /login because during the preflight Spring finds the method and then send a 200OK to the client so the client then is allowed to send a POST that is captured by the UsernamePasswordAuthenticationFilter.
#Controller
public class LoginController {
#RequestMapping(value = { "/login" }, method = RequestMethod.POST)
public void dummyLogin() {
}
}
So, my question is: Should I really declare an empty method to "cheat" during the CORS preflight or it is just that I have missed something? Because it is not so elegant to declare a dummy method when you really want to delegate the job to the UsernamePasswordAuthenticationFilter...
The problem is that org.springframework.security.web.authentication.logout.LogoutFilter and org.springframework.security.web.authenticationUsernamePasswordAuthenticationFilter do not continue with the filter chain if they handled a login/logout. And since the configuration via WebSecurityConfigurerAdapter is processed later in the chain, the CorsProcessor is never applied.
I decided to keep the old solution and use a org.springframework.web.filter.CorsFilter.
It is not necessary to have empty method to make it work. The only thing you have to do is to allow OPTIONS call on the /login URL.
.antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
Ex :
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.loginProcessingUrl("/login") //Not necesary because is the default
.permitAll().and()
.authorizeRequests()
.antMatchers("/api/getStatistics").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
.anyRequest().denyAll().and()
.addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}

Resources