在使用 Spring Security 时,我遇到了一个奇怪的问题,我无法解决这个问题。
如果我使用注释声明自定义身份验证提供程序@Component
,Spring 将不再能够检测BasicAuthenticationFilter
. 我使用邮递员在标头中传递基本身份验证和自定义过滤器的密钥。这是异常/错误:
2024-02-26T01:56:59.981+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=Mvc [pattern='/public/**'], Filters=[]] (1/2)
2024-02-26T01:56:59.981+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@409df37d, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@2ab1c7a9, org.springframework.security.web.context.SecurityContextHolderFilter@22c14d10, org.springframework.security.web.header.HeaderWriterFilter@e594c46, org.springframework.security.web.csrf.CsrfFilter@27b7913, org.springframework.security.web.authentication.logout.LogoutFilter@411e567e, com.prajwal.security.config.filter.CustomFilter@7cfb4736, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5257123d, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@38276668, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3344c1d7, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6e818345, org.springframework.security.web.access.ExceptionTranslationFilter@5dd5422f, org.springframework.security.web.access.intercept.AuthorizationFilter@339b45f8]] (2/2)
2024-02-26T01:56:59.981+05:30 DEBUG 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Securing GET /private/hello
2024-02-26T01:56:59.981+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/13)
2024-02-26T01:56:59.981+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (3/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (4/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (5/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.csrf.CsrfFilter : Did not protect against CSRF since request did not match CsrfNotRequired [TRACE, HEAD, GET, OPTIONS]
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking LogoutFilter (6/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.s.w.a.logout.LogoutFilter : Did not match request to Ant [pattern='/logout', POST]
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking CustomFilter (7/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.security.web.FilterChainProxy : Invoking BasicAuthenticationFilter (8/13)
2024-02-26T01:56:59.982+05:30 TRACE 4304 --- [nio-8080-exec-4] o.s.s.w.a.www.BasicAuthenticationFilter : Found username 'prajwal' in Basic Authorization header
2024-02-26T01:56:59.982+05:30 DEBUG 4304 --- [nio-8080-exec-4] o.s.s.w.a.www.BasicAuthenticationFilter : Failed to process authentication request
org.springframework.security.authentication.ProviderNotFoundException: No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:234) ~[spring-security-core-6.1.5.jar:6.1.5]
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:174) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at com.prajwal.security.config.filter.CustomFilter.doFilterInternal(CustomFilter.java:34) ~[classes/:na]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:233) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:191) ~[spring-security-web-6.1.5.jar:6.1.5]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:352) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:268) ~[spring-web-6.0.14.jar:6.0.14]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.0.14.jar:6.0.14]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.0.14.jar:6.0.14]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
自定义身份验证.java
@AllArgsConstructor
@Getter
@Setter
public class CustomAuthentication implements Authentication {
private final boolean isAuthenticated;
private final String key;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(() -> "USER");
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
@Override
public boolean isAuthenticated() {
return this.isAuthenticated;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { }
@Override
public boolean equals(Object another) {
return false;
}
@Override
public String getName() {
return "CustomAuthentication";
}
@Override
public boolean implies(Subject subject) {
return Authentication.super.implies(subject);
}
}
CustomAuthenticationProvider.java
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Value("${our.key}")
private String key;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
CustomAuthentication ca = (CustomAuthentication) authentication;
String headerKey = ca.getKey();
if(null != headerKey && headerKey.equals(key)) {
return new CustomAuthentication(true, null);
}
return authentication;
}
@Override
public boolean supports(Class<?> authentication) {
return CustomAuthentication.class.isAssignableFrom(authentication);
}
}
自定义身份验证管理器.java
@Component
@AllArgsConstructor
public class CustomAuthenticationManager implements AuthenticationManager {
private final CustomAuthenticationProvider cap;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if(cap.supports(authentication.getClass())) {
return cap.authenticate(authentication);
}
return authentication;
}
}
自定义过滤器.java
@Component
@AllArgsConstructor
public class CustomFilter extends OncePerRequestFilter {
private final CustomAuthenticationManager cam;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CustomAuthentication ca = new CustomAuthentication(false, request.getHeader("key"));
Authentication authentication = cam.authenticate(ca);
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContext);
filterChain.doFilter(request, response);
return;
}
}
WebSecurityConfig.java
@Configuration
@AllArgsConstructor
@EnableWebSecurity
public class WebSecurityConfig {
private final CustomFilter customFilter;
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().requestMatchers("/public/**");
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorizeConfig -> {
authorizeConfig.anyRequest().authenticated();
})
.httpBasic(Customizer.withDefaults())
.addFilterBefore(customFilter, BasicAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
}
JpaUserDetailsService.java
@AllArgsConstructor
@Service
public class JpaUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) {
System.out.println("UserDetailsService is called");
User user = userRepository.findByName(username);
System.out.println("User: " + user.getUsername() + " accessed the pages");
return new SecurityUser(user);
}
}
tl;dr使用 Spring Security 时:
AuthenticationManager
除非您有非常复杂的用例,否则不要实现您自己的AuthenticationProvider
暴露为 a@Bean
或@Component
。它优先于自动配置,并防止将任何UserDetailsService
配置配置到身份验证提供程序中。下面是解释以及针对您的特定用例的建议。
针对您的用例的建议
CustomAuthenticationManager
。你不应该需要这个。CustomAuthentication
CustomFilter
如果密钥不存在,可能需要更多的逻辑来解决会发生什么。您确实想尝试验证请求,还是应该委托给其他身份验证机制?例如:AuthenticatonManager
给您的CustomFilter
. 文档中有一个示例CustomAuthenticationProvider
很好,但不要将其设为@Component
. 将其作为安全配置的简单对象:说明
自定义身份验证管理器
通常,实现自己的而不是依赖默认值并不是一个好主意。
ProviderManager
您可能会错过和的许多功能ObservationAuthenticationManager
。您想要做的是创建自己的AuthenticationProvider
并确保它被使用。此外,将 the 创建
AuthenticationManager
为@Bean
/@Component
不会执行任何操作。它必须在 HttpSecurity 配置中显式配置,如下所示:身份验证提供者为
@Bean
/@Component
在 Spring Security 中,当您使用 时,会通过HttpSecurityConfiguration为您创建
@EnableWebSecurity
一个“全局” 。AuthenticationManager
默认情况下,它将使用
UserDetailsService
您提供的任何 bean,如文档中所暗示的(强调我的):这是通过InitializeUserDetailsBeanManagerConfigurer发生的。
但是,如果您恰好提供了一个
@Bean
/@Component
of typeAuthenticationProvider
,就像在您的示例中一样,那么另一个配置就会启动,InitializeAuthenticationProviderBeanManagerConfigurer,它优先于上面提到的用户详细信息配置。请注意,如果您有零个或两个或多个类型的组件AuthenticationProvider
,则 UserDetails 配置确实会按预期工作。据我所知,这没有记录。