architecture microservice spring boot - spring

I am working with Spring cloud (microservices) and I have implemented security with JWT token.
in my security application, I have entities like User, Role and UserRole.
so Every request first comes to the ZOOL service and it calls Authentication service and Authentication service creates/returns JWT token.
Also, I have another microservice-rest application (Questions-app) that needs JWT token.
in the Questions-app I have a Question entity that contains authorId field.
#Entity
#Table(name="QUESTION")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID", updatable = false, nullable = false)
private long id;
#Column(name = "AUTHOR")
private long authorId;
#Column(name = "TITLE")
private String title;
}
Now, it is not clear for me, is it right to set authorId long type or I should create User, Role, UserRole entities (just simple copy from AUTH project) in the questions-app and set "AUTHOR" column like that
#OneToOne
#JoinColumn(name="AUTHOR")
private User user;
I know that in the first option when I need show question and user's name on the webpage, then I should call 2 services (one from question-app (fetch question) and another from auth service (fetch user information by author id)
I would like to know what is the best practises?

If you have common database for all of these microservices and you need User related information based on question id.
Then instead of doing another database call for user, you can directly do #OneToOne to User.
From your question it is better to go for 2 option.

Related

Spring, Question regarding Query on Many-to-Many(Association with Extra Columns) in web development

I have 3 class that using Many to Many relationship in Entity level (herbibate)
They are Teams,Users and TeamUsers.
Since I am using hibernate they are all Fetch Lazy
Team:
Users:
TeamUsers:
and we have 3 interface (using by #Autowired in Service or repo layer) which is:
1.TeamRepository extends PagingAndSortingRepository<Team, Integer>
2.UserRepository extends PagingAndSortingRepository<User, Integer>
3.TeamUserRepository extends PagingAndSortingRepository<TeamUsers, Integer>
And here is the core code for many to many relationship mapping:
Team class:
#OneToMany(mappedBy = "team", fetch = FetchType.EAGER)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
User class:
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private Set<TeamUsers> team_users = new HashSet<TeamUsers>();
TeamUsers class:
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "TEAM_ID")
private Team team;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "USER_ID")
private User user;
Now, my questions is, what is the best practice to play around this data?
for example:
1. I need all team information and team member from my DB
In restful controller get mapping function, what should I return? should I use TeamUser repository (or maybe one more service layer) to return findAll()? if do so the data will come as object collection of User and Team right(maybe also contain extra column in that table)? Can Json successfully contain that data?
2. I am on webpage and I want to delete a user from a team.
At this timepoint I know the TeamID and UserID from HTML, when I send the Post request to API, should I get User object by UserID, then get Team object by TeamID, then get(Query) TeamUsers object by giving User object and Team Object? or maybe just query by id(from TeamUsers on html) send to API and simply remove this TeamUsers entity?
I'm new to spring and frontend development and I am much apprecaite for your help!
I would suggest the following
If you want to return all the teams and for each team all its users, you should have a TeamService which call findAll method of Team repository this way you will get a list of Teams and for each team a set with its users. And if instead of that you want all of your users and for each one what are its teams you should do the other way, call the findAll method of User repository
Both solutions will serialize to json without problem as long as the objects have its constructor and getter and setter methods
If you want to delete only the relation between user and team you could have a method in your TeamEntity Spring Data repository like this
long deleteByTeamIdAndUserId(long teamId, long userId);
And spring will create a method to perform this action or you can write the query you like just above the method name to be more specific
#Modifying
#Query("delete from TeamUsers t where t.userId=:userId and t.teamId = :teamId")
long deleteByTeamIdAndUserId(long teamId, long userId);

Need help regarding JPA entity mapping

I'm fairly new to ORM. I'm having trouble deciding how exactly I should map the following entities.
DiscussionThread
Post
User
AnonymousUser
DiscussionThread would be something similar to the ones we see in bulletin boards online. It would contain a list of Post which would be posted by User. However, I do not want the User to reveal his/her identity while posting in the DiscussionThread.
In order to achieve that I created a list of proxy usernames denoted by the entity AnonymousUser. Thus, whenever a User decides to make a Post in a DiscussionThread, he would be posting as an AnonymousUser. Any further Post made by the same User in that DiscussionThread would be linked to the same AnonymousUser.The User will have different AnonymousUser names in different DiscussionThreads. An instance of AnonymousUser may be used by two different users on two different threads.
In simpler words, there will be one AnonymousUser for one User in each DiscussionThread.
I have created the following POJO entities, but I'm stuck in how I should map them to each other.
public class AnonymousUser {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String displayPicture;
//Not sure how to make relationships here
private Set<DiscussionThread> discussionThreads;
private Set<User> users;
}
public class DiscussionThread {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String description;
}
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String email;
private String username;
}
Any help will be appreciated.
Thank you!
Well, you basically described:
Don't know if it's right or not but this is one way you could diagram and think about such problems. This is Chen's database notation in Visio.

Spring data mongodb #DBRef list

I am trying to have a list in a model using #DBRef but I can't get it to work.
This is my User model:
#Data
#Document
public class User {
#Id
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
#Indexed(unique = true)
#NotBlank
private String email;
#NotBlank
private String name;
#NotBlank
private String password;
#DBRef
private List<Server> servers;
}
Server model:
#Data
#Document
public class Server {
#Id
#JsonSerialize(using = ToStringSerializer.class)
private ObjectId id;
#NotBlank
private String name;
#NotBlank
private String host;
}
The structure is very simple, every user can have multiple servers. But when I add servers to the user the server is created, but the servers array contains one null entry("servers" : [ null ]). So the server isn't added to the user. This is how I create a server and add it to an user:
#PostMapping
public Mono create(#Valid #RequestBody Server server, Mono<Authentication> authentication) {
return this.serverRepository.save(server).then(authentication.flatMap(value -> {
User user = (User) value.getDetails();
user.getServers().add(server);
return userRepository.save(user);
})).map(value -> server);
}
So I simply create and save a server, add the server the user and then save the user. But it doesn't work. I keep having an array with one null entry.
I've seen this page: http://www.baeldung.com/cascading-with-dbref-and-lifecycle-events-in-spring-data-mongodb. But it is for saving the child document, not for linking it. Also it is for a single document, not for an array or list.
Why is my list not being saved correctly?
All my libraries are coming from spring boot version 2.0.0.M6.
UPDATE
When removing #DBRef from the user's servers property the servers are getting saved, but they of course get double created, in the server collection and in every user.servers. So the error has something to do with references.
After some googling I found the answer...
https://jira.spring.io/browse/DATAMONGO-1583
https://jira.spring.io/browse/DATAMONGO-1584
Reactive mongo doesn't support this.
Actually there is a way to resolve DbRefs without to using the blocking driver. Yes - the references are resolved in a blocking fashion, but does not require a second connection. In order to achieve this we have to write our own DbRefResolver: NbDbRefResolver.java. In the provided resolver there is a flag: RESOLVE_DB_REFS_BY_ID_ONLY. If is switched on will not going to resolve the DbRefs from the database, but instead will resolve them to fake objects with id only. It is up to implementation to fill the references later in non-blocking fashion.
If the flag RESOLVE_DB_REFS_BY_ID_ONLY is set to false it will eagerly resolve the references by using the non-blocking driver, but will block the execution until the references are resolved.
Here is how to register the DbRefResolver in the app: DbConfig.kt
Files attached are provided here: https://jira.spring.io/browse/DATAMONGO-1584
Me did it like that for roles :
#Unwrapped(onEmpty = Unwrapped.OnEmpty.USE_NULL)
private Collection<Role> roles;
you can check the doc (2021) here : https://spring.io/blog/2021/04/20/what-s-new-in-spring-data-2021-0

Implementing multi tenancy on Spring Data JPA

I am planning to write a multi tenant aware application using Spring Data JPA as the persistent layer. However I'm not planning to keep separate databases per tenant rather all the data is in a single database. Following beans represent the Project entity and Tenant entity that I'm planning to implement. When fetching a project or fetching the list of all projects, I should be able to filter them according to a specific tenant. I know that I can easily do that by writing methods like bindByIdAndTenant_Id(int id, int id) but I'm worrying about the scalability of the solution. What I need is to have a method like findById(int id) and content will be automatically filtered by the tenant id that is fetched from a context. Is there a way I can do that in Spring Data JPA?
Project Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "TENANT_ID", referencedColumnName = "id", foreignKey = #ForeignKey(name = "FK_TENANT_IN_PROJ"))
private Tenant tenant;}
Tenant Entity
public class Tenant {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
int id;
#Column(name = "NAME", unique = true)
private String name;}
There is (afaik) no such "out-of-the-box" mechanism. How should Spring know how a tenant is identified and what is important to separate the data. Maybe there is even some shared data between tenants so things get more complicated and it's on your own to do that.
I assume your data is accessed using a web layer and you have to identify the tenant on every call. A "simple" path parameter or a header is (without any further validation) too easy to manipulate so maybe a token (JWT) is a good alternative to store request based tenant information. Here is a nice article about exactly that topic. Another one is talking about pros and cons, especially related to differences in storage architecture.
Personally, I would tend to the "different databases" approach. It's easy to scale out and user data is separated properly. Also backup and restore mechanisms are possible for each tenant without affecting the other users. At least use different schemata for each tenant, but maybe that depends on the database and what exactly is meant by "schema".

Authorize Object using spring security?

I have an application that have two domain model
Organization and TicketQuestion .
Authenticated User want to create ticket that have an organization property to solve that
each user permit to some organization like this:
User1 permit to Organization1
User2 permit to Organization2
TicketController.java have save method that create ticket.
I have this vulnerability: User1 can invoke method with ticket that have Organization2( that dose not have permission to it ).
I am using Hibernate filter for authorize data in GET methods but i dont know how can i protect data that user want persist and dose not have permission ??;
/ticket/save
{
id:-1,
organization:{
id:2,
title:'organization2' //not allowed this organization
}
}
#Entity
#Table(name = "core_organization_structure")
public class OrganizationStructure {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "title", nullable = false)
private String title;
}
#Entity
#Table(name = "core_Ticket")
public class Ticket {
..some prop
#ManyToOne
#JoinColumn(name = "org_id", nullable = false)
private OrganizationStructure org;
}
When the form is submitted, you need to load the authenticated user's permissions and check that they are authorized to perform the action they are attempting to perform.
If the user is attempting to create a ticket for an organization that they do not have permissions to; don't persist the record, and handle it appropriately. (Throw an exception, return a 401, etc...)

Resources