I am using Jackson to serialize objects to be saved on MongoDB (through Jongo). These objects contain a password hash that I want to store on the database.
I also have a REST API that will return those objects. When the objects are serialized through the REST API they will contain the password hash. Despite the communication being done over HTTPS, this sounds like a security risk to me. How can I prevent the serialization of the password hash through the REST API but not for database persistence? Is there something like conditional serialization of fields?
#JsonView may suit your need.
// View definitions:
class Views {
static class Public { }
static class Internal extends Public { }
}
public class User {
// Name is public
#JsonView(Views.Public.class) String name;
// Hash password only for internal usage
#JsonView(Views.Internal.class) String hashPassword;
}
In your REST API, you could specify:
public class Resource {
#JsonView(Views.Public.class)
#GET
#Produces(MediaType.APPLICATION_JSON )
public List< User > getElements() {
//do something
return someResultList;
}
}
The above api would only include "name" property of User in the response.
Note: If no view annotation, assumed to mean View identified by Object.class: that is, included in all views.
When serializing to DB, you could do this:
objectMapper.viewWriter(Views.Internal.class).writeValue(out, beanInstance);
Which would include all properties of User.
More info here: http://wiki.fasterxml.com/JacksonJsonViews
use this annotation on the password field in the object:
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
Related
I need to ignore the field when return the response from spring boot. Pls find below info,
I have one pojo called Student as below
Student {
id,
name,
lastName
}
i am getting a body for as PostRequest as below
{
id:"1",
name:"Test",
lname:"Test"
}
i want get all the data from frontEnd (id,name,Lname) But i just want to return the same pojo class without id as below,
{
name:"Test",
lName:"Test"
}
I have tried #JsonIgnore for column id, But it makes the id column as null(id=null -it is coming like this even when i send data to id field from postman) when i get the data from frontEnd.
I would like to use only one pojo to get the data with proper data(withoud getting id as Null), and need to send back the data by ignoring the id column.
Is there any way to achieve it instead of using another pojo?
You just need to use #JsonInclude(JsonInclude.Include.NON_NULL) at class level and it will be helpful for ignore all your null fields.
For example :
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Test {
// Fields
// Constructors
// Getters - setters
}
As of now you are using only one POJO it's not good practice because it's your main entity into your project, so good practice is always make DTO for the same.
This is possible via the #JsonView annotation that is part of Jackson. Spring can leverage it to define the views used on the controller.
You'd define your DTO class like this:
class User {
User(String internalId, String externalId, String name) {
this.internalId = internalId;
this.externalId = externalId;
this.name = name;
}
#JsonView(User.Views.Internal.class)
String internalId;
#JsonView(User.Views.Public.class)
String externalId;
#JsonView(User.Views.Public.class)
String name;
static class Views {
static class Public {
}
static class Internal extends Public {
}
}
}
The Views internal class acts as a marker to jackson, in order to tell it which fields to include in which configuration. It does not need to be an inner class, but that makes for a shorter code snippet to paste here. Since Internal extends Public, all fields marked with Public are also included when the Internal view is selected.
You can then define a controller like this:
#RestController
class UserController {
#GetMapping("/user/internal")
#JsonView(User.Views.Internal.class)
User getPublicUser() {
return new User("internal", "external", "john");
}
#GetMapping("/user/public")
#JsonView(User.Views.Public.class)
User getPrivateUser() {
return new User("internal", "external", "john");
}
}
Since Spring is aware of the JsonView annotations, the JSON returned by the /public endpoint will contain only externalId and name, and the /internal endpoint will additionally include the internalId field.
Note that fields with no annotation will not be included if you enable any view. This behaviour can be controlled by MapperFeature.DEFAULT_VIEW_INCLUSION, which was false in the default Spring ObjectMapper when I used this for the last time.
You can also annotate your #RequestBody parameters to controller methods with JsonView, to allow/disallow certain parameters on input objects, and then use a different set of parameters for output objects.
class Address {
String address1, country, state, zip;
}
class Foo {
#Field(type = FieldType.Object)
Address work;
boolean workAddressSameAsHome;
#Field(type = FieldType.Object)
Address home;
}
I would like to return work value as home in the JSON response if workAddressSameAsHome=true, as the value will not be stored in ES. How do I make this work for a GET request /foo/<id>
Spring Data Elasticsearch does not offer any REST URLs, so I assume you are using Spring Data Rest on top.
BTW, your Foo class has no #Document and #Id property, I guess your real class has one.
What you can do in Spring Data Elasticsearch is to provide a bean that implements the AfterConvertCallback interface - available since Spring Data Elasticsearch 4.0:
#Component
public class SetWorkAddressAsHomeCallback implements AfterConvertCallback<Foo> {
#Override
public Foo onAfterConvert(Foo foo, Document document, IndexCoordinates indexCoordinates) {
if (foo.workAddressSameAsHome) {
foo.home = foo.work;
}
return foo;
}
}
This callback is invoked after the document is read from Elasticsearch. Here I just copy the work address to the home address field.
For more information about the callbacks check the documentation at https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.entity-callbacks
You might consider to implement a BeforeConvertCallback as well where you would clear the home address when the workAddressSameAsHome is true, this would be invoked before the entity is saved in Elasticsearch, thus preventing to store a home address in this case. But keep in mind that you won't be able to do an Elasticsearch search for the home address in this case.
I am creating a requestModel and let say a person doesn't send me some keys.
If that key is not present I want to put null if i get the value of the key.
I don't want to investigate if a key is present or not .
public class CustomerModel {
private Optional<String> s3Bucket;
private Optional<String> docType;
public String getS3Bucket() {
if(s3Bucket.isPresent()) {
return s3Bucket.get();
} else {
return null;
}
}
public void setS3Bucket(Optional<String> s3Bucket) {
this.s3Bucket = s3Bucket;
}
public Optional<String> getDocType() {
return docType;
}
public void setDocType(Optional<String> docType) {
this.docType = docType;
}
}
Do we have any library or something where.
1. If i get the key and it is not present in the coming request json, i will get the null out of it and if the key is present and has value . It will be stored as value.
2. When writing the getter for s3bucket (getS3Bucket), i dont want to write it for everykey value. Is there a automatic way to do this.
I looked at lot of posts but the scenario is not there.
P.S - I am new to java
I believe Jackson is exactly what you need. And if you are using Spring - it already uses Jackson under the hood I guess.
Here you can find some examples and documentation of how JSON mapping on to model class is done.
If you need to customize some behavior, you can use annotations like #JsonProperty (there are many).
If properties in your model class have the same names as properties in JSON, most probably you won't need to provide any further configs.
Here is a simple example:
public class User {
#JsonProperty("userName")
private String name;
private int age;
// getters and setters
}
And if you have JSON like this:
{
"userName" : "Foo Bar",
"age" : 18
}
Jackson will do all the magic for you unless you need something very specific.
If something is not in JSON you get (let's say you received JSON without age) - corresponding property in model class will be null if it is object type and default value (0, false, etc.) for primitives (in our case age would be 0).
I have a JPA entity with a couple of fields (the real ones are more complex). I'm receiving some data via REST (POST operation in a Spring controller) and storing it right away in the JPA entities; I want to see if there is a possibility to exclude some field(s) when the request is sent, Jackson deserializes it, and constructs the object. But at the same time I want those fields to be included when I send back (object gets serialized) the response.
#Table("key_card")
public final class KeyCard {
private String username; // Don't want this to be sent as input,
// but want to be able to send it back
// in the response
#NotBlank
private final char[] password;
}
I'm just trying not to model it twice (for the request and response) if there is a way to solve this.
You can use JSON views: http://wiki.fasterxml.com/JacksonJsonView
Class Views {
static class AlwaysInclude { }
static class OnlyOnSerialize extends AlwaysInclude { }
}
And then on your view:
#Table("key_card")
public final class KeyCard {
#JsonView(Views.OnlyOnSerialize.class)
private String username;
#JsonView(Views.AlwaysInclude.class)
#NotBlank
private final char[] password;
}
To exclude a Java object property only from Json deserialization and to include instead its value during serialization you can use an appropriate combination of #JsonIgnore and #JsonProperty annotations.
In particular you should:
annotate with #JsonIgnore the property itself
annotate with #JsonIgnore its set method
annotate with #JsonProperty its get method
Here you can find an in-depth explanation and an example: Jackson: using #JsonIgnore and #JsonProperty annotations to exclude a property only from JSON deserialization
I have the following JAX-RS resource:
#POST
public Response createPerson(
final User user) {
...
}
and User bean is:
public class User {
protected String lastName;
protected String role;
#DefaultValue("true")
protected Boolean active;
#DefaultValue("dd.MM.yyyy")
protected String dateFormat;
...//getters and setters
}
When I don't specify values for 'active' and 'dateFormat' I expect them to be filled with default values. But they are null.
I've read docs for #DefaultValue and it seems to be not suitable for my scenario. But how can I ask jersey to fill these absent properties wiith defaults?
Edit:
I want to use annotations instead of the code (e.g. in constructor) because I want to be able to automatically generate API documentation (e.g. swagger). Swagger already supports #DefaultValue when providing parameter info, but I can extend it, if another approach with annotations is used.
Of course I can use code together with swagger-specific annotations, but this leads to duplications. I'd rather use same meta-info to get both application logic and documentation. I'm ok with custom annotations, while extending both jersey and swagger.
You could set the defaults either directly in the class definition or in the no-arg constructor, all in plain old java, no need for any annotations. When using the primitive type instead of the wrapper, active would default to false.
Just as an example:
public static class User {
public boolean primActive;
public Boolean wrapActive;
public boolean consActive;
public User() {
consActive = true;
}
}
The resource:
#POST
#Path("foo")
public User getUser( User user ) {
return user;
}
When posting an empty request (when using json: {}), the following is returned.
{
primActive: false
consActive: true
}