我正在尝试在 Spring Boot 应用程序中实现 JWT 身份验证,但遇到一些错误。请帮忙。 错误和日志位于本文档末尾
这是我的安全配置类
@Configuration
public class SecurityConfig {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private JwtFilter jwtFilter;
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager theUserDetailsManager = new JdbcUserDetailsManager(dataSource);
theUserDetailsManager
.setUsersByUsernameQuery("select user_email, user_password, enabled from chat_user where user_email=?");
theUserDetailsManager.setAuthoritiesByUsernameQuery("select user_email, authority from authorities where user_email=?");
return theUserDetailsManager;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(configurer -> configurer
.requestMatchers("/css/**", "/images/**").permitAll() //initially non authenticated users does not have access to css, so this is added
.requestMatchers("/showSignUpForm","/showLoginForm","/processSignUpForm").permitAll() // allow unauthenticated access to sign up page
.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin(form -> form.loginPage("/showLoginForm")
.loginProcessingUrl("/authenticateTheUser")
.usernameParameter("user_email")
.passwordParameter("user_password")
.defaultSuccessUrl("/showChatPage", true)
.permitAll());
return http.build();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration auth) throws Exception {
return auth.getAuthenticationManager();
}
}
这是我的控制器
@RestController
@RequestMapping("/public")
@Slf4j
public class PublicController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private ChatUserService userService;
@Autowired
private JwtUtil jwtUtil;
@GetMapping("/health-check")
public String healthCheck() {
return "Ok";
}
@PostMapping("/signup")
public void signup(@RequestBody ChatUser user) {
userService.saveChatUser(user);
}
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody ChatUser user) {
try{
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUser_email(), user.getUser_password()));
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUser_email());
String jwt = jwtUtil.generateToken(userDetails.getUsername());
return new ResponseEntity<>(jwt, HttpStatus.OK);
}catch (Exception e){
log.error("Exception occurred while createAuthenticationToken ", e);
return new ResponseEntity<>("Incorrect username or password", HttpStatus.BAD_REQUEST);
}
}
}
这是我的 JwtFilter 类
@Component
public class JwtFilter extends OncePerRequestFilter{
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
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);
}
if (username != null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt)) {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
}
这是我的 UserDetailsServiceImpl 类
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private ChatUserService chatUserService;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
ChatUser user = chatUserService.findByUserEmail(email);
if (user != null) {
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUser_email())
.password(user.getUser_password())
.roles("ROLE_USER")
.build();
}
throw new UsernameNotFoundException("User not found with username: " + email);
}
}
ChatUserService接口
public interface ChatUserService {
public List<ChatUser> getChatUsers();
public void saveChatUser(ChatUser theChatUser);
public ChatUser getChatUser(int theId);
public void deleteChatUser(int theId);
public ChatUser findByUsername(String username);
public ChatUser findByUserEmail(String email);
}
ChatUserServiceImpl
@Service
public class ChatUserServiceImpl implements ChatUserService{
@Autowired
private ChatUserDAO chatUserDao;
@Autowired
private AuthorityDAO authorityDao;
@Override
@Transactional
public List<ChatUser> getChatUsers() {
return chatUserDao.getchatUsers();
}
@Override
@Transactional
public void saveChatUser(ChatUser theChatUser) {
chatUserDao.saveChatUser(theChatUser);
// Create and save the Authority
Authority authority = new Authority(theChatUser.getUser_email(), "ROLE_USER");
authorityDao.saveOrUpdateAuthority(authority);
// Add the Authority to the ChatUser
theChatUser.getAuthorities().add(authority);
}
@Override
@Transactional
public ChatUser getChatUser(int theId) {
return chatUserDao.getChatUser(theId);
}
@Override
@Transactional
public void deleteChatUser(int theId) {
chatUserDao.deleteChatUser(theId);
}
@Override
@Transactional
public ChatUser findByUsername(String username) {
return chatUserDao.findByUsername(username);
}
@Override
@Transactional
public ChatUser findByUserEmail(String email) {
return chatUserDao.findByUserEmail(email);
}
}
ChatUserDAO 的类似接口 我的 ChatUserDAOImpl 类看起来像这样,它使用休眠会话工厂
@Repository
public class ChatUserDAOImpl implements ChatUserDAO{
@Autowired
private SessionFactory sessionFactory;
@Override
public List<ChatUser> getchatUsers() {
Session currentSession = sessionFactory.getCurrentSession();
Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser", ChatUser.class);
List<ChatUser> chatUsers = theQuery.getResultList();
return chatUsers;
}
@SuppressWarnings("deprecation")
@Override
public void saveChatUser(ChatUser theChatUser) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.saveOrUpdate(theChatUser);
}
@Override
public ChatUser getChatUser(int theId) {
Session currentSession = sessionFactory.getCurrentSession();
ChatUser theChatUser = currentSession.get(ChatUser.class, theId);
return theChatUser;
}
@Override
public void deleteChatUser(int theId) {
Session currentSession = sessionFactory.getCurrentSession();
Query theQuery = currentSession.createQuery("delete from ChatUser where chat_id=:chatUserId");
theQuery.setParameter("chatUserId", theId);
theQuery.executeUpdate();
}
@Override
public ChatUser findByUsername(String username) {
Session currentSession = sessionFactory.getCurrentSession();
Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser where user_name=:u", ChatUser.class);
theQuery.setParameter("u", username);
try {
return theQuery.getSingleResult();
} catch (NoResultException nre) {
return null;
}
}
@Override
public ChatUser findByUserEmail(String email) {
Session currentSession = sessionFactory.getCurrentSession();
Query<ChatUser> theQuery = currentSession.createQuery("from ChatUser where user_email=:e", ChatUser.class);
theQuery.setParameter("e", email);
try {
return theQuery.getSingleResult();
} catch (NoResultException nre) {
return null;
}
}
}
当我尝试运行该应用程序时,出现此错误
描述:
com.springprojects.realtimechatapp.filter.JwtFilter 中的字段 userDetailsService 需要一个 bean,但找到了 2 个: - userDetailsServiceImpl:在文件 [C:...\service\UserDetailsServiceImpl.class] 中定义 - userDetailsManager:由类路径资源 [com/springprojects/.../SecurityConfig.class] 中的方法“userDetailsManager”定义
行动:
考虑将其中一个bean标记为@Primary,更新消费者以接受多个bean,或使用@Qualifier来标识应该使用的bean
我尝试过的 安全类中的 UserDetailsServiceImpl 实例不允许使用 @Primary。我在 UserDetailsManager 上尝试了@Primary,但现在它给了我这个错误:
说明: com.springprojects.realtimechatapp.dao.ChatUserDAOImpl 中的字段 sessionFactory 需要一个类型为“org.hibernate.SessionFactory”的 bean,但无法找到。 注入点有以下注释: - @org.springframework.beans.factory.annotation.Autowired(必需= true)
行动: 考虑在您的配置中定义“org.hibernate.SessionFactory”类型的 bean。
我对如何使用其中之一感到困惑。
如果你打开
UserDetailsManager
的java文档,你会看到它extends UserDetailsService
并且你也有自己的实现UserDetailsServiceImpl
,这就是你得到:Field userDetailsService in com.springprojects.realtimechatapp.filter的原因。 JwtFilter 需要一个 bean,但找到了 2 个:
基于此,我建议您从
UserDetailsManager
中删除 SecurityConfig class
并保留您已经添加的 UserDetailsService
。
有关更多详细信息,以及使用
UserDetailsManager
或自己实现 UserDetailsService
有什么区别,您可以在此答案中找到 using-jdbcuserdetailsmanager-vs-own-userdetailsservice