How can we use validation in spring boot for Restfull Apis - spring-boot

#ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
Map<String, Object> responseBody = new LinkedHashMap<>();
responseBody.put("timestamp", new Date());
responseBody.put("status", status.value());
List<String> errors = ex.getBindingResult().getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toList());
responseBody.put("errors", errors);
return new ResponseEntity<>(responseBody, headers, status);
}
}
Actually i am literally new in kotlin i try to to create some restfull apis with the use of spring boot but here i stuck that how can we add validation in this here i find some code now its in java and i am a kotlin develpore.their anyone can change this in kotlin

Related

Spring Boot instinitate #Bean according to Condition

im working in spring boot project where i want to instantiate a Restemplate Bean with Interceptors , my issue that i don't want to duplicate the code because there is just the header that changes for each conciguration. this is my code :
#Bean
#Qualifier("restTemplateOne")
public RestTemplate restTemplateWithAccessToken() {
return new RestTemplateBuilder()
.interceptors((HttpRequest request, byte[] body, ClientHttpRequestExecution execution) -> {
//this is the only header that i want to add for
request.getHeaders().set("MY_PARTICULAR_HEADER", "my value");
request.getHeaders().set(HttpHeaders.AUTHORIZATION,"my auth value");
return execution.execute(request, body);
}).build();
}
#Bean
#Qualifier("restTemplateTwo")
public RestTemplate restTemplateWithIdToken() {
return new RestTemplateBuilder()
.interceptors((HttpRequest request, byte[] body, ClientHttpRequestExecution execution) -> {
request.getHeaders().set(HttpHeaders.AUTHORIZATION,"my auth value");
return execution.execute(request, body);
}).build();
}
#Autowired
#Qualifier("restTemplateOne")
private RestTemplate restTemplateOne;
#Autowired
#Qualifier("restTemplateTwo")
private RestTemplate restTemplateTwo;
do you have any idea how i can optimize code to avoid duplication . something like adding a parameter to the method and adding the header or not according to the condition.
Thanks in advance.
Just extract and parameterize your interceptor:
#Bean
#Qualifier("restTemplateOne")
public RestTemplate restTemplateWithAccessToken() {
return new RestTemplateBuilder()
.interceptors(new CustomClientHttpRequestInterceptor(true))
.build();
}
#Bean
#Qualifier("restTemplateTwo")
public RestTemplate restTemplateWithIdToken() {
return new RestTemplateBuilder()
.interceptors(new CustomClientHttpRequestInterceptor(false))
.build();
}
private static class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private boolean needParticularHeader;
public CustomClientHttpRequestInterceptor(boolean needParticularHeader) {
this.needParticularHeader = needParticularHeader;
}
#Override
public ClientHttpResponse intercept(HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution) throws IOException {
if (needParticularHeader) {
//this is the only header that i want to add for
request.getHeaders().set("MY_PARTICULAR_HEADER", "my value");
}
request.getHeaders().set(HttpHeaders.AUTHORIZATION, "my auth value");
return execution.execute(request, body);
}
}

Required parameters exception doesn't work in spring boot 2.x

In my spring boot application, I tried to handled the Required parameter exception. This question may be duplicated. But the answers posted don't help me.
My controller
#GetMapping("/test")
public ObjectId test(#RequestBody OIdLGroupIds OIdLGroupIds,#RequestParam ObjectId _id){
return videoService.test();
}
My global exception handler is like following.
#RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse("Server Error", details);
return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(NotFoundHandler.class)
public final ResponseEntity<Object> handleRecordNotFoundException(NotFoundHandler ex, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("message", "Record not found");
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorResponse error = new ErrorResponse("Record Not Found", details);
return new ResponseEntity(error, HttpStatus.NOT_FOUND);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
MethodArgumentNotValidException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", LocalDate.now());
body.put("status", status.value());
Set<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(x -> x.getDefaultMessage())
.collect(Collectors.toSet());
body.put("details", errors);
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
}
It doesn't throw any details (Body is blank). But the response status is 400 Bad Request. But when I comment all above codes, it throws default exceptions with body.
I tried this also
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String name = ex.getParameterName();
System.out.println(name);
logger.error(name + " parameter is missing");
return super.handleMissingServletRequestParameter(ex, headers, status, request);
}
I have tried in many ways, But no luck. Did I miss anything? Please help me. Thanks in advance.
Empty request body raises HttpMessageNotReadableException.
#RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
String name = ex.getParameterName();
System.out.println(name);
logger.error(name + " parameter is missing");
return super.handleMissingServletRequestParameter(ex, headers, status, request);
}
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
System.out.println(ex.getMessage());
logger.error("Request body is missing");
return super.handleHttpMessageNotReadable(ex, headers, status, request);
}
}
If you allow empty body, use #RequestBody(required = false).

Spring Webflux OAuth 2 resource server

I have a Spring OAuth 2 server based on Spring Boot 1.5 (Spring Security v4) which generates customized tokens and a few resource servers who communicate with this authorization server, making use of /oauth/check_token endpoint by configuration of RemoteTokenServices.
All the logic related to storing/retrieving tokens on Authorization server side is done with JdbcTokenStore.
I am building a new Spring Boot 2 application which is build with Spring webflux module and trying to implement client_credentials flow with existing Authorization Server using Spring Security 5.1.1.
I found that support for resource servers was added in 5.1.0.RC1 (https://spring.io/blog/2018/08/21/spring-security-5-1-0-rc1-released#oauth2-resource-servers) and updated in 5.1.0.RC2 (https://spring.io/blog/2018/09/10/spring-security-5-1-0-rc2-released#oauth2-resource-server) but looks like it's only possible to configure it with JWT support.
I might be messing up with concepts here but looking for more info and a way to configure all these components together.
I'm in same situation as you.I solve that problem in next way, maybe it can help you:
spring-boot-starter-parent.version: 2.1.1
spring-cloud-dependencies.version: Greenwich.R1
Security configuration:
#EnableWebFluxSecurity
public class SecurityConfig {
#Autowired
private ReactiveAuthenticationManager manager; //custom implementation
#Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return http
.authorizeExchange()
.pathMatchers("/role").hasRole("ADMIN")
.pathMatchers("/test").access(new HasScope("server")) //custom implementation
.anyExchange().authenticated()
.and()
.httpBasic().disable()
.oauth2ResourceServer()
.jwt()
.authenticationManager(manager)
.and().and()
.build();
}
}
ReactiveAuthorizationManager (HasScope) implementation:
Helper which allow search for scopes in authentication object
public class HasScope implements ReactiveAuthorizationManager<AuthorizationContext> {
public HasScope(String...scopes) {
this.scopes = Arrays.asList(scopes);
}
private final Collection<String> scopes;
#Override
public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext object) {
return authentication
.flatMap(it -> {
OAuth2Authentication auth = (OAuth2Authentication) it;
Set<String> requestScopes = auth.getOAuth2Request().getScope();
boolean allow = requestScopes.containsAll(scopes);
return Mono.just(new AuthorizationDecision(allow));
});
}
}
ReactiveAuthenticationManager implementation:
That is the main component in configuration which create OAuth2Authentication. There is a problem with response for wrong access_token, it returns only status code without body response.
#Component
public class ReactiveAuthenticationManagerImpl implements ReactiveAuthenticationManager {
private final ResourceServerProperties sso;
private final WebClient.Builder webClient;
private final ObjectMapper objectMapper;
private AuthoritiesExtractor authoritiesExtractor = new FixedAuthoritiesExtractor();
public ReactiveAuthenticationManagerImpl(ResourceServerProperties sso,
#Qualifier("loadBalancedWebClient") WebClient.Builder webClient, ObjectMapper objectMapper) {
this.sso = sso;
this.webClient = webClient;
this.objectMapper = objectMapper;
}
#Override
public Mono<Authentication> authenticate(Authentication authentication) {
return Mono.just(authentication)
.cast(BearerTokenAuthenticationToken.class)
.flatMap(it -> getMap(it.getToken()))
.flatMap(result -> Mono.just(extractAuthentication(result)));
}
private OAuth2Authentication extractAuthentication(Map<String, Object> map) {
Object principal = getPrincipal(map);
OAuth2Request request = getRequest(map);
List<GrantedAuthority> authorities = authoritiesExtractor.extractAuthorities(map);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
token.setDetails(map);
return new OAuth2Authentication(request, token);
}
private Object getPrincipal(Map<String, Object> map) {
if (map.containsKey("principal")) {
try {
//that is the case for user authentication
return objectMapper.convertValue(map.get("principal"), UserPrincipal.class);
} catch (IllegalArgumentException ex) {
//that is the case for client authentication
return objectMapper.convertValue(map.get("principal"), String.class);
}
}
return null;
}
#SuppressWarnings({"unchecked"})
private OAuth2Request getRequest(Map<String, Object> map) {
Map<String, Object> request = (Map<String, Object>) map.get("oauth2Request");
String clientId = (String) request.get("clientId");
Set<String> scope = new LinkedHashSet<>(request.containsKey("scope") ?
(Collection<String>) request.get("scope") : Collections.emptySet());
return new OAuth2Request(null, clientId, null, true, new HashSet<>(scope),
null, null, null, null);
}
private Mono<Map<String, Object>> getMap(String accessToken) {
String uri = sso.getUserInfoUri();
return webClient.build().get()
.uri(uri)
.accept(MediaType.APPLICATION_JSON)
.header("Authorization", "Bearer " + accessToken)
.exchange()
.flatMap(it -> it.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {}))
.onErrorMap(InvalidTokenException.class, mapper -> new InvalidTokenException("Invalid token: " + accessToken));
}

How to pass multiple RequestHeader when using #FeignClient

I need to pass multiple Request Headers using #FeignClient
When its one header of type String the #RequestHeader works fine but with multiple I get RequestHeader.value() was empty on parameter 0, while starting the spring boot error .
#RequestMapping(value="/*********employees", method= RequestMethod.GET , consumes = MediaType.APPLICATION_JSON_VALUE)
EmployeeData fetchWorkdayEmployeess(#RequestHeader Map<String, Object> headers);
as well as I tried using #HeaderMap
#RequestMapping(value="/*********employees", method= RequestMethod.GET , consumes = MediaType.APPLICATION_JSON_VALUE)
EmployeeData fetchWorkdayEmployeess(#HeaderMap Map<String, Object> headers);
I also tried passing multiple #RequestHeaders as parameters but it doesn't seem to work
I needed to use a custom RequestInterceptor
#Configuration
class FeignCustomHeaderConfig {
#Bean
public CSODHeaderAuthRequestInterceptor basicAuthRequestInterceptor() {
try {
return new HeaderAuthRequestInterceptor(token_map);
} catch (Exception e) {
log.error(e.getLocalizedMessage());
}
return new CSODHeaderAuthRequestInterceptor(null);
}
class HeaderAuthRequestInterceptor implements RequestInterceptor {
//Expensive OAuth2 flow logic
private HashMap<String, String> tokenMap;
public HeaderAuthRequestInterceptor(HashMap<String, String> tokenMap) {
this.tokenMap = tokenMap;
}
#Override
public void apply(RequestTemplate requestTemplate) {
if(tokenMap == null)
return;
requestTemplate.header(key1, tokenMap.get(key1));
requestTemplate.header(key2, tokenMap.get(key2));
....
}
}
And then add the configuration class to your feign client
#FeignClient(name="....",url="...",configuration=FeignCustomHeaderConfig.class)
Reference link here :

Spring RestTemplate.postForEntry, returns 403 error

I want to communicate with a server in Java area by utilizing RestTemplate class.
But when I access to my server with RestTemplate, it shows just 403(forbidden) error.
my controller code :
#Controller(value="homeController")
public class HomeController {
#RequestMapping(value="/test")
public #ResponseBody Map<String, String> test(#RequestParam(value="message", required=false, defaultValue="hell world") String message){
Map<String, String> map = new HashMap<String,String>();
map.put("greeting", message);
return map;
}
client side's code:
#Test
public void test2(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
map.add("message", "test");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);
ResponseEntity<String> response = restTemplate.postForEntity( url, request , String.class );
System.out.println(response.getBody());
}
If the code works successfully, console should output "test" word.
EDIT:
When I access to the controller, with my web browser, it shows json data correctly.
Now,
How do I fix the code to communicate to server on POST method ?
thanks
As you said you're using spring-security, you can simply add a request Interceptor and add authentication. Spring will create a random password if you don't set one; it will be logged in the console so you can simply copy it from there.
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new BasicAuthorizationInterceptor("username", "yourpassword")));
Another (better) option is to configure the authentication yourself. A simple in memory auth would make
#Configuration
#EnableWebSecurity
public class RestSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated();
http.httpBasic();
http.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("username").password("yourpassword").roles("ADMIN");
}
}
You should specify what kinds of HTTP methods your "test" method handles.
You can do this in #RequestMapping annotation this way:
#RequestMapping(path="/test", method = {RequestMethod.GET, RequestMethod.POST},
consumes = "application/x-www-form-urlencoded")

Resources