我正在为自己编写一个小型应用程序,并希望在 Spring Boot 中实现 2 因素身份验证。为此,请遵循本文的提示:https ://www.baeldung.com/spring-security-two-factor-authentication-with-soft-token
面临以下问题: 1)我根据这篇文章编写的代码不起作用。Spring Security 完全忽略 is2FaEnabled == true 并在任何情况下授权用户,即使尚未输入代码。
从这个线程中的日志来看,.authenticationDetailsSource (authenticationDetailsSource) 甚至没有进行验证。
2)如何实现:在授权过程中,首先检查是否启用了2FA,如果是,然后将用户引导到另一个URL或打开一个带有输入的模块,正确输入代码后,授权它?
这是我的源代码:
CustomWebAuthenticationDetailsSource.java
@Component
public class CustomWebAuthenticationDetailsSource implements
AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> {
@Override
public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
return new CustomWebAuthenticationDetails(context);
}
}
CustomWebAuthenticationDetails.java
public class CustomWebAuthenticationDetails extends WebAuthenticationDetails {
@Getter
private String verificationCode;
public CustomWebAuthenticationDetails(HttpServletRequest request) {
super(request);
verificationCode = request.getParameter("code");
}
}
CustomAuthenticationProvider.java
public class CustomAuthenticationProvider extends DaoAuthenticationProvider {
@Autowired
private UserServiceImpl userServiceImpl;
private Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
@Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
User user = userServiceImpl.findUserByEmail(auth.getName());
String verificationCode
= ((CustomWebAuthenticationDetails) auth.getDetails())
.getVerificationCode();
if ((user == null)) {
throw new BadCredentialsException("Invalid username or password");
}
if (user.getIs2FaEnabled()) {
Totp totp = new Totp(user.getTwoFaSecret());
if (!isValidLong(verificationCode) || !totp.verify(verificationCode)) {
throw new BadCredentialsException("Invalid verfication code");
}
}
Authentication result = super.authenticate(auth);
return new UsernamePasswordAuthenticationToken(
user, result.getCredentials(), result.getAuthorities());
}
private boolean isValidLong(String code) {
try {
Long.parseLong(code);
} catch (NumberFormatException e) {
return false;
}
return true;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
WebSecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
// n.b https://stackoverflow.com/questions/1018797/can-you-use-autowired-with-static-fields
private static UserDetailsServiceImpl userDetailsService;
@Autowired
private UserDetailsServiceImpl userDetailsServiceImpl;
@PostConstruct
private void init() {
userDetailsService = this.userDetailsServiceImpl;
}
@Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public DaoAuthenticationProvider authProvider() {
CustomAuthenticationProvider authProvider = new CustomAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
// Backend configuration
@Configuration
@Order(1)
public static class BackendConfigurationAdapter extends WebSecurityConfigurerAdapter {
/*@Autowired
private UserDetailsServiceImpl userDetailsService;*/
@Autowired
private CustomWebAuthenticationDetailsSource authenticationDetailsSource;
public BackendConfigurationAdapter() {
super();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/admin/**")
.antMatcher("/admin/**/**")
.authorizeRequests()
.anyRequest()
.hasAuthority("ADMIN_PRIVILEGE")
.and()
.formLogin()
.authenticationDetailsSource(authenticationDetailsSource)
.loginPage("/admin/login")
.loginProcessingUrl("/admin/login")
.usernameParameter("email")
.passwordParameter("password")
.defaultSuccessUrl("/admin/dashboard")
.failureUrl("/admin/login?authError")
.permitAll()
.and()
.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(86400)
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/admin/logout"))
.logoutSuccessUrl("/admin/login")
.deleteCookies("JSESSIONID")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.csrf()
.ignoringAntMatchers("/admin/**");
}
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(
"/backend/css/**",
"/backend/js/**",
"/backend/fonts/**",
"/backend/images/**",
"/backend/init/**"
);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
// Frontend Configuration
@Configuration
@Order(2)
public static class FrontendConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private CustomWebAuthenticationDetailsSource authenticationDetailsSource;
public FrontendConfigurationAdapter() {
super();
}
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().mvcMatchers("/robots.txt").permitAll()
.antMatchers(
"/", "/auth", "/signup", "/restore", "/activation/**",
"/admin/login", "/admin_restore",
"/attachments/get/**",
"/sendMessage",
"/error",
"/page/**",
"/categories", "/categories/**",
"/terms/**", "/posts", "/posts/**"
).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.authenticationDetailsSource(authenticationDetailsSource)
.loginPage("/auth")
.loginProcessingUrl("/auth")
.usernameParameter("email")
.passwordParameter("password")
.defaultSuccessUrl("/")
.failureUrl("/auth?authError")
.permitAll()
.and()
.rememberMe()
.rememberMeParameter("remember-me")
.tokenValiditySeconds(86400)
.and()
.oauth2Login().defaultSuccessUrl("/")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.csrf()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and()
.headers().frameOptions().sameOrigin();
}
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(
"/frontend/css/**",
"/frontend/js/**",
"/frontend/fonts/**",
"/frontend/images/**",
"/frontend/lib/**",
"/frontend/vendor/**"
);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
}
提前致谢!