With spring-data a #RepositoryRestResource allow me to perform CRUD operations for a given #Entity class. All is impressively magical but how can I add a security layer to prevent anybody to call a million times the insertion URL?
It seems, this problem is not specific to Spring Data REST. If you have any public interface that allows to add data to your database, you have the same problem.
However, regarding Spring Data REST, there are (at least) two possibilities:
Don't export the save(T) method
Use #RestResource(exported = false) to prevent Spring Data REST to export certain methods at all:
#RepositoryRestResource(path = "people", rel = "people")
interface PersonRepository extends CrudRepository<Person, Long> {
#Override
#RestResource(exported = false)
void save(Person person);
}
You can still use the save(T) method in your code, but it won't be available via REST. See the reference documentation for more details.
Secure your application with Spring Security
Require users to log in before they are allowed to save data. Spring Data REST provides an example that shows how to secure a Spring Data REST application in multiple ways with Spring Security: Spring Data REST + Spring Security
Related
how to make a specific GraphQL query as public API in spring boot security?
In REST API, we can specify the URL as public like the following code
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().requestMatchers(CorsUtils::isPreFlightRequest)
.antMatchers("/actuator/**", "/graphiql/**", "/voyager/**", "/vendor/**");
}
How to do the same for specific GraphQL query or mutation,
query {
listEmployee(){
id
}
}
GraphQL uses a single endpoint for all queries and mutations. This is one of the main differences when compared to REST.
That means you cannot control its security at the URL level if you want a query or mutation to have different security settings than others as they all have the same URL. Instead every query and mutation is backed by their own resolver method. You can control security at the method level using #PreAuthorize etc… (More info).
I am working on two different spring boot services, that will need to access a common MongoRepository collection of Users.
For the sake of simplicity, we have SpringBootApp1:
User.java
#Document(collection = "user")
public class User {
#Id
private String id;
....
}
Then I get the repository as:
UserRepository.java
public interface UserRepository extends MongoRepository<User, String> {
}
Now I need another application, SpringBootApp2 that reads the Users. I was planning to do the same in the second service, but then I would have two versions of User object, one defined on each service and both of them trying to read from the same MongoRepository collection. If one User class is modified in one service, the other will not know and they will start being off-sync, on top of having repetition or code on both services.
What would be the best approach in this case?
Why you need SpringBootApp2 to read the User table again ? You can expose an API in SpringBootApp1 which will fetch you the desired data by calling from the App2 . All your database related operations you can keep in one micro service or one app, and if you need the data from the database just expose methods in App1 and call that from App2.
UPDATE :
As you said, App1 does READ-WRITE with the collection and grants access only to authenticated users, and App2 does a simple READ-ONLY activity, in that case, you can use the JPA repo interface in App2 with only the read from collection method implemented. You don't need any setters method for the Entity and no save/update method implemented for your interface. This design is harmless. You are just making a call to the same database from two different services. No harm in that. Anyways you are always making a fresh query to the collection from App2.
Else if you have concerns with this approach as well, and if you dont want to keep multiple DB read logics from different services, then (I do not know what else functionalities your App1 offers other than DB operations) I would like to suggest you to create App3 which is only for the sole purpose of DB operations.
In that case :
App1 will do security related operations + other services
Design App3 which does DB manipulation work, without any direct user credentials or security
App1 will perform authentication, and on success call App3 for any DB READ-WRITE work, App3 wont need any security
App2 will call App3 for any DB READ operations, App3 wont need any security
I am writing a spring rest application, the problem is that I am not sure while I should use a repository or when a service interface together with implementation of it. Let's say that I have a repository that has a method findById I created a service interface that has the same method it returns the object and is called Object findById(Long id); and I wonder if I should create an implementation of that that's looks like that
public Object findById(Long id) {
repository.findById(id).orElseThrow(() -> new RuntimeException("message"));
}
but I could also do the same without this service class as the repository also returns a Optional so it could be also done in the controller
repository.findById(id).orElseThrow(() -> new RuntimeException("message"));
But it's hard to test repositories, better is to create an implementation of the service and then test the service. Anyway what's yours opinion about it, which one is better for you and why?
I think it's all about your project architecture. one of the classic, simplest and most favorite architectures is N-Layer architecture which normally is implemented with 3 main layers. Controllers, Services and Repositories.
Controllers are responsible for getting the requests from clients, updating the model usually with calling Services and returning a response for clients.
Services are where your business logic are implemented and where you should usually check for your transaction management and some security checking and etc.
and finally Repositories are where you interact with underlying systems like File System and Database to save the state of your application.
Setup
I have a PagingAndSortingRepository from which I want to expose only a limited set of (mostly) read operations and add some of non-DB services. I added a REST controller to front the crud repository.
Problem
On Swagger interface, I see all the operations enabled, even if only one operation is called from the rest repo. All the operations get the same path ,e.g. "/rest/foo" in below example.
How can I disable Spring Boot injecting all the operations?
Additional Observations :
If I do not have any method using crud repo in the rest controller, operations are not listed. Even though if the crud repo is auto wired.
I did not want to disable each operation in CRUD Repo individually. Even if I do that Swagger would list the operations, but calls would fail with 405.
Sample Code
public interface MyCRUDRepository extends PagingAndSortingRepository<Foo, FooPK> {
}
#RestController
public class MyRESTController {
#Autowired
MyCRUDRepository repository;
#RequestMapping("/rest/foo")
public Foo find(String id) {
return repository.findOne(id);
}
}
Two changes I made to fix the issue :
Added a method attribute (GET) in RequestMapping. Without this all the methods were allowed, bound to the same method.
As suggested by #mrkernelpanic, adding #ApiOperation to the method rectified the error where Swagger added all the methods to it's reported API.
What is the best pattern to follow when saving an object that belongs to a authenticated user using Spring Security and Web MVC?
All the tutorials that I've come across show how to authenticate a user logging in and that's as far as they go, they don't explain how to work with multiple users and their data. For example: A logged in user is saving a new blog post. Here is what the controller looks like:
#PostMapping("/blog/save")
public String blogSavePost(#AuthenticationPrincipal CurrentUser currentUser, BlogForm blogForm) {
blogService.save(currentUser, blogForm);
return "redirect:/blog";
}
Should the currentUser be passed to the blogService where the service then sets it on the entity Blog (which is then passed to BlogRepository for saving)? It seems a bit tedious to have to pass this on every save or edit..
What about when editing an object - since the id of the object being saved is passed to the controller (either as a parameter of part of the form object), it's open to being changed by a user. How should the service layer verify that the data being saved belongs to the user? I figure that there is an easy way to handle this with Spring Security, I just haven't come across what it is..
Question 1
Entirely up to you. I would just set the association in the Controller before passing it to the service:
Question 2.
You can prevent binding of certain fields by defining a binder in youir controller:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("userId");
}
You can also use method level security to prevent users from editing entities other than those that belong to them. See here for an example:
Spring Security in conjuction with Spring repositories to secure and authenticate various parts of a website
Alternatively you could use a Web Security expression that would secure the endpoint for save/update/delete requests.
http://docs.spring.io/spring-security/site/docs/current/reference/html/el-access.html#el-access-web