How to resolve SpringBoot 405 Method Not Allowed Error? - spring-boot

I am expecting to receive the message "Works" on Postman (used for temporary testing until I implement the frontend and forms using HTML and CSS) when I type in the URL "http://localhost:8080/registration". This should be happening based on the code posted below. Instead, I receive the 405 Method Not Allowed Error.
WebSecurityConfig class
package com.example.finalVersionBookProject.security.config;
import com.example.finalVersionBookProject.appuser.AppUserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final AppUserService appUserService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig(AppUserService appUserService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.appUserService = appUserService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
Registration class
package com.example.finalVersionBookProject.registration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
#Controller
#RequestMapping(path = "/registration")
public class Registration {
private RegistrationService registrationService;
#PostMapping()
public String register(#RequestBody RegistrationRequest request) {
return registrationService.register(request);
}
}
Registration Service Class
package com.example.finalVersionBookProject.registration;
import org.springframework.stereotype.Service;
#Service
public class RegistrationService {
public String register(RegistrationRequest request) {
return "works";
}
}
I have tried replacing RestController with Controller, changing some of the annotations, both with no success. I tried using the IntelliJ debugger to resolve the issue, but it did not provide any new information that was helpful in solving the problem. I am expecting the message "Works" to be outputted to Postman, but I get the 405 Error.

When you type in URL http://localhost:8080/registration in Postman, what method did you use? I suspect you are using GET whilst your controller has a #PostMapping which support POST method.
You can try to use #GetMapping instead.

Related

AuthenticationManager.authenticate method not getting called

I am trying to follow the API Key authentication code from this answer:
https://stackoverflow.com/a/48448901
I created my filter class:
package com.propfinancing.CADData.web;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class PullAPIKeyFromHeaderFilter extends AbstractPreAuthenticatedProcessingFilter {
private String apiKeyHeaderName;
public PullAPIKeyFromHeaderFilter(String apiKeyHeaderName) {
this.apiKeyHeaderName = apiKeyHeaderName;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String headerValue = request.getHeader(apiKeyHeaderName);
return request.getHeader(headerValue);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return apiKeyHeaderName;
}
}
And then I implemented my security configuration:
package com.propfinancing.CADData.web;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
#Configuration
#EnableWebSecurity
#Order(1)
public class APIKeySecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${caddata.apiKey.header.name}")
private String apiKeyHeaderName;
#Value("${caddata.apiKey}")
private String apiKey;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
PullAPIKeyFromHeaderFilter pullAPIKeyFromHeaderfilter = new PullAPIKeyFromHeaderFilter(apiKeyHeaderName);
AuthenticationManager authManager = new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!apiKey.equals(principal))
throw new BadCredentialsException("Invalid API key");
authentication.setAuthenticated(true);
return authentication;
}
};
pullAPIKeyFromHeaderfilter.setAuthenticationManager(authManager);
httpSecurity.antMatcher("/**");
httpSecurity.addFilter(pullAPIKeyFromHeaderfilter);
httpSecurity.requiresChannel().anyRequest().requiresSecure();
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlAuthConfigurer = httpSecurity.authorizeRequests();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl = urlAuthConfigurer.anyRequest();
authorizedUrl.authenticated();
}
}
When I do an external call to the application with the header as part of the request, I get a 403 Forbidden response.
I can see the filter pulling the key from the header. That part is working.
But, the authenticate() method is not being called to check if the header is valid.
I am not sure what I missed, the code looks the same to me.
Any ideas?
Looks like the wrong base class, per https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.html :
The purpose is then only to extract the necessary information on the
principal from the incoming request, rather than to authenticate them.
Try extending https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html instead.
I was not able to get the code above to work, but I changed it to use the second answer on the thread (Using a Filter) https://stackoverflow.com/a/63852212 It works as expected.

Setters for session scoped bean not working in RestController

I'm trying to get the username from the session scoped bean once the user has logged in using /users/login
I've Autowired a session scoped bean into a RestController and in one of the endpoints in the rest controller, I'm invoking a setter on the session scoped bean. But the effect of setters is not visible for requests from the same session.
The following is my session scoped bean:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
#Getter
#Setter
public class SessionSpecificUserDetails implements Serializable {
private String userName;
}
import lombok.Getter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.annotation.SessionScope;
#Getter
#Configuration
public class UserSessionDetailsConfiguration {
#Bean
#SessionScope
public SessionSpecificUserDetails sessionSpecificUserDetails() {
return new SessionSpecificUserDetails();
}
}
The following is the RestController
import com.course.backend.coursebackend.config.SessionSpecificUserDetails;
import com.course.backend.coursebackend.dao.User;
import com.course.backend.coursebackend.repository.UserRepository;
import com.course.backend.coursebackend.utils.ResponseUtils;
import com.course.backend.coursebackend.utils.UserUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
UserRepository userRepository;
#Autowired
SessionSpecificUserDetails sessionSpecificUserDetails;
#GetMapping("/getLoggedInUser")
public String getLoggedInUser() {
return sessionSpecificUserDetails.getUserName();
}
#PostMapping("/login")
public ResponseUtils.CustomResponse login(#RequestBody User user) {
List<User> users = getAllUsers();
Optional<User> optionalUser = users.stream()
.filter(currentUser -> currentUser.getUserName().equals(user.getUserName()))
.findAny();
if (optionalUser.isEmpty()) {
return ResponseUtils.getErrorResponse(List.of("Username not found!"));
}
if (optionalUser.get().getPassword().equals(user.getPassword())) {
sessionSpecificUserDetails.setUserName(user.getUserName());
return ResponseUtils.getSuccessResponse("Login Successful!");
}
return ResponseUtils.getErrorResponse(List.of("Login failed due to wrong password!"));
}
}
I see that spring is creating proxy for the session scoped bean. (And may be because of that my setters are not having any effect even for the same session?)
My question is what's the correct way to use the session scoped beans in the RestController? And what's the good way to get the username for the same session across requests?
I tried marking UserController also as #SessionScope but that's also not working.

#PropertyResource is not working in Spring Boot

Below is my Property class:
package com.example.sdasDemo;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#PropertySource("classpath:info.properties")
#ConfigurationProperties(prefix = "feeder")
#Getter
#Setter
public class Properties {
public String[] id;
}
Below is the simple controller:
package com.example.sdasDemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class controller {
#Autowired
Properties properties;
#RequestMapping("/get")
public String getData(){
System.out.println(properties.getId());
return properties.getId();
}
}
Below is the info.properties file
feeder.id=soumik,das,21022021
http://localhost:8080/get returns null pointer exception
Why this #PropertyResouce is not working? What am I missing here. Please suggest.
I found the problem. It was happening due to wrong profile name provided. For that reason, the property file location could not be found and resulting null pointer exception.

Spring rest controller does not validate my DTO

I have this request and response:
#Data
public class TestRequestDto {
#Min(7)
private String name;
}
#Data
public class TestResponseDto {
private String response;
}
And I have a controller:
package com.example.validation.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
#Slf4j
#RestController
public class TetController {
#PostMapping("/test")
public TestResponseDto getTestResponseDto(#Valid #RequestBody TestRequestDto request){
log.info(request.getName());
TestResponseDto response = new TestResponseDto();
response.setResponse("response");
return response;
}
}
I send a post request({"name":"test"}) with an invalid name but it works. What am I doing wrong?
Starting with Boot 2.3, we also need to explicitly add the spring-boot-starter-validation dependency

How do you setup a resource server in spring boot that only uses role from the jwt token to allow access?

I think I have some odd requirements here because no matter where I look, I can't a specific example for what I was asked to do. I created a dummy project called contacts to test this. I am suppose to secure my api with Oauth2, but the authorization server is not on the same box.
It is my understanding that the client will need to call the authorization to get a token and then the request with the token will be sent to my api.
In my server the scope will determine if the user has access. I am not doing any authentication on my server.
I can't seem to get this to work though.
Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping(value = "/contacts")
#PreAuthorize("#oauth2.hasScope('ec.edm.mdm')")
public class ContactsController {
#Autowired
ContactRepository customerRepo;
#RequestMapping(method = RequestMethod.GET, produces = { "application/json" })
public Page<Contact> findAllContacts(Pageable pagable) {
return customerRepo.findAll(pagable);
}
}
Application
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
#EnableResourceServer
#SpringBootApplication
public class App {
private static final Logger LOG = LoggerFactory.getLogger(App.class);
#Autowired
private Environment environment;
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
/**
* Allows for #PreAuthorize annotation processing.
*/
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}

Resources