How to limit requests per USER in ServletAPI (Spring MVC) - java

How can I allow only a one request to micorservice method with specific URL #PathVariable per User.
My Controller
#RestController
#RequestMapping(value = "/rest/product", produces = "application/json;charset=UTF-8")
public class ProductRestController {
#Autowired
ProductService productService;
#Autowired
ProductAsm productAsm;
#RequestMapping(value = "/ID/{ID}", method = RequestMethod.GET)
public ResponseEntity<ProductResource> getProductID(#PathVariable("ID") Long ID, #AuthenticationPrincipal User) {
Product product = productService.getProduct(ID);
if (product == null)
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
return new ResponseEntity<>(productAsm.toResource(product), HttpStatus.OK);
}
For example :
first request /rest/product/ID/2231 allowed for USER(with login="xaxa" )
second request /rest/product/ID/2545 allowed for USER(with login="xaxa" )
thirdth request /rest/product/ID/2231 not allowed for USER(with login="xaxa" )
Which is the best way to implement this functionality?(Have I to keep this URL request with User login in DB or there is already solutions)

You could use AOP and implement your own aspect that would be called Before your Rest Endpoint method.
This pointcut would read ID provided in a request and would try to find a Lock corresponding with this ID. Then the usual - try to access resource and potentially wait.
Implementation could base on Guava's Striped class - at least for the start.
There are several problems that need to be taken into consideration:
Striped could be replaced with some LRU Cache for better memory management.
You would of course have to provide synchronization for the case when the same ID is accessed simultaneously by two requests.
It would work only for an application deployed on a single node.
It would not be very good approach performance-wise. Depending on your traffic this may be an issue.

Related

Using DTOs properly in a Restful web service

I am developing a restful web service using spring framework. I have a few questions on how to use DTOs properly in my project.
First of all, I saw a lot of code examples on using DTOs and there is always a one to one mapping with entities. For User entity we have UserDto, For Country, we have CountryDto and etc. I think this is not flexible at all. For every service, I need two DTOs for input and output. is that ok?
If I define two DTOs for each service, Can I name them with request and response suffixes? For example for createUser service, I have two DTO objects called CreateUserRequest and CreateUserResponse.
Some services have no outputs(Just a status code) or inputs(Just an Id in URL). Should I Define an empty DTO for them or not? If I don't define a DTO in these scenarios, it is a bit confusing because sometimes you define two DTOs for service and sometimes just one.
Should I map path variables like /users/{id} to DTO objects too? for example instead of:
#GetMapping("/users/{id}")
public ResponseEntity getUser(#PathVariable(value = "id") Long id)
do something like:
#GetMapping("/users/{id}")
public ResponseEntity getUser(#PathVariable(value = "id") GetUserRequest request)
Follow this pattern :
#RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> getUser(#PathVariable long id) {
ResponseEntity<User> response = null;
try {
User user = userService.getUser(id);
response = new ResponseEntity<User>(user, HttpStatus.OK);
} catch (ApplicationException ex) {
response = new ResponseEntity<User>(HttpStatus.NOT_FOUND);
}
return response;
}
Don't create a url like this : /users/{id}
Create a url like this - /{id} - this will give more abstraction.
In the ideal restful web service you manipulate resources with properties, not request-response structures. So, if you create User, it's ok to accept and return the same DTO and the method looks like this:
#PostMapping("/")
public ResponseEntity<User> addUser(#RequestBody User user)
Using request body for GET is technically possible, but de-facto is deprecated, use path variables and request params.
Some services have no outputs(Just a status code) or inputs(Just an Id in URL). Should I Define an empty DTO for them or not?
No, you should not define DTO for objects that you do not have.
And the last detail: if you have multiple methods manipulating same resource (e.g. User), do not make multiple User DTOs for get, post, etc.

Does #Cacheable annotated methods execute when the actual data is modified?

I am building a RESTful web service that can be consumed by a browser or another web service.
I am willing to reduce the bandwidth through caching, however i want the method to be executed and send the actual data only if it's different than the last modified cache.
From my understanding of the #cacheable annotation, the method is only executed once and the output is cached until the cache expires .
Also #CachePut executes everytime and updates the cache but does it send the cache again even if it's not updated?
summary is: i need the client to be able to send the last modified date of it's cache and only get a new data if it has been modified.
Also how does Spring handle the client side caching and if-modified-since headers? does i need to save the last modified time or it is automatically handled ?
No, you need to do it by yourself.
You need to annotate your "fetch" method with #Cacheable(docs) and then, annotate "update" method with #CacheEvict (docs) in order to "drop" your cache. So when you would fetch your data next time after its modification, it will be fresh.
Alternatively, you can create another method with #CacheEvict and manually call it from "update" method.
The cache related annotations (#Cacheable, #CacheEvict etc) will only deal with the cache being maintained by application. Any http response header like last-modified etc has to be managed seperately. Spring MVC provides a handy way to deal with it (docs).
The logic to calculate the last modified time has to be obviously application specific.
An example of its usage would be
MyController {
#Autowire
CacheService cacheService;
#RequestMapping(value = "/testCache", method = RequestMethod.GET)
public String myControllerMethod(WebRequest webRequest, Model model, HttpServletResponse response) {
long lastModified = // calculate as per your logic and add headers to response
if (request.checkNotModified(lastModified)) {
// stop processing
return null;
} else {
return cacheService.getData(model);
}
}
#Component
public class CacheService{
#Cacheable(value = "users", key = "#id")
public String getData(Model model) {
//populate Model
return "dataview";
}

Validation in controller or service?

I have a method of downloading messages in the controller
#GetMapping(value = "/sent/{id}")
public
HttpEntity<MessageSent> getMessageSent(
#ApiParam(value = "The message ID", required = true) #PathVariable Long id
) {
return ResponseEntity.ok().body(messageSearchService.getMessageSent(id, authorizationService.getUserId()));
}
However, I have forgotten to verify if the message about the given ID belongs to the user. It does not do this either in the service.
#Override
public MessageSent getMessageSent(
#Min(1) Long messageId,
#Min(1) Long userId
) throws ResourceNotFoundException {
Optional<UserEntity> user = this.userRepository.findByIdAndEnabledTrue(userId);
user.orElseThrow(() -> new ResourceNotFoundException("No user found with id " + userId));
return this.messageRepository.findByIdAndSenderAndIsVisibleForSenderTrue(messageId, user.get())
.map(MessageEntity::getSentDTO)
.orElseThrow(() -> new ResourceNotFoundException("No message found with id " + messageId));
}
And now my question is whether it should be done in the controller or service? I would prefer to do this in the service, but I don't know if it is appropriate.
As a general rule of thumb, I would say that business logic of this sort should be in the service. Controllers should be light-weight and pass on requests. Further, there may be other clients of your service, not just controllers, so this allows you to keep validation in one place.
Don't put this validation in the controller - controllers part is only be entry points for coming requests and exposing API's.
I would suggest you to create additional service which responsible of doing the validation and injecting this service in your
messageSearchService service. This way you can use the validation
service in other services as well which required same validation. In
Addition , this why you follow the principle of each class has only
one its own responsibility.

Spring Security 3.2: Prevent Direct Object Reference

I expose a Spring REST service like this..
#RequestMapping(value = "/{id}", method = RequestMethod.GET, headers = Constant.ACCEPT_APPLICATION_JSON)
#ResponseBody
public IndividualProviderDto showJson(#PathVariable("id") Long id) {
IndividualProviderDto individualProviderDto = individualProviderService.findIndividualProvider(id);
if (SecurityUtils.getCurrentLogin().equals(individualProviderDto.getUserName())) {
individualProviderDto.setCredential("");
HttpHeaders headers = new HttpHeaders();
headers.add(Constant.CONTENT_TYPE, Constant.APPLICATION_JSON_CHARSET_UTF8);
return individualProviderDto;
}
throw new IllegalArgumentException("User not found");
}
In the above code I am explicitly making a check to ensure that the id belongs to the logged in USER.
SecurityUtils.getCurrentLogin().equals(individualProviderDto.getUserName()
This check has to be applied wherever I need to protect a resource. Of course I can have an Aspect and apply it from one place using pointcut expressions. I have also heard of ESAPI that forges the url
but I was wondering if Spring Security Configuration provides something out of the box, so that I don't reinvent the wheel.
Spring security is not the answer -- it provides facility for authentication and authorization. Checking availability of a particular entity (in your case id) should be a part of service layer, as it's already implemented in your code.

Spring controllers - thread safety and storing resources

I have a question about spring mvc and thread safety.
We are developing web application that will be stored on tomcat. If I understand right, Tomcat creates thread per request and it has some thread pool. Now, dispatcher servlet is shared between requests and probably is thread safe.
But when I create controller like this:
#Controller
#RequestMapping("/manage")
public class QuestionManagementController {
he has Singleton scope so the same controller is used by every request that comes from every user.
I am wondering how this problem is usually solved:
1: are controllers created with Session scope? (but I think that there also could be problems if one user quickly do some things that may lead to race conditions in the controller).
2: controllers are scoped as request
3: creating stateless controllers that don't share any variables at class level, or have them in read only mode
or maybe there is some better "best practice" that solves this kind of problem.
I am asking this question, because now we have them as Singleton scoped, and there is a problem, that in most methods we are querying for user in the database , and we can't store this information in class level variable because of the scope. We could try to use some thread safe collection but later there could be other resources needing synchronized access.
A lot of parameters can be added to the controller methods like request, session, principal etc to avoid your problem.
Usually there's a 3-layers architecture:
#Controller (they delegates to services)
#Service (they do the work using DAOs or Repositories)
#Repository (or DAOs, they do DB access)
So depending on what you are querying for in the DB, I would advise having a service, injected by Spring with a cache if hitting the DB is costly and everytime you need the stuff from the DB call the service (i.e. nothing stored in the controller class level)
A short example, let's say we are behind spring-security and everything need a fully logged-in user. We have an userData table where the key is the user login, we have an url /data to get a page showing my user data:
#Controller
#RequestMapping("/data")
public class UserDataController
{
#Autowired
private UserService userService;
#RequestMapping(value = "", method = RequestMethod.GET)
public ModelAndView data(final Principal principal) {
Assert.notNull(principal); // throw if assertion fails
Assert.hasText(principal.getName());
final UserData userData = this.userService.findByUserName(principal.getName());
Assert.notNull(userData, "userData not found");
return new ModelAndView("userData", "userData", userData);
}
}
#Service("userService")
public class userService
{
private static final String USERDATA_CACHE = "com.acme.foo.UserData";
#Autowired
private UserDataRepository userDataRepository;
#Cacheable(USERDATA_CACHE)
public UserData findByUserName(final String userName) {
return this.userDataRepository.findByUserName(userName);
}
}
#Repository
public class UserDataRepository
{
// or use spring-data-jpa
public UserData findByUserName(final String userName) {
// query table userData and create an UserData from results.
}
}
Here I use principal and spring ensure that this is current user one.
Refs:
#Cachable
see also Initialize Singletons in Spring Framework 3 MVC
Note sure if this answer fully to your concerns

Categories