Inject SecurityContext in Service - java

I have my Application set-up as following:
Resource
#Path("/books")
public class BookResource {
#Inject
BookService bookService;
#Context
SecurityContext securityContext;
#GET
public Response getBooks() {
List<BookDTO> books = bookService.getAllBooks();
return Response.ok(books).build();
}
}
Service
public interface BookService {
List<BookDTO> getAllBooks();
}
ServiceImpl
public class BookServiceImpl implements BookService {
#Context
SecurityContext securityContext;
#Override
public List<BookDTO> getAllBooks() {
BookDTO book1 = new BookDTO("Catcher in the Rye");
BookDTO book2 = new BookDTO("Moby Dick");
return Arrays.asList(new Book[]{book1,book2});
}
}
In my Resource, the SecurityContext is injected and i can fetch the current user.
Is there a way to inject the SecurityContext outside of the Resource (the place where i put my path annotations)? If so, how can I do this?
I want to move my security back to the service and maybe repository too.
Update
I solved it by the following code, but I think it can get a lot better/cleaner.
BaseResource
public class BaseResource {
#Context
SecurityContext securityContext;
public class BaseRequest {
private Principal principal;
public BaseRequest() {
principal = securityContext.getUserPrincipal();
}
public Principal getPrincipal() {
return principal;
}
}
}
BookResource
public class BookResource extends BaseResource {
#Inject
BookService bookService;
#Path("/{id}")
public Response getBookById(#PathParam("id") Long id) {
BookDTO book = bookService.getBookById(new GetBookRequest(id));
return Response.ok(book).build();
}
public Response getAllBooks() {
List<BookDTO > books = bookService.getAllBooks(new GetAllBooksRequest());
return Response.ok(books).build();
}
public class GetBookRequest extends BaseRequest {
private Long id;
public GetBookRequest(Long id) {
super();
this.id = id;
}
public Long getId() {
return id;
}
}
public class GetAllBooksRequest extends BaseRequest {
public GetAllBooksRequest() {
super();
}
}
}
BookService
public interface BookService {
public List<BookDTO> getAllBooks(GetAllBooksRequest request);
public BookDTO getBookById(GetBookRequest request);
}
BookServiceImpl
#Named
public class BookServiceImpl implements BookService {
#Override
public List<BookDTO> getAllBooks(GetAllBooksRequest request) {
Principal principal = request.getPrincipal();
BookDTO book1 = new BookDTO();
book1.setName("Catcher in the Rye");
book1.setId(1L);
BookDTO book2 = new BookDTO();
book2.setName("Moby Dick");
book2.setId(2L);
return Arrays.asList( new BookDTO[]{ book1, book2 });
}
#Override
public BookDTO getBookById(GetBookRequest request) {
Principal principal = request.getPrincipal();
BookDTO book = new BookDTO();
book.setName("Catcher in the Rye");
book.setId(request.getId());
return book;
}
}

You don't need to inject anything. Use SecurityContextHolder insteed.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
var principalName = authentication.getName();

Related

Spring Boot - "this.peopleService" is null?

I'm a bit new to Spring Boot and I'm trying to create model/repo/service/serviceImp/controller type of architecture.
After I try to make a this get request:
http://localhost:8080/api/v1/people/name?name=steve
and I get this error (I created a couple of people in DB):
"java.lang.NullPointerException: Cannot invoke \"com.project.Springbootbackend.service.PeopleService.findAllByName(String)\" because \"this.peopleService\" is null\r\n\tat com.project.Springbootbackend.controller.PeopleController.findAllByName(PeopleController.java:24)
This is my code:
People(entity)
#Entity
public class People {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name")
private String name;
#Column(name = "surname")
private String surname;
#Column(name = "email")
private String email;
...
//constructor + get/set
PeopleController
#RestController
#RequestMapping("/api/v1/people")
#RequiredArgsConstructor
public class PeopleController {
private PeopleService peopleService;
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
PeopleRepo
public interface PeopleRepository extends JpaRepository<People, Integer> {
List<People> findAllByName(String name);
}
PeopleService
public interface PeopleService {
List<People> findAllByName(String name);
}
PeopleServiceImp
#RequiredArgsConstructor
#Service
public class PeopleServiceImp implements PeopleService {
PeopleRepository peopleRepository;
#Override
public List findAllByName(String name) {
return (List) ResponseEntity.ok(peopleRepository.findAllByName(name));
}
}
Thx guys in advance.
*SOLUTION:
Entity, service & repository is the same.
ServiceImp and controller changes are down belowe:
Controller:
#RestController
#RequestMapping("/api/v1/people")
public class PeopleController {
private PeopleService peopleService;
public PeopleController(PeopleService peopleService) {
this.peopleService = peopleService;
}
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
ServiceImp
#Service
public class PeopleServiceImp implements PeopleService {
private PeopleRepository peopleRepository;
public PeopleServiceImp(PeopleRepository peopleRepository) {
this.peopleRepository = peopleRepository;
}
#Override
public List<People> findAllByName(String name) {
List<People> people = peopleRepository.findAllByName(name);
return people;
}
}
Your constructor does not inject the service, because of the RequiredArgsConstructor (see Link) needs special treatment. Therefore, use final:
#RestController
#RequestMapping("/api/v1/people")
#RequiredArgsConstructor
public class PeopleController {
private final PeopleService peopleService;
#GetMapping("/name")
public ResponseEntity<List<People>> findAllByName(#RequestParam String name) {
return ResponseEntity.ok().body(peopleService.findAllByName(name));
}
}
Same here:
#RequiredArgsConstructor
#Service
public class PeopleServiceImp implements PeopleService {
private final PeopleRepository peopleRepository;
#Override
public List findAllByName(String name) {
return (List) ResponseEntity.ok(peopleRepository.findAllByName(name));
}
}
Additional hint, use a typed list:
#Override
public List<People> findAllByName(String name) {
return ResponseEntity.ok(peopleRepository.findAllByName(name));
}
Try like this:
#Autowired
private PeopleService peopleService;
#Autowired
private PeopleRepository peopleRepository;
You also need to add the #SpringBootApplication annotation in the main class of the application.
Something like that:
#SpringBootApplication
class PeopleApplication {
public static void main(String[] args) {
...
Take a look at this article about automatic dependency injection in Spring:
https://www.baeldung.com/spring-autowire
You missed the autowiring annotation in the controller to inject the service which may make this.peopleService to be null.
#Autowired
private PeopleService peopleService;
You also need to do autowire in your serviceimpl class
#Autowired
private PeopleRepository peopleRepository;

Swagger2 configuration spring boot won't working

I'm trying to add swagger to a project and when I access localhost:9999/swagger-ui returns "Whitelabel Error Page" always;
Here is my swagger configuration.
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.contact(new Contact("Vinicius Deluca", "", "vinidelucka#gmail.com"))
.title("Products-ms")
.description("")
.license("")
.licenseUrl("")
.version("1.0")
.build();
}
}
This is the first time I use this feature and can't see what I'm doing wrong. See some tutorials and looks like I did all in the same way. But still won't working.
My api runs direct after port 9999, without root package (localhost:9999/products is a get)
Controller:
#RestController
#RequestMapping("/products")
public class ProductController {
#Autowired
ProductService productService;
#GetMapping
public List<Product> productsList() {
List<Product> products = productService.productsList();
return products;
}
#GetMapping("/{id}")
public Optional<Product> searchProductById(#PathVariable String id) {
Optional<Product> product = productService.searchProductById(id);
return product;
}
#GetMapping("/search")
public List<Product> searchProducts(#RequestParam(required = false) Integer min_price,
#RequestParam(required = false) Integer max_price,
#RequestParam(required = false) String q) {
List<Product> products = productService.searchProducts(min_price, max_price, q);
return products;
}
#PostMapping
public ResponseEntity<Product> insertProduct(#RequestBody ProductForm form) {
Product product = productService.insertProduct(form);
return ResponseEntity.ok().body(product);
}
#PutMapping("/{id}")
#Transactional
public ResponseEntity<Product> updateProduct(#PathVariable String id, #RequestBody ProductForm form) {
Product product = productService.updateProduct(id, form);
return ResponseEntity.ok().body(product);
}
#DeleteMapping("/{id}")
#Transactional
public ResponseEntity<Product> deleteProduct(#PathVariable String id) {
ResponseEntity<Product> response = productService.deleteProduct(id);
return response;
}
}
application:
#SpringBootApplication
public class ProductMsApplication {
public static void main(String[] args) {
SpringApplication.run(ProductMsApplication.class, args);
}
}

Spring Data REST Event not raised in #RepositoryRestController

I'm using Spring Boot, Spring Data REST, Spring HATEOAS. I created a #RepositoryRestController:
#Api(tags = "Ticket Entity")
#RepositoryRestController
#PreAuthorize("isAuthenticated()")
public class TicketController extends RevisionController<TransitCertificate> {
private Logger log = LogManager.getLogger();
#Autowired
private LocalValidatorFactoryBean validator;
#Autowired
private TicketService ticketService;
#Autowired
private EnumTranslator enumTranslator;
#SuppressWarnings("rawtypes")
#Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
#Autowired
private MessageSource messageSource;
#Autowired
private Javers javers;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(validator);
}
#PostMapping(path = "/tickets")
public ResponseEntity<?> save(#RequestBody(required = true) #Valid Ticket ticket, PersistentEntityResourceAssembler resourceAssembler) {
return new ResponseEntity<>(resourceAssembler.toResource(ticketService.save(ticket)), HttpStatus.OK);
}
}
I need to intercept the event before the Ticket object is persisted. I created my handler:
#Component
#RepositoryEventHandler(Ticket.class)
public class TicketHandler {
private Logger log = LogManager.getLogger();
#Autowired
private WorkShiftRepository workShiftRepository;
#HandleBeforeCreate
public void handleBeforeCreates(Ticket ticket) {
WorkShift workShift = workShiftRepository.findByAgentUsernameAndEndDateIsNull();
if (workShift != null) {
ticket.setWorkShift(workShift);
}
}
}
and this is my TicketRepository:
#Transactional
#PreAuthorize("isAuthenticated()")
public interface TicketRepository extends PagingAndSortingRepository<Ticket, Long> {
#RestResource(exported = false)
#Override
public <S extends Ticket> Iterable<S> save(Iterable<S> entities);
#RestResource(exported = false)
#Override
public <S extends Ticket> S save(S entity);
#RestResource(exported = false)
#Override
public void delete(Long id);
#RestResource(exported = false)
#Override
public void delete(Ticket entity);
#Query(value = "SELECT MAX(number) FROM Ticket t WHERE t.block=:ticketBlock")
public Long findMaxNumber(#Param("ticketBlock") TicketBlock ticketBlock);
}
as described in the documentation but the event is not emitted. Like described here I'm using the #HandleBeforeCreate annotation. Am I doing something wrong?

RESTful URLs with Jersey 2 subresources?

Is there a way to use Resorces both as root and as sub-resource?
I want to call my api endpoint this way:
GET /persons/{id}/cars # get all cars for a person
GET /cars # get all cars
How to implement my resources to use this url schema?
Person resource:
#Path("persons")
public class PersonsResource {
#GET
#Path("{id}/cars")
public CarsResource getPersonCars(#PathParam("id") long personId) {
return new CarsResource(personId);
}
}
Cars resource:
#Path("cars")
public class CarsResource {
private Person person;
public CarsResource(long personId) {
this.person = findPersonById(personId);
}
#GET
public List<Car> getAllCars() {
// ...
}
#GET
public List<Cars> getPersonCars() {
return this.person.getCars();
}
}
You don't do it that way, instead you inject an instance of CarsResource into PersonsResource', and then you call the method ofgetPersonCars`, as follows
#Path("persons")
public class PersonsResource {
#inject
private CarsResource carsResource;
#GET
#Path("{id}/cars")
public List<Cars> getPersonCars(#PathParam("id") long personId) {
return carsResource.getPersonCars(personId);
}
}
#Path("cars")
public class CarsResource {
#GET
#Path("all")
public List<Car> getAllCars() {
// ...
}
public List<Cars> getPersonCars(long personId) {
Person person = findPersonById(personId);
return person.getCars();
}
}

Get data from a repository using Spring

Ok so I am new to spring and don't really know how this works. I have been trying a few things and think its close to doing it but not getting any data from the server and giving me this error
Unsatisfied dependency expressed through constructor argument with index 4 of type [jp.co.fusionsystems.dimare.crm.service.impl.MyDataDefaultService]: : Error creating bean with name 'MyDataDefaultService' defined in file
My end point
//mobile data endpoint
#RequestMapping(
value = API_PREFIX + ENDPOINT_MyData + "/getMyData",
method = RequestMethod.GET)
public MyData getMyData() {
return MyDataDefaultService.getData();
}
My Object
public class MyData {
public MyData(final Builder builder) {
videoLink = builder.videoLink;
}
private String videoLink;
public String getVideoLink()
{
return videoLink;
}
public static class Builder
{
private String videoLink = "";
public Builder setVideo(String videoLink)
{
this.videoLink = videoLink;
return this;
}
public MyData build()
{
return new MyData(this);
}
}
#Override
public boolean equals(final Object other) {
return ObjectUtils.equals(this, other);
}
#Override
public int hashCode() {
return ObjectUtils.hashCode(this);
}
#Override
public String toString() {
return ObjectUtils.toString(this);
}
}
The Repository
public classMyServerMyDataRepository implements MyDataRepository{
private finalMyServerMyDataJpaRepository jpaRepository;
private final MyDataConverter MyDataConverter = new MyDataConverter();
#Autowired
publicMyServerMyDataRepository(finalMyServerMyDataJpaRepository jpaRepository) {
this.jpaRepository = Validate.notNull(jpaRepository);
}
#Override
public MyData getData() {
MyDataEntity entity = jpaRepository.findOne((long) 0);
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
The DefaultService that gets called by the endpoint
public class MyDataDefaultService {
private static final Logger logger = LoggerFactory.getLogger(NotificationDefaultService.class);
private finalMyServerMyDataRepository repository;
#Autowired
public MyDataDefaultService(MyServerMyDataRepository repository) {
this.repository = Validate.notNull(repository);
}
//Get the data from the server
public MobileData getData()
{
logger.info("Get Mobile Data from the server");
//Get the data from the repository
MobileData mobileData = repository.getData();
return mobileData;
}
}
The Converter
public class MyDataConverter extends AbstractConverter<MyDataEntity, MyData>
{
#Override
public MyData convert(MyDataEntity entity) {
MyData.Builder builder = new MyData.Builder()
.setVideo(entity.getVideoLink());
return builder.build();
}
}
My Entity
#Entity
#Table(name = “myServer”)
public class MyDataEntity extends AbstractEntity{
#Column(name = "video_link", nullable = true)
private String videoLink;
public String getVideoLink() {
return videoLink;
}
public void setVideoLink(final String videoLink) {
this.videoLink = videoLink;
}
}
Thank you for any help with this
Hibernate entity should have default constructor defined and implement Serializable interface as well, assume AbstractEntity matches the requirement. Hibernate won't accept an entity without a primary key so you have to define the one too:
#Entity
#Table(name = “myServer”)
public class MyDataEntity implements Serializable {
#Id
#GeneratedValue
private Long id;
#Column(name = "video_link", nullable = true)
private String videoLink;
public MyDataEntity() {
}
...setters&getters
}
MyData object represents the JSON server response, you can use Jackson annotations to control the result JSON properties:
public class MyDataResponse {
#JsonProperty("video_link")
private String videoLink;
public MyDataResponse() {
}
public MyDataResponse(String videoLink) {
this.videoLink = videoLink;
}
...setters&getters
}
Spring has an awesome project so called Spring Data that provides the JPA repositories, so there's no even the #Repository annotation ever needed:
public class MyDataRepository extends CrudRepository<MyDataEntity, Long> {
}
The Builder class represents the Service layer:
#Service
public class MyDataService {
#Autowired
private MyDataRepository myDataRepository;
public MyDataResponse getMyData(Long id) {
MyDataEntity entity = myDataRepository.findOne(id);
...rest logic, copy necessary data to MyDataResponse
}
}
Then a controller is:
#RestController // #ResponseBody not needed when using like this
public MyDataController {
#Autowired
private MyDataService myDataService;
#RequestMapping("/getMyData") // no need to specify method for GET
public MyDataResponse getMyData(#RequestParam("ID") Long myDataId) {
... validation logic
return myDataService.getMyData(myDataId); // return response
}
}
Now it should work, don't forget to add required dependencies to your classpath.

Categories

Resources