我遇到了一个SpringBoot应用程序的问题。我的ICS报告说此API的响应时间很长,但我的代码指标显示一切正常**,例如,发送到我的API的查询运行“业务”代码仅需50毫秒,但有效响应需要超过500毫秒!
我的JVM的内部日志显示,在每个请求上,我都有一个“UsernameNotFoundExcption”,即使凭据是正确的,API工作正常。这就是为什么我认为我的问题来自SpringSecurity层,但我不能确定原因。
我的切入点:
@Component
public class BasicAuthenticationPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("utf-8");
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 - " + authEx.getMessage());
}
@Override
public void afterPropertiesSet() throws Exception {
setRealmName("MYAPI");
super.afterPropertiesSet();
}
}
和我的适配器:
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private BasicAuthenticationPoint basicAuthenticationPoint;
@Bean
public BCryptPasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
HttpSecurity httpSec = http.csrf().disable();
httpSec = httpSec.authorizeRequests()
.antMatchers("/my-business-resources/**").hasRole("USER")
.antMatchers("/actuator/**").hasRole("ADMIN")
.and();
httpSec.httpBasic().authenticationEntryPoint(basicAuthenticationPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// Load users file
Resource confFile = resourceLoader.getResource("classpath:users.list");
// Returns users from the configuration file <username, password (BCrypted), admin (boolean)>
List<ApiUser> users = ApiUtils.getUsersFromFile(confFile);
for(ApiUser u : users){
// Add the username/password to the in-memory authentication manager
if (u.admin)
auth.inMemoryAuthentication().withUser(u.username).password(u.password).roles("USER", "ADMIN");
else
auth.inMemoryAuthentication().withUser(u.username).password(u.password).roles("USER");
}
}
}
我错过什么了吗?
**PS:**我的Sping Boot 应用程序被打包为WAR,并在Tomcat服务器中执行,以实现标准化。
编辑:
以下是完整的UsernameNotFound堆栈跟踪(ICS格式):
Exception Details
Type: UsernameNotFoundException
Exception Class: org.springframework.security.core.userdetails.UsernameNotFoundException
API: Exception
Thread Name: https-openssl-apr-9343-exec-10 <522634598>
Exception StackTrace
Method Class Line File Name
loadUserByUsername org.springframework.security.provisioning.InMemoryUserDetailsManager 146 <unknown>
retrieveUser org.springframework.security.authentication.dao.DaoAuthenticationProvider 104 <unknown>
authenticate org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider 144 <unknown>
authenticate org.springframework.security.authentication.ProviderManager 174 <unknown>
authenticate org.springframework.security.authentication.ProviderManager 199 <unknown>
doFilterInternal org.springframework.security.web.authentication.www.BasicAuthenticationFilter 180 <unknown>
doFilter org.springframework.web.filter.OncePerRequestFilter 107 <unknown>
doFilter org.springframework.security.web.FilterChainProxy$VirtualFilterChain 334 <unknown>
doFilter org.springframework.security.web.authentication.logout.LogoutFilter 116 <unknown>
doFilter org.springframework.security.web.FilterChainProxy$VirtualFilterChain 334 <unknown>
doFilterInternal org.springframework.security.web.header.HeaderWriterFilter 66 <unknown>
doFilter org.springframework.web.filter.OncePerRequestFilter 107 <unknown>
doFilter org.springframework.security.web.FilterChainProxy$VirtualFilterChain 334 <unknown>
doFilter org.springframework.security.web.context.SecurityContextPersistenceFilter 105 <unknown>
doFilter org.springframework.security.web.FilterChainProxy$VirtualFilterChain 334 <unknown>
doFilterInternal org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter 56 <unknown>
doFilter org.springframework.web.filter.OncePerRequestFilter 107 <unknown>
doFilter org.springframework.security.web.FilterChainProxy$VirtualFilterChain 334 <unknown>
doFilterInternal org.springframework.security.web.FilterChainProxy 215 <unknown>
doFilter org.springframework.security.web.FilterChainProxy 178 <unknown>
invokeDelegate org.springframework.web.filter.DelegatingFilterProxy 357 <unknown>
doFilter org.springframework.web.filter.DelegatingFilterProxy 270 <unknown>
internalDoFilter org.apache.catalina.core.ApplicationFilterChain 193 <unknown>
doFilter org.apache.catalina.core.ApplicationFilterChain 166 <unknown>
doFilter org.springframework.boot.web.servlet.support.ErrorPageFilter 130 <unknown>
access$000 org.springframework.boot.web.servlet.support.ErrorPageFilter 66 <unknown>
doFilterInternal org.springframework.boot.web.servlet.support.ErrorPageFilter$1 105 <unknown>
doFilter org.springframework.web.filter.OncePerRequestFilter 107 <unknown>
doFilter org.springframework.boot.web.servlet.support.ErrorPageFilter 123 <unknown>
internalDoFilter org.apache.catalina.core.ApplicationFilterChain 193 <unknown>
doFilter org.apache.catalina.core.ApplicationFilterChain 166 <unknown>
filterAndRecordMetrics org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter 155 <unknown>
filterAndRecordMetrics org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter 123 <unknown>
doFilterInternal org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter 108 <unknown>
doFilter org.springframework.web.filter.OncePerRequestFilter 107 <unknown>
internalDoFilter org.apache.catalina.core.ApplicationFilterChain 193 <unknown>
doFilter org.apache.catalina.core.ApplicationFilterChain 166 <unknown>
doFilterInternal org.springframework.web.filter.CharacterEncodingFilter 200 <unknown>
doFilter org.springframework.web.filter.OncePerRequestFilter 107 <unknown>
internalDoFilter org.apache.catalina.core.ApplicationFilterChain 193 <unknown>
3条答案
按热度按时间4zcjmb1e1#
好了,经过大量的调试,我发现问题了!
问题出在我用来填充内存验证器的代码中:
多次调用
auth.inMemoryAuthentication()
会在每次调用时创建一个新的InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>
(Spring代码),因此我们拥有与用户一样多的不同AuthenticationManager
(每个用户一个),因此对每个请求执行多次身份验证过程。下面是我如何修复该错误:
现在我的平均响应时间是80毫秒
tyky79it2#
我相信它需要500 ms的原因是由于密码编码。BCrypt是一种编码机制,旨在使认证需要更长的时间,以减轻暴力攻击。
通常,这个性能问题可以通过将长期有效的凭据(如密码)与短期有效的凭据(如令牌)交换来解决。OAuth是一个尝试将这种做法标准化的框架。Spring Security 5.1 introduces support for protecting APIs with OAuth 2.0。这不会立即解决您的性能问题,因为它可能需要引入更多的基础设施,但从长远来看,它可能是一个更好的安全设计选择。
作为一种替代方案,你可以使用一个更弱的编码器或可能更少的BCrypt循环,但请注意,这两者都是一个性能-安全的权衡。你可以用其他的东西来替换你的
BCryptPasswordEncoder
,例如:EDIT:很高兴您发现了这个问题!顺便说一下,我建议您清理一下您描述身份验证管理器的方式。它可以替换为
UserDetailsService
:这很好,因为它的配置占用空间更小(身份验证管理器比用户详细信息服务“更大”)。如果您只想提供用户,那么更典型的做法是覆盖
UserDetailsService
而不是AuthenticationManager
。ymdaylpp3#
您正在对资源和其他文件执行安全检查。您应该将它们排除在身份验证之外。
请看这里:Spring Security: how to exclude certain resources?