How do you setup a resource server in spring boot that only uses role from the jwt token to allow access? - spring-boot

I think I have some odd requirements here because no matter where I look, I can't a specific example for what I was asked to do. I created a dummy project called contacts to test this. I am suppose to secure my api with Oauth2, but the authorization server is not on the same box.
It is my understanding that the client will need to call the authorization to get a token and then the request with the token will be sent to my api.
In my server the scope will determine if the user has access. I am not doing any authentication on my server.
I can't seem to get this to work though.
Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping(value = "/contacts")
#PreAuthorize("#oauth2.hasScope('ec.edm.mdm')")
public class ContactsController {
#Autowired
ContactRepository customerRepo;
#RequestMapping(method = RequestMethod.GET, produces = { "application/json" })
public Page<Contact> findAllContacts(Pageable pagable) {
return customerRepo.findAll(pagable);
}
}
Application
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
#EnableResourceServer
#SpringBootApplication
public class App {
private static final Logger LOG = LoggerFactory.getLogger(App.class);
#Autowired
private Environment environment;
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
/**
* Allows for #PreAuthorize annotation processing.
*/
#EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}

Related

How do session time out and and ask for login again in Spring boot

I am trying to build simple JSON rest API using spring boot, I have build the application it works fine, now I need to do the simple Basic authentication and I did this with Spring security. Please find the Code below, My pain point here is when I run the application, I ask for the login first time then I tried to reload the URL again it does not ask for user name password. How to make the URL session expired and ask to prompt for login again. Note : I am not using any UI, It just an API
Security Configuration Class
package com.erplogic.erp.topcon.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
public class SecurityConfiguration {
#Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated()
).sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
)
.httpBasic();
return http.build();
}
#Bean
InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.builder().username("user").password(encoder().encode("pass")).roles("USER").build();
return new InMemoryUserDetailsManager(user);
}
#Bean
PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
#Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
Controller Class
package com.erplogic.erp.topcon.controller;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.erplogic.erp.topcon.service.SapApiService;
import com.erplogic.poc.objects.Root;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.erplogic.maven.plclog.wsdl2java.StandardFaultMessage_Exception;
#RestController
public class OutboundController {
#Autowired
private SapApiService sapApiService;
#RequestMapping(path ="/outbound",produces = MediaType.APPLICATION_JSON_VALUE,method = RequestMethod.GET)
public Root getOutbound() throws KeyManagementException, NoSuchAlgorithmException, StandardFaultMessage_Exception, JsonProcessingException {
return ((SapApiService) sapApiService).processOutboudService();
}
#RequestMapping(value = "/outbound/{outboundId}",produces = MediaType.APPLICATION_JSON_VALUE,method = RequestMethod.GET)
public String getOutboundId(#PathVariable String outboundId) throws KeyManagementException, JsonProcessingException, NoSuchAlgorithmException, StandardFaultMessage_Exception {
return ((SapApiService) sapApiService).processOutboudServiceByID(outboundId);
}
}

Missing bean in controller even after adding ComponentScan annotation

I am trying to run a spring boot application which gets a list of names of people from the database. I am getting the below error :
Description:
Field peopleserviceinterface in com.sample.lombpackdemo.controller.FriendController required a bean of type 'com.sample.lombpackdemo.service.FriendsServiceInterface' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.sample.lombpackdemo.service.FriendsServiceInterface' in your configuration.
package com.sample.lombpackdemo.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.sample.lombpackdemo.entity.Friends;
import com.sample.lombpackdemo.rowmapper.FriendsRowMapper;
import com.sample.lombpackdemo.service.FriendsServiceInterface;
#CrossOrigin(origins = { "http://localhost:3000"})//to allow access from other servers
#RestController
#RequestMapping("/code")
#ComponentScan(basePackages="com.sample.lombpackdemo.service")
public class FriendController {
#Autowired
private FriendsServiceInterface peopleserviceinterface;//autowire the service
#GetMapping("/all")
public ResponseEntity<List<Friends>> getAllPeople(){
List <Friends> listOfAllPpl = peopleserviceinterface.getAllFriends();
System.out.println("Getting all friends"+listOfAllPpl.toString());
return new ResponseEntity<List<Friends>>(listOfAllPpl,HttpStatus.OK);
}
}
The FriendServiceInterface class is as below:
package com.sample.lombpackdemo.service;
import java.util.List;
import org.springframework.stereotype.Component;
import com.sample.lombpackdemo.entity.Friends;
#Component
public interface FriendsServiceInterface {
public List<Friends> getAllFriends();
}
The FriendService class:
package com.sample.lombpackdemo.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.sample.lombpackdemo.entity.Friends;
import com.sample.lombpackdemo.repos.FriendsRepo;
import com.sample.lombpackdemo.rowmapper.FriendsRowMapper;
#Service
#Profile("devel")//added to disable CORS only on development time
#Configuration
#Component
public class FriendService implements FriendsServiceInterface {
#Autowired
private FriendsServiceInterface peopleserviceinterface;
#Autowired
private FriendsRepo pplRepo;//should always autowire repository
#Autowired
private JdbcTemplate jdbc;
private static long idCounter = 0;
//FriendsRowMapper fRowMap=new FriendsRowMapper();
#Override
public List<Friends> getAllFriends() {
String sql="select f_name from list_friends";
return jdbc.query(sql, new FriendsRowMapper() );
/*
List<Friends> ppList= (List<Friends>) fRowMap.findAll();
try {
System.out.println("Repository value"+pplRepo.findAll());
System.out.println("Inside findAll of service class" +ppList.toString() );
}
catch(Exception e)
{
}
return ppList;
//
*/}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
//registry.addMapping("/save-javaconfig").allowedOrigins("http://localhost:3000");
}
};
}
Please let me know what else needs to be changed. I have tried adding Component annotation to FriendService class and ComponentScan annotation to the controller class.
Edited to add JdbcConfig class
package com.sample.lombpackdemo.repos;
//import java.util.logging.Logger;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
#Configuration
#ComponentScan(basePackages = "com.sample.lombpackdemo")
public class SpringJdbcConfig {
protected final Logger log = LoggerFactory.getLogger(getClass());
#Bean
public DataSource mysqlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/person_example");
dataSource.setUsername("root");
dataSource.setPassword("subfio");
return dataSource;
}
#Bean(name = "dbProductService")
#ConfigurationProperties (prefix = "spring.datasource")
#Primary public DataSource createProductServiceDataSource()
{ System.out.println("Inside db cofig ");
return DataSourceBuilder.create().build(); }
}
}

Spring WebFlux Request Principal is null in test

We have library which provides a custom filter for our requests, which we use to write information on the request to our logs. We previously only needed servlet support, but now need to add webflux support. I've created a new WebFilter, which works as expected.
However, trying to test this new filter has been difficult. We're relying on the principal in the request for user information for the log, but during the test, it is always null. I've tried many things, including following the examples here: https://docs.spring.io/spring-security/site/docs/5.1.0.RELEASE/reference/html/test-webflux.html
My Filter:
package com.project.utils.security.request.logging.config.autoconfig;
import com.project.utils.logging.LoggingUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
public class ReactiveRequestLoggingFilter implements WebFilter {
private static final Logger logger = LoggerFactory.getLogger(ReactiveRequestLoggingFilter.class);
#Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
return serverWebExchange
.getPrincipal()
.flatMap(
principal -> {
LoggingUtil loggingUtil =
new LoggingUtil(serverWebExchange.getRequest(), principal.getName());
loggingUtil.setEvent("Http Request");
logger.info(loggingUtil.toString());
return webFilterChain.filter(serverWebExchange);
});
}
}
My Test:
package com.project.utils.security.request.logging.config.autoconfig;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockUser;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
import com.project.utils.security.request.logging.config.testcontrollers.ReactiveTestController;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {ReactiveTestController.class})
class ReactiveRequestLoggingFilterTest {
#Autowired ReactiveTestController controller;
ReactiveRequestLoggingFilter filter = new ReactiveRequestLoggingFilter();
#Test
void test() {
WebTestClient client =
WebTestClient.bindToController(controller)
.webFilter(filter)
.apply(springSecurity())
.configureClient()
.build()
.mutateWith(mockUser("User"));
client
.get()
.uri("/test")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("GET");
}
}
The controller I'm using for the test:
package com.project.utils.security.request.logging.config.testcontrollers;
import com.project.utils.security.request.logging.config.annotations.EnableReactiveRequestLogging;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
#RestController
#RequestMapping("/test")
#EnableReactiveRequestLogging
public class ReactiveTestController {
#GetMapping
Mono<ResponseEntity<String>> get() {
return Mono.just(ResponseEntity.ok("GET"));
}
}
We are using Spring Boot 2.1, which uses Spring Security 5.1. Like I said, the actual functionality works, but we need to get tests written for it, and I can't figure out how to get the principal into the request.
Thanks!
Okay, found a working solution from this answer: https://stackoverflow.com/a/55426458/1370823
My final test looked like:
package com.project.utils.security.request.logging.config.autoconfig;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
import com.project.utils.security.request.logging.config.testcontrollers.ReactiveTestController;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {ReactiveTestController.class, ReactiveRequestLoggingFilter.class})
#WebFluxTest(controllers = {ReactiveTestController.class})
#WithMockUser(value = "Test User", username = "TestUser")
class ReactiveRequestLoggingFilterTest {
#Autowired ReactiveTestController controller;
#Autowired ReactiveRequestLoggingFilter filter;
#Test
void test() {
WebTestClient client =
WebTestClient.bindToController(controller)
.webFilter(new SecurityContextServerWebExchangeWebFilter())
.webFilter(filter)
.apply(springSecurity())
.build();
client
.get()
.uri("/test")
.exchange()
.expectStatus()
.isOk()
.expectBody(String.class)
.isEqualTo("GET");
}
}

How and where to add new bean in Sping application generated by Jhipster

I generated my Spring application using Jhipster. Now I want to add controller for FilesUpload, and StorageService for it. But when I run my application it gets me this message
Description:Parameter 0 of constructor in com.kongresspring.myapp.web.rest.FileUploadResource required a bean of type 'com.kongresspring.myapp.service.StorageService' that could not be found.
Action:Consider defining a bean of type 'com.kongresspring.myapp.service.StorageService' in your configuration.
I can't find beans.xml to add new bean. I'm new in spring, so maybe there's some other way to configure bean, I'm not familiar whit. Here's my code for uploading file controller:
package com.kongresspring.myapp.web.rest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.kongresspring.myapp.service.StorageService;
#RestController
#RequestMapping("/api")
public class FileUploadResource {
private final Logger log = LoggerFactory.getLogger(FileUploadResource.class);
private final StorageService storageService;
#Autowired
public FileUploadResource(StorageService storageService) {
this.storageService = storageService;
}
/**
* POST uploadFile
*/
#PostMapping("/upload-file")
public String uploadFile(#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
storageService.store(file);
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "success";
}
/**
* GET preview
*/
#GetMapping("/preview")
public String preview() {
return "preview";
}
}
And here's my StorageService code:
package com.kongresspring.myapp.service;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Path;
import java.util.stream.Stream;
public interface StorageService {
void init();
void store(MultipartFile file);
Stream<Path> loadAll();
Path load(String filename);
Resource loadAsResource(String filename);
}
You can create an implementation for StorageService, and annotate it as #Service/#Component, spring will automatically discover the bean:
#Service
public class StorageServiceImpl implements StorageService {
void init(){// You code goes here/}
void store(MultipartFile file){///}
Stream<Path> loadAll(){///}
Path load(String filename){//}
Resource loadAsResource(String filename){///}
}

bootstrap template & spring boot ( can not display an html page)

I'am using a bootstrap template and spring boot , i wanted to test the controller , for that i code a simple restfull service wich retun the index page of the template but it didn't work it gives me an error.
Here's the architecture of the project
And here's my controller
package demo.controllers;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.view.RedirectView;
import demo.connection.SingletonConnection;
import demo.dao.IDatabase;
import demo.dao.IEntities;
import demo.entities.DB;
#RestController
public class DatabaseController implements ErrorController{
private static final String PATH = "/error";
protected Connection conn;
#Autowired
private IDatabase db;
#Autowired
private IEntities entities;
// En cas d'érreur
#RequestMapping(value = PATH)
public String error() {
return "Error";
}
#Override
public String getErrorPath() {
return PATH;
}
#RequestMapping(value="/")
public String index(Model model){
return "pages/index.html";
}
}
when i run the application with the path : http://localhost:8080/test

Resources