Junit5 - SpringBoot: Error creating bean with name after applied configuration in WebSecurity
Link to the project on Github: https://github.com/sineverba/online-banking-backend/tree/develop
I have these tests for a controller:
@ExtendWith(SpringExtension.class)
@WebMvcTest(BankAccountTransactionsController.class)
class BankAccountTransactionsControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private BankAccountTransactionsService bankAccountTransactionsService;
@Autowired
private ObjectMapper objectMapper;
/**
* index
*/
@Test
void testCanIndex() throws Exception {
var transaction01 = BankAccountTransactionsControllerTest.validBankAccountTransactionsEntity(1L, new BigDecimal(99.99), "First Transaction");
var transaction02 = BankAccountTransactionsControllerTest.validBankAccountTransactionsEntity(2L, new BigDecimal(150.00), "Second Transaction");
var result = new ArrayList<BankAccountTransactionsEntity>();
result.add(transaction01);
result.add(transaction02);
when(bankAccountTransactionsService.index()).thenReturn(result);
mvc.perform(get("/api/v1/bank-account-transactions/"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()", is(2)))
.andExpect(jsonPath("$[0].id", is(1)))
.andExpect(jsonPath("$[1].id", is(2)))
.andExpect(jsonPath("$[0].amount", is(new BigDecimal(99.99))))
.andExpect(jsonPath("$[1].amount", is(150)))
.andExpect(jsonPath("$[0].purpose", is("First Transaction")))
.andExpect(jsonPath("$[1].purpose", is("Second Transaction")))
.andExpect(jsonPath("$[0]", Matchers.hasKey("transactionDate")))
.andExpect(jsonPath("$[1]", Matchers.hasKey("transactionDate")));
}
@Test
void testCanSave() throws Exception {
var id = 1L;
var transactionToSave = validBankAccountTransactionsEntity(new BigDecimal(99.99), "First Transaction");
var savedTransaction = validBankAccountTransactionsEntity(id, new BigDecimal(99.99), "First Transaction");
when(bankAccountTransactionsService.post(transactionToSave))
.thenReturn(savedTransaction);
mvc.perform(
post("/api/v1/bank-account-transactions/")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsBytes(transactionToSave))
)
.andExpect(status().isCreated())
.andExpect(jsonPath("$.id", is((int) id)))
.andExpect(jsonPath("$.amount", is(transactionToSave.getAmount())));
}
private static BankAccountTransactionsEntity validBankAccountTransactionsEntity(Long id, BigDecimal amount, String purpose) {
return BankAccountTransactionsEntity.
builder()
.id(id)
.amount(amount)
.purpose(purpose)
.build();
}
private static BankAccountTransactionsEntity validBankAccountTransactionsEntity(BigDecimal amount, String purpose) {
return BankAccountTransactionsEntity.
builder()
.amount(amount)
.purpose(purpose)
.build();
}
It works, and it works very well.
I added a new class:
package com.bitbank.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import com.bitbank.services.v1.UserDetailsServiceImpl;
import com.bitbank.utils.JwtUtils;
public class AuthTokenFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
And I edited WebSecurityConfig
as follow, adding http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
private List<String> getAllowedOrigins() {
return Arrays.asList("http://localhost:[*]", "https://online-banking-frontend.netlify.app",
"https://online-banking-frontend.vercel.app");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// Enable CORS and disable CSRF
http = http.cors().and().csrf().disable();
http.authorizeRequests().mvcMatchers("/api/v1/ping").permitAll()
.mvcMatchers("/api/v1/bank-account-transactions").permitAll().anyRequest().authenticated();
// THIS IS THE ISSUE! =========>
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOriginPatterns(getAllowedOrigins());
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
After this, I got error on tests (but not on real launch of Spring)
java.lang.IllegalStateException: Failed to load ApplicationContext
Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authenticationJwtTokenFilter': Unsatisfied dependency expressed through field 'jwtUtils'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.bitbank.utils.JwtUtils' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authenticationJwtTokenFilter': Unsatisfied dependency expressed through field 'jwtUtils'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.bitbank.utils.JwtUtils' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
No qualifying bean of type 'com.bitbank.utils.JwtUtils' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Edit n. 1
I added some MockedBean:
@ExtendWith(SpringExtension.class)
@WebMvcTest(BankAccountTransactionsController.class)
class BankAccountTransactionsControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private BankAccountTransactionsService bankAccountTransactionsService;
// new
@MockBean
public AuthTokenFilter authenticationJwtTokenFilter;
// new
@MockBean
private JwtUtils jwtUtils;
@Autowired
private ObjectMapper objectMapper;
And now I have a different issue:
java.lang.IllegalArgumentException: Attribute name must not be null
at org.springframework.util.Assert.notNull(Assert.java:201)
at org.springframework.mock.web.MockHttpServletRequest.setAttribute(MockHttpServletRequest.java:762)
from Recent Questions - Stack Overflow https://ift.tt/Zg0Yyo4
https://ift.tt/2seQpNE
Comments
Post a Comment