Should spring security filters call authentication providers directly?
我正在尝试 * 模式2*,从上面的帖子,基本上我的自定义过滤器拦截一个请求,采取所有的凭据,并把它放在与authenticated=false
的SecurityContext。然后我的 CustomAuthenticationProvider 应该拿起这些凭据,并验证它。在我的项目中,我的过滤器拦截请求,并做它的工作,但我的auth提供程序没有得到调用。
- 用户名密码验证过滤器.java*
package com.springsecurity.learning.config;
import java.io.IOException;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springsecurity.learning.dto.CredentialsDto;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class UsernamePasswordAuthFilter extends OncePerRequestFilter {
private final String END_POINT = "/api/login";
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// TODO Auto-generated method stub
if(END_POINT.equals(request.getRequestURI())
&& HttpMethod.POST.matches(request.getMethod())) {
CredentialsDto credentialsDto = MAPPER.readValue(request.getInputStream(), CredentialsDto.class);
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(credentialsDto.getUsername(),
credentialsDto.getPassword())
);
}
filterChain.doFilter(request, response);
}
}
- 自定义身份验证提供程序.java*
package com.springsecurity.learning.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import com.springsecurity.learning.dto.CredentialsDto;
import com.springsecurity.learning.dto.UserDto;
import com.springsecurity.learning.services.AuthenticationService;
import lombok.AllArgsConstructor;
@Component
@AllArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final AuthenticationService authenticationService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// TODO Auto-generated method stub
UserDto userDto = null;
if(authentication instanceof UsernamePasswordAuthenticationToken) {
userDto = authenticationService.authenticate(
new CredentialsDto((String)authentication.getPrincipal(),
(String)authentication.getCredentials()));
}
if(userDto==null)return null;
return new UsernamePasswordAuthenticationToken(userDto.getUsername(),
null,
List.of(new SimpleGrantedAuthority(userDto.getRole())
));
}
@Override
public boolean supports(Class<?> authentication) {
// TODO Auto-generated method stub(UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
return true;
}
}
- 安全配置.java*
package com.springsecurity.learning.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(CustomAuthenticationProvider customAuthenticationProvider) {
return new ProviderManager(customAuthenticationProvider);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity, CustomAuthenticationProvider customAuthenticationProvider) throws Exception {
httpSecurity
.authenticationProvider(customAuthenticationProvider)
.addFilterAfter(new UsernamePasswordAuthFilter(), LogoutFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/home/public")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic().disable();
return httpSecurity.build();
}
}
这是我的安全过滤链
1条答案
按热度按时间ar7v8xwq1#
您的过滤器是罪魁祸首,因为您没有调用
AuthenticationManager
上的authenticate
方法(在您的情况下,它将调用提供程序)。如果你查看一些默认的Spring Security过滤器,比如
UsernamePasswordAuthenticationFilter
和BasicAuthenticationFilter
,你会发现它们确实调用了那个方法,并在SecurityContext
中设置了结果Authentication
,而你只是在不尝试任何身份验证的情况下设置它。您的过滤器需要调用它,并具有更多的功能,我还建议扩展Spring Security提供的
AbstractAuthenticationProcessingFilter
并实现attemptAuthenticationMethod
。这将读取您的对象、调用身份验证并与Spring Security的其他部分集成(如触发事件、会话管理等)。
通过让
CredentialsDto
实现Authentication
接口,可以使它更简洁一些。然后CustomAuthenticationProvider
可以检查它是否支持它,并只强制转换它。这样就节省了创建中间对象的时间。CustomAuthenticationProvider
现在非常简单。类似的东西。
对于配置,您的
UsernamePasswordAuthFilter
现在需要是一个正确的bean才能注入AuthenticationManager
,如果您保持与现在相同的策略,您将遇到NullPointerException
,因为没有注入依赖项。