Spring Security报UnsatisfiedDependencyException错误,请求匹配配置求助
问题:Spring Security 请求匹配器报错与JWT登录接口异常
错误信息
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'configureSecurity' defined in class path resource [com/project/CarDB/Config/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'configureSecurity' threw exception with message: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).
相关代码
SecurityConfig 类
package com.project.CarDB.Config; import com.project.CarDB.service.UserDetailServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @Configuration @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig { @Autowired UserDetailServiceImpl userDetailsService; @Bean public UserDetailsService userDetailsService (BCryptPasswordEncoder bCryptPasswordEncoder){ InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser( User.withUsername("user").password(bCryptPasswordEncoder.encode("userPass")).roles("USER").build() ); manager.createUser( User.withUsername("admin").password(bCryptPasswordEncoder.encode("adminPass")).roles("ADMIN").build() ); return manager; } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{ auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder()); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public SecurityFilterChain configureSecurity(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(HttpMethod.POST, "/login").permitAll() // Use mvcMatchers for Spring MVC patterns .anyRequest().authenticated() ); return http.build(); } }
LoginController 类
package com.project.CarDB.web; import com.project.CarDB.Domain.Account.AccountCredentials; import com.project.CarDB.service.JwtService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; 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.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController public class LoginController { @Autowired JwtService jwtService; @Autowired AuthenticationManager authenticationManager; @RequestMapping(value = "/login", method = RequestMethod.POST) public ResponseEntity<?> getToken (@RequestBody AccountCredentials accountCredentials){ UsernamePasswordAuthenticationToken credentials = new UsernamePasswordAuthenticationToken(accountCredentials.getUserName(), accountCredentials.getPassword()); Authentication authentication = authenticationManager.authenticate(credentials); // GENERATE TOKENS String jwts = jwtService.getToken(authentication.getName()); //BUILD RESPONSE WITH THE GENERATED TOKEN return ResponseEntity.ok() .header(HttpHeaders.AUTHORIZATION, "BEARER " + jwts) .header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "Authorization") .build(); } }
JwtService 类
package com.project.CarDB.service; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import java.security.Key; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.Date; @Component public class JwtService { static final long EXPIRATIONTIME = 86400000; // 1 day in ms static final String PREFIX = "Bearer"; static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); public String getToken(String username) { String token = Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATIONTIME)) .signWith(key) .compact(); return token; } public String getAuthUser(HttpServletRequest request) { String token = request.getHeader(HttpHeaders.AUTHORIZATION); if (token != null) { String user = Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token.replace(PREFIX, "")) .getBody() .getSubject(); if (user != null) return user; } return null; } }
问题分析与解决方案
错误原因
- 自定义配置类错误继承了
WebSecurityConfiguration,这是Spring内部的核心配置类,用户自定义配置无需继承它 requestMatchers未明确指定匹配器类型,Spring Security无法判断/login是Spring MVC端点还是普通路径- 缺少JWT认证过滤器,导致登录后其他接口无法通过Token验证身份
修复步骤
1. 修正SecurityConfig核心配置
移除错误继承,明确使用MvcRequestMatcher匹配Spring MVC端点,添加JWT过滤器:
package com.project.CarDB.Config; import com.project.CarDB.service.JwtService; import com.project.CarDB.service.UserDetailServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; @Configuration @EnableWebSecurity @EnableMethodSecurity public class SecurityConfig { @Autowired private JwtService jwtService; @Autowired private UserDetailServiceImpl userDetailsService; @Bean public UserDetailsService userDetailsService(BCryptPasswordEncoder bCryptPasswordEncoder) { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user") .password(bCryptPasswordEncoder.encode("userPass")) .roles("USER") .build()); manager.createUser(User.withUsername("admin") .password(bCryptPasswordEncoder.encode("adminPass")) .roles("ADMIN") .build()); return manager; } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { return authConfig.getAuthenticationManager(); } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(jwtService, userDetailsService); } @Bean public SecurityFilterChain configureSecurity(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { var loginMatcher = new org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher(introspector, "/login"); http.csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers(HttpMethod.POST, loginMatcher.getPattern()).permitAll() .anyRequest().authenticated()) .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }
2. 实现JWT认证过滤器
创建JwtAuthenticationFilter类,用于解析请求中的Token并完成身份认证:
package com.project.CarDB.Config; import com.project.CarDB.service.JwtService; import com.project.CarDB.service.UserDetailServiceImpl; 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.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; public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; private final UserDetailServiceImpl userDetailsService; public JwtAuthenticationFilter(JwtService jwtService, UserDetailServiceImpl userDetailsService) { this.jwtService = jwtService; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String username = jwtService.getAuthUser(request); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } filterChain.doFilter(request, response); } }
验证效果
- 启动应用后,POST请求
/login可正常获取JWT Token - 携带Token请求其他接口能正常通过认证
- 未携带Token的请求会被拦截,返回未授权响应
内容的提问来源于stack exchange,提问作者Mihnea A
相关产品推荐
相关产品推荐

