Spring boot WebSecurityConfigurerAdapter basic authentication 用户密码验证问题
Spring boot WebSecurityConfigurerAdapter basic authentication user password validation issue
我正在使用 Spring Boot 编写一个简单的 REST API,我想启用基本身份验证。因此,我使用了 WebSecurityConfigurerAdapter,如下所示。为了简单起见,我只想检查密码(pwd123)并允许任何用户登录。请参考下面的代码。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null || authentication.getCredentials() == null) {
throw new BadCredentialsException("Bad credentials");
}
if (authentication.getCredentials().equals("pwd123")) {
return new UsernamePasswordAuthenticationToken(authentication.getName(),
authentication.getCredentials().toString(),
Collections.emptyList());
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
});
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
假设 user_A 已使用有效密码访问 REST API,即 pwd123,然后使用错误密码执行发送 API 调用。但是,允许用户访问 API,这就是问题所在。
当我进行调试时,我意识到 authenticationIsRequired 函数在 BasicAuthenticationFilter class 中 Spring安全性,在这种情况下 returns false。请参考该代码。
private boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user
// isn't authenticated (see SEC-53)
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if (existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
// Limit username comparison to providers which use usernames (ie
// UsernamePasswordAuthenticationToken) (see SEC-348)
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
return true;
}
// Handle unusual condition where an AnonymousAuthenticationToken is already
// present. This shouldn't happen very often, as BasicProcessingFitler is meant to
// be earlier in the filter chain than AnonymousAuthenticationFilter.
// Nevertheless, presence of both an AnonymousAuthenticationToken together with a
// BASIC authentication request header should indicate reauthentication using the
// BASIC protocol is desirable. This behaviour is also consistent with that
// provided by form and digest, both of which force re-authentication if the
// respective header is detected (and in doing so replace/ any existing
// AnonymousAuthenticationToken). See SEC-610.
return (existingAuth instanceof AnonymousAuthenticationToken);
}
请让我知道我的实现中缺少什么
如评论中所述,您可以尝试提供自定义 UserDetailsService
而不是提供自定义 AuthenticationProvider
。这是完整的配置:
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorizeRequests) -> authorizeRequests
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
return (username) -> new User(username, "{noop}pwd123", AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}
当您发展到通过 third-party 服务查找用户时,您可以在自定义 UserDetailsService
中添加代码来执行此操作(lambda 函数或实际的 class实现接口)并继续返回 org.springframework.security.core.userdetails.User
.
注意: 我实际上不建议在生产中使用 plain-text 密码。您可以将 {noop}pwd123
替换为 {bcrypt}<bcrypt encoded password here>
.
正如评论和答案中所建议的那样,即使您使用 InMemoryUserDetailsManager
问题也没有得到解决,这意味着,一旦用户使用正确的用户名和密码进行身份验证,他的密码就不会在随后的 REST API 调用中验证,即可以使用任何密码。这是因为 BasicAuthenticationFilter
class 中的功能会跳过拥有有效 JSESSION cookie 的用户。
要解决此问题,我们应该配置 http 以通过以下方式创建 state-less 会话
http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
在configure
函数中WebSecurityConfigurerAdapter
请参考
我正在使用 Spring Boot 编写一个简单的 REST API,我想启用基本身份验证。因此,我使用了 WebSecurityConfigurerAdapter,如下所示。为了简单起见,我只想检查密码(pwd123)并允许任何用户登录。请参考下面的代码。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null || authentication.getCredentials() == null) {
throw new BadCredentialsException("Bad credentials");
}
if (authentication.getCredentials().equals("pwd123")) {
return new UsernamePasswordAuthenticationToken(authentication.getName(),
authentication.getCredentials().toString(),
Collections.emptyList());
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
});
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and().httpBasic();
}
}
假设 user_A 已使用有效密码访问 REST API,即 pwd123,然后使用错误密码执行发送 API 调用。但是,允许用户访问 API,这就是问题所在。
当我进行调试时,我意识到 authenticationIsRequired 函数在 BasicAuthenticationFilter class 中 Spring安全性,在这种情况下 returns false。请参考该代码。
private boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user
// isn't authenticated (see SEC-53)
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if (existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
// Limit username comparison to providers which use usernames (ie
// UsernamePasswordAuthenticationToken) (see SEC-348)
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
return true;
}
// Handle unusual condition where an AnonymousAuthenticationToken is already
// present. This shouldn't happen very often, as BasicProcessingFitler is meant to
// be earlier in the filter chain than AnonymousAuthenticationFilter.
// Nevertheless, presence of both an AnonymousAuthenticationToken together with a
// BASIC authentication request header should indicate reauthentication using the
// BASIC protocol is desirable. This behaviour is also consistent with that
// provided by form and digest, both of which force re-authentication if the
// respective header is detected (and in doing so replace/ any existing
// AnonymousAuthenticationToken). See SEC-610.
return (existingAuth instanceof AnonymousAuthenticationToken);
}
请让我知道我的实现中缺少什么
如评论中所述,您可以尝试提供自定义 UserDetailsService
而不是提供自定义 AuthenticationProvider
。这是完整的配置:
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorizeRequests) -> authorizeRequests
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
return (username) -> new User(username, "{noop}pwd123", AuthorityUtils.createAuthorityList("ROLE_USER"));
}
}
当您发展到通过 third-party 服务查找用户时,您可以在自定义 UserDetailsService
中添加代码来执行此操作(lambda 函数或实际的 class实现接口)并继续返回 org.springframework.security.core.userdetails.User
.
注意: 我实际上不建议在生产中使用 plain-text 密码。您可以将 {noop}pwd123
替换为 {bcrypt}<bcrypt encoded password here>
.
正如评论和答案中所建议的那样,即使您使用 InMemoryUserDetailsManager
问题也没有得到解决,这意味着,一旦用户使用正确的用户名和密码进行身份验证,他的密码就不会在随后的 REST API 调用中验证,即可以使用任何密码。这是因为 BasicAuthenticationFilter
class 中的功能会跳过拥有有效 JSESSION cookie 的用户。
要解决此问题,我们应该配置 http 以通过以下方式创建 state-less 会话
http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
在configure
函数中WebSecurityConfigurerAdapter
请参考