Fra*_*pos 6 java mockito jwt spring-boot
我有一个控制器,它向用户提供 403 响应,除非他们使用 JWT 令牌进行身份验证,该令牌通过授权标头作为不记名令牌传递。我正在寻找有关如何使用 Mockito 进行测试的资源,但到目前为止我还不是很成功,因为他们中的大多数人告诉我使用 @WithMockUser 注释,我知道这是为了 Spring 安全性,是的,但不包括模拟对于 JWT 令牌。我尝试过模拟一些对象,例如 UserDetailsClass 和 JwtFilter,甚至对不记名令牌进行硬编码,但我认为应该有更多内容。
@MockBean
private CategoryCommandService categoryCommandService;
@Autowired
private MockMvc mockMvc;
@MockBean
private MyUserDetailsService myUserDetailsService;
@MockBean
private CategoryRepository categoryRepository;
@MockBean
private JwtUtil jwtUtil;
@Autowired
private JwtRequestFilter filter;
@Test
void testCreateCategory() throws Exception {
CategoryCreateDto categoryCreateDto = new CategoryCreateDto("category");
CategoryCreateDto categoryCreateResponseDto = new CategoryCreateDto(UUID.fromString("2da4002a-31c5-4cc7-9b92-cbf0db998c41"), "category");
String jsonCreate = asJsonString(categoryCreateDto);
String jsonResponse = asJsonString(categoryCreateResponseDto);
RequestBuilder request = MockMvcRequestBuilders
.post("/api/adverts/category")
.content(jsonCreate)
.header("Authorization", "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJmb29AZW1haWwuY29tIiwiZXhwIjoxNjM4ODU1MzA1LCJpYXQiOjE2Mzg4MTkzMDV9.q4FWV7yVDAs_DREiF524VZ-udnqwV81GEOgdCj6QQAs")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON);
mockMvc.perform(request).andReturn();
when(categoryCommandService.createCategory(categoryCreateDto)).thenReturn(
categoryCreateResponseDto);
MvcResult mvcResult = mockMvc.perform(request)
.andExpect(status().is2xxSuccessful())
.andExpect(content().json(jsonResponse, true))
.andExpect(jsonPath("$.id").value("2da4002a-31c5-4cc7-9b92-cbf0db998c41"))
.andExpect(jsonPath("$.title").value("category"))
.andReturn();
logger.info(mvcResult.getResponse().getContentAsString());
}
Run Code Online (Sandbox Code Playgroud)
这是我的控制器:
@CrossOrigin
@RequestMapping("/api/adverts/category")
@RestController
public class CategoryCommandController {
@Autowired
private CategoryCommandService categoryCommandService;
@Autowired
private CategoryRepository categoryRepository;
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> createCategory(@RequestBody CategoryCreateDto categoryCreateDto) {
if (categoryCreateDto.getTitle() != null) {
return new ResponseEntity<>(categoryCommandService.createCategory(categoryCreateDto), HttpStatus.CREATED);
}
else {
return new ResponseEntity<>(new FeedbackMessage("Missing title"), HttpStatus.BAD_REQUEST);
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的过滤器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static com.example.adverts.SecurityConstants.SIGN_UP_URL;
@Component
public class JwtRequestFilter extends OncePerRequestFilter {
@Autowired
private MyUserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String path = request.getRequestURI();
if (path.equals(SIGN_UP_URL)) {
chain.doFilter(request, response);
return;
}
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
} else {
response.setStatus(HttpStatus.FORBIDDEN.value());
}
if (username != null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
chain.doFilter(request, response);
}
}
}
Run Code Online (Sandbox Code Playgroud)
和 JwtUtil 类:
@Service
public class JwtUtil {
private String SECRET_KEY = "secret";
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
Run Code Online (Sandbox Code Playgroud)
这是整个 Github 分支。
https://github.com/francislainy/adverts-backend/tree/dev_jwt
谢谢。
更新
为了清楚起见,如果我硬编码一个有效的令牌,我会得到一个 200 状态代码,但我的测试仍然会失败,不会返回任何内容,而在 JWT 和 Spring 安全性之前它们都通过了。
主要问题是使用
@MockBean
private JwtUtil jwtUtil;
Run Code Online (Sandbox Code Playgroud)
这使得 JwtRequestFilter 执行错误
@MockBean
private JwtUtil jwtUtil;
Run Code Online (Sandbox Code Playgroud)
因为username模拟 bean 总是返回 null。
要使用实际的JwtUtils
AddincludeFilters将其包含在 spring 上下文中,那么我们还需要myUserDetailsService.loadUserByUsername模拟JwtRequestFilter. 之后测试就会通过。请参阅下面代码中的注释以了解更改。
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
15763 次 |
| 最近记录: |