@WithUserDetails 失败,因为找不到用户名

发布于 2025-01-16 19:39:46 字数 6979 浏览 0 评论 0原文

我正在尝试对我的 AuthController.java 进行测试,但它一直失败,提示找不到用户名。我已将这个问题缩小到 UserService.java 中检查是否 foundUser == null 的行。

这是我的 AuthControllerTests.java

package dev.tdwl.controller;

import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(classes = {AuthController.class, UserService.class})
@AutoConfigureMockMvc
public class AuthControllerTests {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private PasswordEncoder encoder;

    @MockBean
    private JwtUtils jwtUtils;

    @MockBean
    private AuthenticationManager authenticationManager;

    @MockBean
    private CategoryListsRepository categoryRepo;

    @MockBean
    private UserRepository userRepository;

    @Test
    @WithUserDetails(value = "testuser", userDetailsServiceBeanName = "userService")
    void testAuthenticationCheckValid() {
        try {
            mockMvc.perform(get("/auth/check")).andExpect(status().isOk()).andDo(print());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

和我的 AuthController.java

package dev.tdwl.controller;


import com.mongodb.MongoException;
import dev.tdwl.model.AuthenticationRequest;
import dev.tdwl.model.CategoryLists;
import dev.tdwl.model.JwtResponse;
import dev.tdwl.model.User;
import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;

@RestController
public class AuthController {

    private CategoryListsRepository categoryRepo;
    private UserRepository userRepo;
    private AuthenticationManager authenticationManager;
    private PasswordEncoder encoder;
    private JwtUtils jwtUtils;

    @Autowired
    public AuthController(UserRepository repository, AuthenticationManager authenticationManager, PasswordEncoder encoder, JwtUtils jwtUtils, CategoryListsRepository categoryRepo) {
        this.authenticationManager = authenticationManager;
        this.userRepo = repository;
        this.encoder = encoder;
        this.jwtUtils = jwtUtils;
        this.categoryRepo = categoryRepo;
    }

    @GetMapping("/auth/check")
    public ResponseEntity<?> verifyLogin() {
        UserDetailsImpl userDetails = (UserDetailsImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (userDetails.getId() == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        return ResponseEntity.ok(userDetails);
    }

    @PostMapping("/auth/login")
    public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) {
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getEmail(), authenticationRequest.getPassword()));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = jwtUtils.generateJwtToken(authentication);

        UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
        return ResponseEntity.ok(new JwtResponse(jwt, user.getUsername(), user.getId()));
    }

    @PostMapping("/auth/signup")
    public ResponseEntity<?> authenticateClient(@RequestBody AuthenticationRequest authenticationRequest) {
        String email = authenticationRequest.getEmail();
        String password = authenticationRequest.getPassword();

        User newUser = new User(email, encoder.encode(password));

        try {
            userRepo.save(newUser);
            CategoryLists newList = new CategoryLists(newUser.getId(), Collections.emptyList());
            categoryRepo.save(newList);
        } catch (DuplicateKeyException | MongoException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }

        return ResponseEntity.ok("User registered successfully!");
    }
}

和 UserService.java

package dev.tdwl.services;

import dev.tdwl.model.User;
import dev.tdwl.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserService implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User foundUser = userRepository.findUserByEmail(username);
        if (foundUser == null) throw new UsernameNotFoundException("User not found.");

        return UserDetailsImpl.build(foundUser);
    }
}

失败的行位于 UserService 中。 java 尝试findUserByEmail,因为userRepository被模拟,它只返回null并抛出用户名未找到异常。如果我删除 Mock,那么我的测试会失败,因为它无法在 AuthController 中找到构造函数所需的 UserRepository bean

有关如何正确执行此操作的任何想法吗?

I am trying to make a test for my AuthController.java but it keeps failing saying that the username is not found. I have narrowed this problem down to the line in UserService.java that checks if foundUser == null.

Here is my AuthControllerTests.java

package dev.tdwl.controller;

import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest(classes = {AuthController.class, UserService.class})
@AutoConfigureMockMvc
public class AuthControllerTests {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private PasswordEncoder encoder;

    @MockBean
    private JwtUtils jwtUtils;

    @MockBean
    private AuthenticationManager authenticationManager;

    @MockBean
    private CategoryListsRepository categoryRepo;

    @MockBean
    private UserRepository userRepository;

    @Test
    @WithUserDetails(value = "testuser", userDetailsServiceBeanName = "userService")
    void testAuthenticationCheckValid() {
        try {
            mockMvc.perform(get("/auth/check")).andExpect(status().isOk()).andDo(print());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And my AuthController.java

package dev.tdwl.controller;


import com.mongodb.MongoException;
import dev.tdwl.model.AuthenticationRequest;
import dev.tdwl.model.CategoryLists;
import dev.tdwl.model.JwtResponse;
import dev.tdwl.model.User;
import dev.tdwl.repository.CategoryListsRepository;
import dev.tdwl.repository.UserRepository;
import dev.tdwl.security.jwt.JwtUtils;
import dev.tdwl.services.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;

@RestController
public class AuthController {

    private CategoryListsRepository categoryRepo;
    private UserRepository userRepo;
    private AuthenticationManager authenticationManager;
    private PasswordEncoder encoder;
    private JwtUtils jwtUtils;

    @Autowired
    public AuthController(UserRepository repository, AuthenticationManager authenticationManager, PasswordEncoder encoder, JwtUtils jwtUtils, CategoryListsRepository categoryRepo) {
        this.authenticationManager = authenticationManager;
        this.userRepo = repository;
        this.encoder = encoder;
        this.jwtUtils = jwtUtils;
        this.categoryRepo = categoryRepo;
    }

    @GetMapping("/auth/check")
    public ResponseEntity<?> verifyLogin() {
        UserDetailsImpl userDetails = (UserDetailsImpl) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        if (userDetails.getId() == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        return ResponseEntity.ok(userDetails);
    }

    @PostMapping("/auth/login")
    public ResponseEntity<?> login(@RequestBody AuthenticationRequest authenticationRequest) {
        Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticationRequest.getEmail(), authenticationRequest.getPassword()));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = jwtUtils.generateJwtToken(authentication);

        UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
        return ResponseEntity.ok(new JwtResponse(jwt, user.getUsername(), user.getId()));
    }

    @PostMapping("/auth/signup")
    public ResponseEntity<?> authenticateClient(@RequestBody AuthenticationRequest authenticationRequest) {
        String email = authenticationRequest.getEmail();
        String password = authenticationRequest.getPassword();

        User newUser = new User(email, encoder.encode(password));

        try {
            userRepo.save(newUser);
            CategoryLists newList = new CategoryLists(newUser.getId(), Collections.emptyList());
            categoryRepo.save(newList);
        } catch (DuplicateKeyException | MongoException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }

        return ResponseEntity.ok("User registered successfully!");
    }
}

and my UserService.java

package dev.tdwl.services;

import dev.tdwl.model.User;
import dev.tdwl.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserService implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User foundUser = userRepository.findUserByEmail(username);
        if (foundUser == null) throw new UsernameNotFoundException("User not found.");

        return UserDetailsImpl.build(foundUser);
    }
}

The line this is failing on is in UserService.java where it tries to findUserByEmail, because userRepository is mocked it just returns null and throws the username not found exception. If I remove the Mock, then my test fails because it couldn't find the UserRepository bean required for the constructor in AuthController

Any ideas on how to do this correctly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

攒一口袋星星 2025-01-23 19:39:46

只需在这一行中为 UserRepository 声明 Mockbean:

@MockBean
private UserRepository userRepository;

它不会工作。您需要在 mockMvc.perform() 之前添加以下代码。

Mockito.when(userRepository.findUserByEmail(Mockito.anyString)).thenReturn(new User());

这将模拟 findUserByEmail 方法,并且不会返回 null。

just by declaring Mockbean for UserRepository in this line:

@MockBean
private UserRepository userRepository;

It won't work. You need to add following piece of code before mockMvc.perform().

Mockito.when(userRepository.findUserByEmail(Mockito.anyString)).thenReturn(new User());

This will mock the findUserByEmail method and it will not return null.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文