Authentication using request scoped context with Spring - spring

I am trying to create custom "userContext" as a SpringBean with request scope but I am unable to do so. Basically I have a Jersey REST api and I want to do authentication and authorization using my custom filters in which I autowire my "userContext" bean. The process looks like this:
REST API called (I expect Spring to create new instance of userContext bean)
AuthenticationFilter autowires new instance of userBean and populates it
AuthorizationFilter autowires the same instance which is populated now and authorize the user
When i first call the REST api (after server restart), it works as expected, but any other call fails, because AutorizationFilter gets an empty instance of userBean. I expect some fundamental misunderstanding of scopes on my part.
Btw: I'd like to avoid using ThreadLocal directly since Request scope should take care of it
I would like to know, why authorizationFilter doesn't see the populated version of the userBean and why the first call works. Thanks in advance for any help.
Just some parts of the code:
#Secured({Role.ADMIN}) //custom annotation
#GET
#Path("{id}")
public Response getUserById(#PathParam("id") Long id) throws IOException, MainException {
#Secured //custom annotation
#Provider
#Priority(Priorities.AUTHENTICATION)
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class AuthenticationFilter implements ContainerRequestFilter
#Autowired
private User userContext;
#Secured //custom annotation
#Provider
#Priority(Priorities.AUTHORIZATION)
#Scope(value="request", proxyMode= ScopedProxyMode.TARGET_CLASS)
public class AuthorizationFilter implements ContainerRequestFilter {
#Autowired
private User userContext;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class User extends ModelBase implements Serializable {

Since Providers are not request scoped, I had to inject whole ApplicationContext so I could directly modify the right instance of userContext bean (which is request scoped). Basically I did something like this in both filters:
#Autowired
private ApplicationContext applicationContext;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
User userBean=applicationContext.getBean(User.class);
....
userBean.setSomething("aaa");
Then I could just autowire such bean in my REST Resources, because they are request scoped by defaul:
#Component
#Api(value="/users", description = "Endpoint for Users listing")
#Consumes({MediaType.APPLICATION_JSON, Constants.API_VERSIONS.V1_HEADER_XML, Constants.API_VERSIONS.V1_HEADER_JSON})
#Produces({MediaType.APPLICATION_JSON, Constants.API_VERSIONS.V1_HEADER_XML, Constants.API_VERSIONS.V1_HEADER_JSON})
#Path("/users")
public class UserResource {
private static final Logger logger = LoggerFactory.getLogger(UserResource.class);
#Autowired
private User authenticatedUser;
This solution should be viable and resistent against thread race conditions etc.

Related

Mockito: How to mock WebClient configuring Beans

I'm using Mockio, Wiremock and WebClient and I want to test my service layer.
My goal is to use an instance of the webclient and do a real request to wiremock.
Therefore I have to use a standard configuration and not my oauth config from the production mode.
In the service class, I execute reuqets to another api. So the class under test ist annotated with #Service.
Here is the class:
#Service
public class UserServiceImpl implements UserService{
private final Logger log = Logger.getLogger(this.getClass().getName());
private final WebClient webClient;
private final ApplicationConstants applicationConstants;
public UserServiceImpl (WebClient webClient, ApplicationConstants applicationConstants) {
this.applicationConstants = applicationConstants;
this.webClient = webClient;
}
#Override
public User getUserById(#NotNull(message = "userId must not be null.") #NotBlank(message = "userId must not be blank.") String userId) {
return webClient.get()...
}
I configured my WebClient to use Oauth via two Bean Methods in a class annotated with #Configuration.
#Configuration
public class WebClientConfig {
#Bean
public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
...
}
/*
Manages the auth process and token refresh process
*/
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
...
}
}
Because I want to use the webClient without oauth to call wiremock, I want to replace the Beans to return a simple Webclient.builder().build();
So I did:
#ExtendWith({SpringExtension.class, WireMockExtension.class, MockitoExtension.class})
public class TestClass {
#Mock
WebClientConfig webClientConfig;
#MockBean
WebClient webClient;
#InjectMocks
UserServiceImpl userService;
In general as I understand Mockito, i would use my class under test ( the userServiceImpl) with #InjectMocks, so a real instance is used and the dependencies are injected. Therefor I have to provide a Mock for the Webclient. As I don't want to Mock the webclient and just want to configure it different, I do not have to use #Mock. Instead it should be somethig like #MockBean as this annotation creates a bean and replaces existing ones in the context. So I have to mock the Webclientconfig class with #Mock and define something like
when(webclientConfig).webclient(any(OAuth2AuthorizedClientManager.class)).thenReturn(Webclient.builder.build);
But this does not work as I always get a nullpointer exception on the call.
So the basic questions are:
Is my understanding of Mockito right?
How do I have to Manage the Webclient config?
Looks like a case of configuring MockBeans before the rest of the application starts up, which is answered here:
Configure #MockBean component before application start
As of this writing, the answer above mentions using #Primary or #MockBean(answer = Answers.CALLS_REAL_METHODS) as workarounds.

How to include custom security interceptor in spring boot test

I want to do some end-to-end test for spring boot rest-api application. To achieve this im using spring mock mvc. But i can't get the 200 response because the rest api is using custom security interceptor to validate the token in request. Instead i keep getting 401 as a response. How to include this token validation in my test?
I've tried several configuration by including #ContextConfiguration(classes = {WebMvcConfig.class}) in my test class. WebMvcConfig is configuration class to register the interceptor.
This is my test file
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
#SpringBootTest(classes = VeripalServiceApplication.class)
#TestPropertySource(locations="classpath:test.properties")
#Transactional
public class VeripalTextConfigurationTest {
#Autowired
private MockMvc mockMvc;
#Test
public void happpyPath_thenReturns200() throws Exception {
String jsonBody = "some json body";
String endPoint = "/end_point_to_my_api";
HttpHeaders headers = new HttpHeaders();
headers.add("token", "this_is_my_token");
headers.setContentType(aplication/json);
/** Hit the API */
mockMvc.perform(post(endPoint)
.headers(httpHeaders)
.content(jsonBody)
)
.andExpect(status().isOk()).andDo(print());
}
}
And this is the #Configuration
#Configuration
#EnableScheduling
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
private ConsumerService consumerService;
#Autowired
private EndpointService endpointService;
#Autowired
private ConsumerConfigurationService consumerConfigurationService;
#Autowired
private AccessLimitService accessLimitService;
#Autowired
private ConfigurationHistoryService configurationHistoryService;
#Autowired
private LimitCarryOverService limitCarryOverService;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor(consumerService, endpointService, consumerConfigurationService, accessLimitService, configurationHistoryService, limitCarryOverService));
}
}
And this is my Interceptor class
public class Interceptor implements HandlerInterceptor {
// some code here ...
}
you need to have a clear picture of request life-cycle in Servlet API and Spring Security framework.
This article might help you to understand this flow http://blog.florian-hopf.de/2017/08/spring-security.html
So, I'm pretty sure, you have an issue in authentication filters, thus you can resolve it in couple ways:
Disable security, for example by using #AutoConfigureMockMvc(secure = false)
Or you need to mock some places (AuthenticationProvider, UserDetailsService, etc) where you can provide Authentication object
Or, it also might help, try to play with #WithMockUser
.
Related posts:
Spring Test & Security: How to mock authentication?
V2: use IoC + Mockito, e.g. stub it for unit tests. I don't see how your code are written, so I believe a snippet below might help you.
// #Import({MyAuthCustomInterceptor.class}) // eq to #Component/#Service to create a bean
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Autowired
MyAuthCustomInterceptor interceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}
public class VeripalTextConfigurationTest {
#MockBean
MyAuthCustomInterceptor interceptor;
#SetUp
public void setup(){
Mockito.when(interceptor.preHandle(...)).thenReturn(true);
}
}

Propagate Spring boot security context into a bean annotated with #KafkaListener

I have a bean that is annotated with #KafkaListener and inside this bean, I am planning to get the logged-in user credentials through SecurityContextHolder.
However, SecurityContextHolder.getContext().getAuthentication() is giving me a null object probably because this is running in a different thread.
In this case, is there a way to propagate the SecurityContext from ThreadLocal to another thread? Can it easily be done in my Spring Boot configuration?
Below is the sample code:
#Component
#Slf4j
public class MessageConsumer {
private final MessageService messageService;
#Autowired
public MessageConsumer(final MessageService service) {
messageService = service;
}
#KafkaListener(topics = "myTopic")
public void receive(final List<Message> message) {
messageService.consumerAnStoreMessage(message, SecurityContextHolder.getContext().getAuthentication());
}
}
The SecurityContext's authentication represents, for exemple, a user who called a webservice, a website's page... etc...
When you listen to a kafka message, which user's context should be used ?
I don't think what you are trying to do really makes sens.

Spring - Is it good to register request scoped bean at every request though code?

In Spring Web application, I have to use specific value from request object in another spring classes within application. Value is request specific value.
In the following example, is it good way to register value coming from Request Body every time and use #Autowired with #RequestScope annotation to use value in another spring(e.g. #Service) classes? Is it good to register RequestScopedType bean value for each request through BeanFactory?
#RestController
#RequestMapping("/")
public class VehicleServiceController {
#Autowired
private BeanFactory beanFactory;
#Autowired
private ServiceClass serviceClass;
#PostMapping(path = "/postDetails", consumes = MediaType.APPLICATION_JSON_VALUE)
public OutputPayload postDetails(
#RequestBody InputPayload inboundPayload) throws Exception {
beanFactory.getBean(RequestScopedType.class).setValue(inboundPayload.getType());
return serviceClass.methodToCall();
}
}
Will there be any impact on performance as load is very huge? Is there any another way to inject/get RequestBody object value(inboundPayload.getType())?
You don't have to do beanFactory.getBean(RequestScopedType.class). You can just simply autowire it #Autowired RequestScopedType requestScopedType.
Just don't forget to change the scope of the bean as Request.
#Component
#Scope(scopeName = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedType {
But this begs another question, why over complicate things, why can't you pass inboundPayload.getType() to the serviceClass.methodToCall(); ?
What is stopping you from using it this way return serviceClass.methodToCall(inboundPayload.getType());

How to get Spring WebContext in class annotated #controller

In Spring MVC with annotation, we mark any POJO with #Controller.
In this controller we can get WebApplicationContext, using autowired property.
#Controller
public class HomePageController {
#Autowired
ApplicationContext act;
#RequestMapping("/*.html")
public String handleBasic(){
SimpleDomain sd = (SimpleDomain)act.getBean("sd1");
System.out.println(sd.getFirstProp());
return "hello";
}
But in this approach we do not have servletContext handy with us. So is there way we can still use older way of getting WebApplicationContext ? i.e.
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)
How will we get servletContext here ?
I am not facing any compulsion to use old way; so this question is just out of curiosity to check flexibility of spring.
Also It can be a interview question.
You can just inject it into your controller:
#Autowired private ServletContext servletContext;
Or take HttpServletRequest as a parameter and get it from there:
#RequestMapping(...)
public ModelAndView myMethod(HttpServletRequest request ...){
ServletContext servletContext = request.getServletContext()
}
The following is correct approach :
#Autowired
ServletContext context;
Otherwise instead of auto wiring the ServletContext, you can implement ServletContextAware. Spring will notice this when running in a web application context and inject the ServletContext. Read this.
You can also do it inline:
#RequestMapping(value = "/demp", method = RequestMethod.PUT)
public String demo(#RequestBody String request) {
HttpServletRequest re3 = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
return "sfsdf";
}
You can implement an Interface from Spring called org.springframework.web.context.ServletContextAware
public class MyController implements ServletContextAware {
private ServletContext servletContext;
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext=servletContext;
}
}
Then you can use the servletContext any place in the class.
By accessing the session you can get the servlet context, sample code:
#Controller
public class MyController{
....
#RequestMapping(...)
public ModelAndView myMethod(HttpSession session ...){
WebApplicationContextUtils.getRequiredWebApplicationContext(session.getServletContext())
}
}
You can get the HttpSession from the HttpServletRequest also.

Resources