I am trying to have HMAC in springBoot for REST API.
The request I send from Postman is
{
"name":"xyz",
"description":"hello world",
"phone":"123456",
"id":"1"
}
it reached my controller and then to the service where I have a function to validate HMAC.
In the controller I pass the signature as the header and payload in the requestBody
#RestController
public class UserController {
#Autowired
UserInterface userInterface;
#PostMapping(value = "/" ,consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public void createUser(#RequestBody User user, #RequestHeader Map<String, String> headers) {
userInterface.hmacValidation(user, headers);
}
}
#Service
public class UserService implements UserInterface {
public void hmacValidation(User requestBody, Map<String, String> header) {
var headerSignature = header.get("signature");
var payload = getRequestBodyAsString(requestBody);
String result = Hashing.hmacSha256("12345".getBytes(StandardCharsets.UTF_8)).hashString(payload,StandardCharsets.UTF_8).toString();
}
private String getRequestBodyAsString(User requestBody) {
var mapper = new ObjectMapper();
String payload = null;
try {
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
payload = mapper.writeValueAsString(requestBody);
} catch (JsonProcessingException e) {
}
return payload;
}
}
here from the getRequestBodyAsString(User requestbody) function the output I get is a shuffled/rearranged JSON request which generates different Signature which then mismatches the signature client is sending.
the payload that is converted back from UserObject:
{"name":"xyz","id":"1","description":"hello world","phone":"123456"}
public class User {
private String name;
private String id;
private String description;
private String phone;
}
The client can send the request in any order but I have to validate signature regardless of the order the request comes in
Is there any other way to validate HMAC?
You should not deserialize if you want to take hash value. Use string or byte for the request. And map it to your pojo later on once you have the hash
For ex :
public #ResponseBody String controllerMethod(HttpServletRequest httpReq,
HttpServletResponse httpResponse) {
BufferedReader bufferedReader;
StringBuilder sb;
sb = new StringBuilder();
bufferedReader = httpReq.getReader();
char[] charBuffer = new char[128];
int bytesRead;
while ((bytesRead = bufferedReader.read(charBuffer)) != -1) {
sb.append(charBuffer, 0, bytesRead);
}
String reqBody = sb.toString();
}
Use reqBody to get your hashed value.
Related
My issue is when I try this I get a media type error, then I changed the header. Now I receive a 500 error. The problem isnt the api , on postman it works perfectly , am I doing something wrong in my code when requesting a post?
My object model
public class EmailModel {
private String module;
private String notificationGroupType;
private String notificationGroupCode;
private String notificationType;
private String inLineRecipients;
private String eventCode;
private HashMap<String, Object> metaData;
public EmailModel() {
this.module = "CORE";
this.notificationGroupType = "PORTAL";
this.notificationGroupCode = "DEFAULT";
this.notificationType = "EMAIL";
this.inLineRecipients = "[chrispotjnr#gmail.com,chris#mqattach.com]";
this.eventCode = "DEFAULT";
this.metaData = metaData;
}
}
My Controller
It should send a post request with a object body, the emails get sent
#RequestMapping(value = "test", method = RequestMethod.Post)
public void post() throws Exception {
String uri = "TestUrl";
EmailModel em = new EmailModel();
EmailModel data = em;
HttpClient client = HttpClient.newBuilder().build();
HttpRequest request = HttpRequest.newBuilder()
.headers("Content-Type", "application/json")
.uri(URI.create(uri))
.POST(HttpRequest.BodyPublishers.ofString(String.valueOf(data)))
.build();
HttpResponse<?> response = client.send(request, HttpResponse.BodyHandlers.discarding());
System.out.println(em);
System.out.println(response.statusCode());
}
postmanImage
You must to convert EmailModel to json format by ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
String data = objectMapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(em);
and change POST to :
.POST(HttpRequest.BodyPublishers.ofString(data))
See more about ObjectMapper
Capture requests and cookies(on the left side of setting icon)
->Request
->port and put the port number there
I have an entity class like below:
public class InputData {
byte[] nameBytes;
InputType inputType;
InputType outputType;
String inputName;
Description desc;
}
Here is my rest controller:
#PostMapping(path = "/submitData", consumes = "application/json")
public HttpStatus callDataService(#RequestBody Map<String, String> json) {
Gson gson = new GsonBuilder().create();
InputData inputData = gson.fromJson(json.get("inputData"), InputData.class);
Report report = dataService.getReport(inputData);
//return HttpStatus.OK;
}
I have two questions:
How can I send the report as well as Http Status back as a response?
How to send the data to controller?
I have created the following test case:
#Test
public void testController() throws JSONException {
Gson gson = new Gson();
Description desc = new Description();
desc.setMinimumValidSize(512);
File file = new File("src/test/resources/sampleDocuments/test_1.pdf");
byte[] byteArray = { 'P', 'A', 'N', 'K', 'A', 'J' };
JSONObject inputSample = new JSONObject();
inputSample.put("nameBytes", byteArray);
inputSample.put("inputType", ImageType.PDF);
inputSample.put("outputType", ImageType.TIFF);
inputSample.put("inputName", "ABCDEF");
inputSample.put("desc", desc);
String result = invokeRest(fileInputSample.toString(),"/submitData", HttpMethod.POST);
assertEquals("200", result);
}
private String invokeRest(String basicParams, String inputImageType, String
outputImageType, String options, String url, HttpMethod httpMethod) {
String testUrl = "http://localhost:" + port + url;
Map<String, Object> body = new HashMap<>();
body.put("fileInput", basicParams);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity(body, headers);
String result = "";
ResponseEntity<String> response = restTemplate.exchange(testUrl, httpMethod, entity, String.class);
if (response.getStatusCode() == HttpStatus.OK) {
result = response.getBody();
} else {
result = response.getStatusCode().toString();
}
return result;
}
When I run this the test case failed and I was able to pin point the issue :
Expected BEGIN_OBJECT but was STRING at line 1 column 13 path $.desc
So I am guessing I am not sending this values in right way
For Description POJO is below:
public class Description {
private static final int DPI = 300;
private Ctype c = CType.NONE;
private ColorType color = DEFAULT_COLOR;
private int dpi = DPI;
}
public enum CType {
NONE, GROUPA,GROUPB,GROUPB_B,GROUPD
}
public enum ColorType {
RGB, GREY;
}
Here is the values that is being send:
{"desc":"org.restservice.Description#1213ffbc”,
"outputType":"TIFF","inputType":"PDF","nameBytes":"src/test/resources/sampleDocuments/test_16.pdf","inputName":"98111"}
How can I send that as Object if I am sending a Map of <String, String> in body? Is there any other way to send that object to controller?
To return the status and also the object you can try to do it like this:
#PostMapping(path = "/submitData", consumes = "application/json")
public ResponseEntity<Report> callDataService(#RequestBody Map<String, String> json) {
Gson gson = new GsonBuilder().create();
InputData inputData = gson.fromJson(json.get("inputData"), InputData.class);
Report report = dataService.getReport(inputData);
return ResponseEntity.ok(report);
}
I'm trying to test getting parameters for processing a request using the Post method
#RestController
#RequestMapping("api")
public class InnerRestController {
…
#PostMapping("createList")
public ItemListId createList(#RequestParam String strListId,
#RequestParam String strDate) {
…
return null;
}
}
test method
variant 1
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class InnerRestControllerTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Test
void innerCreatePublishList() {
String url = "http://localhost:" + this.port;
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
URI uriToEndpoint = UriComponentsBuilder
.fromHttpUrl(url)
.path(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate)
.build()
.encode()
.toUri();
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, uriToEndpoint, ItemListId.class);
}
}
variant 2
#Test
void createList() {
String uri = "/api/createList";
String listStr = "kl";
String strDate = "10:21";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(uri)
.queryParam("strListId", listStr)
.queryParam("strDate ", strDate);
Map<String, String> map = new HashMap<>();
map.put("strListId", listStr);//request parameters
map.put("strDate", strDate);
ResponseEntity< ItemListId > listIdResponseEntity =
restTemplate.postForEntity(uri, map, ItemListId.class);
}
Update_1
In my project exceptions is handled thus:
dto
public final class ErrorResponseDto {
private String errorMsg;
private int status;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
LocalDateTime timestamp;
...
handler
#RestControllerAdvice
public class ExceptionAdviceHandler {
#ExceptionHandler(value = PublishListException.class)
public ResponseEntity<ErrorResponseDto> handleGenericPublishListDublicateException(PublishListException e) {
ErrorResponseDto error = new ErrorResponseDto(e.getMessage());
error.setTimestamp(LocalDateTime.now());
error.setStatus((HttpStatus.CONFLICT.value()));
return new ResponseEntity<>(error, HttpStatus.CONFLICT);
}
}
In methods, where necessary, I throw a specific exception...
.w.s.m.s.DefaultHandlerExceptionResolver : Resolved
[org.springframework.web.bind.MissingServletRequestParameterException:
Required String parameter 'strListId' is not present]
Who knows what the error is. Please explain what you need to add here and why ?
Let's take a look on declarations of postEntity:
postForEntity(URI url, Object request, Class<T> responseType)
...
postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
As you can see, first argument is either URI or String with uriVariables, but second argument is always request entity.
In you first variant you put uri String as URI and then pass uriToEndpoint as request entity, pretending that it is request object. Correct solution will be:
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null, ItemListId.class);
Addressing your comments.
If server responded with HTTP 409, RestTemplate will throw exception with content of your ErrorResponseDto. You can catch RestClientResponseException and deserialize server response stored in exception. Something like this:
try {
ResponseEntity<ItemListId> listIdResponseEntity =
restTemplate.postForEntity(uriToEndpoint, null,
ItemListId.class);
...
} catch(RestClientResponseException e) {
byte[] errorResponseDtoByteArray = e.getResponseBodyAsByteArray();
// Deserialize byte[] array using Jackson
}
I am developing a front end application using Java Spring-MVC, however I'm facing difficulties in creating Request Object for Hitting Web-Services as I did in .Net-MVC. Can anyone tell me the equivalent classes and methods in Java for below given code.
I need to replicate these 2 methods from .Net-MVC to Java.
1st Method:
private HttpWebRequest RequestObj { get; set; }
public Stream DataStreamObj { get; set; }
private RequestModel RequestModelObj { get; set;
public RequestGenerator(String WebserviceUrl)
{
RequestObj = (HttpWebRequest)WebRequest.Create(WebConfigurationManager.AppSettings["WebServiceURL"] + WebserviceUrl);
RequestObj.Method = "POST";GenerateLoginRequest
RequestObj.ContentType = "application/json";
RequestModelObj = new RequestModel();
RequestModelObj.ApiKey = WebConfigurationManager.AppSettings["apiKey"];
RequestModelObj.DeviceId = Constant.AppConstants.ONE;
}
2nd Method:
private string CallWebservice(Dictionary<String, Object> RequestDict)
{
try
{
HttpWebRequest Request = (HttpWebRequest)RequestDict["request"];
RequestModel RequestModel = (RequestModel)RequestDict["requestData"];
//Tell them the length of content
string Json = JsonConvert.SerializeObject(RequestModel);
byte[] ByteArray = Encoding.UTF8.GetBytes(Json);
Request.ContentLength = ByteArray.Length;
//Write content on stream
Stream DataStream = Request.GetRequestStream();
DataStream.Write(ByteArray, 0, ByteArray.Length);
DataStream.Close();
//Initiate Call
HttpWebResponse Response = GetWebResponse(Request);
DataStream = Response.GetResponseStream();
StreamReader Reader = new StreamReader(DataStream);
// Read the content.
string responseFromServer = Reader.ReadToEnd();
// Display the content.
Reader.Close();
Response.Close();
return responseFromServer;
}
catch (System.Net.WebException ex)
{
var response = ex.Response as HttpWebResponse;
return "";
}
}
RestTemplate class is designed to call REST services, it should come as no surprise that its main methods are closely tied to REST’s underpinnings, which are the HTTP protocol’s methods: HEAD, GET, POST, PUT, DELETE, and OPTIONS. E.g. it’s methods are headForHeaders(), getForObject(), postForObject(), put() and delete() etc.
Read More and Source Code : Spring REST JSON Example
HTTP GET Method Example
1) Get XML representation of employees collection in String format
REST API Code
#RequestMapping(value = "/employees", produces =
MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public String getAllEmployeesXML(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "xmlTemplate";
}
REST Client Code
private static void getEmployees()
{
final String uri =
"http://localhost:8080/springrestexample/employees.xml";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}
2) Get JSON representation of employees collection in String format
REST API Code
#RequestMapping(value = "/employees", produces =
MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String getAllEmployeesJSON(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "jsonTemplate";
}
REST Client Code
private static void getEmployees()
{
final String uri =
"http://localhost:8080/springrestexample/employees.json";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}
3) Using custom HTTP Headers with RestTemplate
REST API Code
#RequestMapping(value = "/employees", produces =
MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String getAllEmployeesJSON(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "jsonTemplate";
}
REST Client Code
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees";
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>("parameters",
headers);
ResponseEntity<String> result = restTemplate.exchange(uri,
HttpMethod.GET, entity, String.class);
System.out.println(result);
}
4) Get data as mapped object
REST API Code
#RequestMapping(value = "/employees", produces =
MediaType.APPLICATION_XML_VALUE, method = RequestMethod.GET)
public String getAllEmployeesXML(Model model)
{
model.addAttribute("employees", getEmployeesCollection());
return "xmlTemplate";
}
REST Client Code
private static void getEmployees()
{
final String uri = "http://localhost:8080/springrestexample/employees";
RestTemplate restTemplate = new RestTemplate();
EmployeeListVO result = restTemplate.getForObject(uri,
EmployeeListVO.class);
System.out.println(result);
}
5) Passing parameters in URL
REST API Code
#RequestMapping(value = "/employees/{id}")
public ResponseEntity<EmployeeVO> getEmployeeById (#PathVariable("id")
int id)
{
if (id <= 3) {
EmployeeVO employee = new
EmployeeVO(1,"Lokesh","Gupta","howtodoinjava#gmail.com");
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
return new ResponseEntity(HttpStatus.NOT_FOUND);
}
REST Client Code
private static void getEmployeeById()
{
final String uri =
"http://localhost:8080/springrestexample/employees/{id}";
Map<String, String> params = new HashMap<String, String>();
params.put("id", "1");
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.getForObject(uri, EmployeeVO.class,
params);
System.out.println(result);
}
HTTP POST Method Example
REST API Code
#RequestMapping(value = "/employees", method = RequestMethod.POST)
public ResponseEntity<String> createEmployee(#RequestBody EmployeeVO
employee)
{
System.out.println(employee);
return new ResponseEntity(HttpStatus.CREATED);
}
REST Client Code
private static void createEmployee()
{
final String uri = "http://localhost:8080/springrestexample/employees";
EmployeeVO newEmployee = new EmployeeVO(-1, "Adam", "Gilly",
"test#email.com");
RestTemplate restTemplate = new RestTemplate();
EmployeeVO result = restTemplate.postForObject( uri, newEmployee,
EmployeeVO.class);
System.out.println(result);
}
HTTP PUT Method Example
REST API Code
#RequestMapping(value = "/employees/{id}", method = RequestMethod.PUT)
public ResponseEntity<EmployeeVO> updateEmployee(#PathVariable("id")
int id, #RequestBody EmployeeVO employee)
{
System.out.println(id);
System.out.println(employee);
return new ResponseEntity<EmployeeVO>(employee, HttpStatus.OK);
}
REST Client Code
private static void deleteEmployee()
{
final String uri =
"http://localhost:8080/springrestexample/employees/{id}";
Map<String, String> params = new HashMap<String, String>();
params.put("id", "2");
EmployeeVO updatedEmployee = new EmployeeVO(2, "New Name", "Gilly",
"test#email.com");
RestTemplate restTemplate = new RestTemplate();
restTemplate.put ( uri, updatedEmployee, params);
}
HTTP DELETE Method Example
REST API Code
#RequestMapping(value = "/employees/{id}", method =
RequestMethod.DELETE)
public ResponseEntity<String> updateEmployee(#PathVariable("id") int
id)
{
System.out.println(id);
return new ResponseEntity(HttpStatus.OK);
}
REST Client Code
private static void deleteEmployee()
{
final String uri =
"http://localhost:8080/springrestexample/employees/{id}";
Map<String, String> params = new HashMap<String, String>();
params.put("id", "2");
RestTemplate restTemplate = new RestTemplate();
restTemplate.delete ( uri, params );
}
I'm using Spring rest with below code base:
When I invoke /info by passing string value in request body, I'm expecting the below response if this value is not present in my backend database.
{"output":-10}
but instead it returns me below response:
{"id": 0, "output":-10}
Can any one tell me how to get rid of this id default value? If there is a boolean variable in JSON mapper, then that would also get returned as
{"id": 0, "booleanVar": false, "output":-10}
Can any one tell me how to get rid of this default value?
Controller.java
#RequestMapping(value = "heartbeat", method = RequestMethod.GET, consumes="application/json")
public ResponseEntity<String> getHeartBeat() throws Exception {
String curr_time = myService.getCurrentTime();
return MyServiceUtil.getResponse(curr_time, HttpStatus.OK);
}
#RequestMapping(value = "info", method = RequestMethod.POST, consumes="application/json")
public ResponseEntity<String> getData(#RequestBody String body) throws Exception {
....
myInfo = myService.getMyInfo(myServiceJson);
return MyServiceUtil.getResponse(myInfo, responseHeader, HttpStatus.OK);
}
MyService.java
#Override
public String getCurrentTime() throws Exception {
String currentDateTime = null;
MyServiceJson json = new MyServiceJson();
ObjectMapper mapper = new ObjectMapper().configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);
try {
Date currDate = new Date(System.currentTimeMillis());
currentDateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(currDate);
json.setCurrentDateTime(currentDateTime);
ObjectWriter writer = mapper.writerWithView(Views.HeartBeatAPI.class);
return writer.writeValueAsString(json);
} catch (Exception e) {
throw new Exception("Excpetion in getCurrentTime: ", HttpStatus.BAD_REQUEST);
}
}
#Override
public String getMyInfo(MyServiceJson myServiceJson) throws Exception {
MyServiceJson json = new MyServiceJson();
json.setFirstName("hhh");
json.setLastName("abc");
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(json);
}
Views.java
public class Views {
public static class HeartBeatAPI { }
}
MyServiceJson.java
#JsonSerialize(include = Inclusion.NON_NULL)
public class MyServiceJson {
private int id;
private String firstName;
private String lastName;
#JsonView(Views.HeartBeatAPI.class)
private String currentDateTime;
// Getter/Setter for the above variables here
.....
}
Use Integer class instead of int primitive type. Primitive types always hold default values, where class type defaults to null.