Как смоделировать пользовательские претензии JWT в @WebMvcTest - PullRequest
0 голосов
/ 25 октября 2019

Я использую Spring Boot 2.2.0.RELEASE, и мой бэкэнд на основе Spring действует как сервер ресурсов OAuth2, который нормально работает в рабочей среде.

Все мои конечные точки REST защищены:

public class BookingController {
 @PreAuthorize("hasAuthority('booking:WRITE')")
 @PostMapping(value = "/book")
 public ResponseEntity<Void> createBooking(@RequestBody BookModel bookModel, JwtAuthenticationToken jwt) {..}

Я хотел написать модульный тест для моих REST API и хотел бы смоделировать токен JWT.

Я пробовал следующее, но всегда получаю «Сообщение об отказе в доступе»

Мой модульный тест выглядит следующим образом:

    @WebMvcTest(controllers = BookingController.class)
    public class BookingControllerTests {

     @Autowired
     private ObjectMapper objectMapper;

     @Autowired
     MockMvc mockMvc;

     @MockBean
     JwtDecoder jwtDecoder;

     @Test
     public void when_valid_booking_then_return_200() {
       BookModel bookModel = new BookModel();
       mockMvc
            .perform(post("/book")
            .with(jwt(jwt ->jwt().authorities(new SimpleGrantedAuthority("booking:WRITE"))))
            .contentType("application/json")
            .content(objectMapper.writeValueAsBytes(bookModel)))
            .andExpect(status().isCreated());
     }

Каким-то образом утверждения, определенные в mockMvc, игнорируются. Смотрите выходные данные отладки:

PrePostAnnotationSecurityMetadataSource : @org.springframework.security.access.prepost.PreAuthorize(value=hasAuthority('booking:WRITE')) found on specific method: public org.springframework.http.ResponseEntity BookingController.createBooking(BookModel ,org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken) 

o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /book; Attributes: [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken@eca97305: Principal: org.springframework.security.oauth2.jwt.Jwt@bbd01fb9; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: SCOPE_read
o.s.s.a.v.AffirmativeBased               : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@18907af2, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
o.s.s.w.FilterChainProxy                 : /book reached end of additional filter chain; proceeding with original chain
o.s.s.a.i.a.MethodSecurityInterceptor    : Secure object: ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity BookingController.createBooking(BookModel,org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken); target is of class [BookModel]; Attributes: [[authorize: 'hasAuthority('booking:WRITE')', filter: 'null', filterTarget: 'null']]
o.s.s.a.i.a.MethodSecurityInterceptor    : Previously Authenticated: org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken@eca97305: Principal: org.springframework.security.oauth2.jwt.Jwt@bbd01fb9; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: SCOPE_read
o.s.s.a.v.AffirmativeBased               : Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter@67afe909, returned: -1
o.s.s.a.v.AffirmativeBased               : Voter: org.springframework.security.access.vote.RoleVoter@79f1e22e, returned: 0
o.s.s.a.v.AffirmativeBased               : Voter: org.springframework.security.access.vote.AuthenticatedVoter@6903ed0e, returned: 0
c.s.d.r.e.GlobalExceptionHandler         : mapped AccessDeniedException to FORBIDDEN

org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]

Ответы [ 2 ]

1 голос
/ 07 ноября 2019

В вашей лямбде для насмешки над JWT вы дважды вызываете постпроцессор, дважды используя скобки .with(jwt(jwt ->jwt()...))

Вместо этого попробуйте

mockMvc
    .perform(post("/book")
    .with(jwt().authorities(new SimpleGrantedAuthority("booking:WRITE"))))
0 голосов
/ 26 октября 2019

Если вам нужен контекст безопасности, то вы не пишете юнит-тест.

В любом случае, почему бы не использовать @WithMockUser?

Здесь вы можете увидеть снимок того, какиспользовать его в интеграционном тесте, который издевается над бизнес-уровнем.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("api")
public class GetAllProfilesControllerITest {

@MockBean
private GetAllProfilesDAO getAllProfilesDAO;
@MockBean
private ProfileControllerMapper profileControllerMapper;

@Inject
private WebApplicationContext context;

private MockMvc mockMvc;

private static final String ENDPOINT = "/profiles";

@Before
public void setUp() {
    mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(springSecurity())
            .build();

}


@WithMockUser(authorities = "MINION")
@Test
public void should_returnUnauthorized_when_cantView() throws Exception {

    //when
    mockMvc.perform(get(ENDPOINT))
            //then
            .andExpect(status().isUnauthorized());

}

@WithMockUser(authorities = {"VIEW", "CREATE"})
@Test
public void should_returnOk_when_canView() throws Exception {

    //when
    mockMvc.perform(get(ENDPOINT))
            //then
            .andExpect(status().isOk());

}

@WithMockUser(authorities = "PUPPY")
@Test
public void should_returnOk_when_puppy() throws Exception {

    //when
    mockMvc.perform(get(ENDPOINT))
            //then
            .andExpect(status().isOk());

}

}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...