当前位置: 首页 > 知识库问答 >
问题:

带Spring Security的Spring Boot-通过SMS/PIN/TOTP进行双因素身份验证

严繁
2023-03-14

我正在开发一个Spring Boot 2.5.0 web应用程序,使用Thymeleaf使用Spring Security表单登录。我正在寻找如何使用Spring Security表单登录实现双因素身份验证(2FA)的想法。

要求是,当用户使用其用户名和密码通过登录时。在登录表单中,如果用户名和密码验证成功,则应向用户的注册手机号码发送一个SMS代码,并用另一个页面询问用户输入SMS代码。如果用户正确获取SMS代码,则应将其转发到安全应用程序页面。

在登录表单上,除了用户名和密码外,用户还需要输入验证码图像中的文本,该图像通过扩展UsernamePasswordAuthenticationFilterSimpleAuthenticationFilter进行验证。

这是当前的安全配置

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsServiceImpl userDetailsService;
    
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)    
            .authorizeRequests()
                .antMatchers(
                        "/favicon.ico",
                        "/webjars/**",
                        "/images/**",
                        "/css/**",
                        "/js/**",
                        "/login/**",
                        "/captcha/**",
                        "/public/**",
                        "/user/**").permitAll()
                .anyRequest().authenticated()
            .and().formLogin()
                .loginPage("/login")
                    .permitAll()
                .defaultSuccessUrl("/", true)
            .and().logout()
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .deleteCookies("JSESSONID")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                .logoutSuccessUrl("/login?logout")
                    .permitAll()
            .and().headers().frameOptions().sameOrigin()
            .and().sessionManagement()
                .maximumSessions(5)
                .sessionRegistry(sessionRegistry())
                .expiredUrl("/login?error=5");
    }

    public SimpleAuthenticationFilter authenticationFilter() throws Exception {
        SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setAuthenticationFailureHandler(authenticationFailureHandler());
        return filter;
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider auth = new DaoAuthenticationProvider();
        auth.setUserDetailsService(userDetailsService);
        auth.setPasswordEncoder(passwordEncoder());
        return auth;
    }

    /** TO-GET-SESSIONS-STORED-ON-SERVER */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

}

这就是上面提到的SimpleAuthenticationFilter

public class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    public static final String SPRING_SECURITY_FORM_CAPTCHA_KEY = "captcha";
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        HttpSession session = request.getSession(true);
        
        String captchaFromSession = null;
        if (session.getAttribute("captcha") != null) {
            captchaFromSession = session.getAttribute("captcha").toString();
        } else {
            throw new CredentialsExpiredException("INVALID SESSION");
        }
        
        String captchaFromRequest = obtainCaptcha(request);
        if (captchaFromRequest == null) {
            throw new AuthenticationCredentialsNotFoundException("INVALID CAPTCHA");
        }
        
        if (!captchaFromRequest.equals(captchaFromSession)) {
            throw new AuthenticationCredentialsNotFoundException("INVALID CAPTCHA");
        }
        
        UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request);
        setDetails(request, authRequest);

        return this.getAuthenticationManager().authenticate(authRequest);
    }

    private UsernamePasswordAuthenticationToken getAuthRequest(HttpServletRequest request) {
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        
        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }

        return new UsernamePasswordAuthenticationToken(username, password);
    }
    
    private String obtainCaptcha(HttpServletRequest request) {
        return request.getParameter(SPRING_SECURITY_FORM_CAPTCHA_KEY);
    }

}

有什么办法吗?提前谢谢。

共有1个答案

呼延珂
2023-03-14

Spring Security提供了一个mfa示例,可以帮助您入门。它使用带有OTP的Google Authenticator,但你可以插入发送/验证短信短代码。

您还可以考虑将验证码验证与(开箱即用)身份验证过滤器分开。如果它们是同一个过滤器链中的独立过滤器,则代码更少,效果也相同。

 类似资料:
  • 本文向大家介绍什么是双因素身份验证?相关面试题,主要包含被问及什么是双因素身份验证?时的应答技巧和注意事项,需要的朋友参考一下 双因素身份验证是在帐户登录过程中启用第二级身份验证。 因此,如果用户只需要输入用户名和密码,那么就被认为是单因素身份验证。

  • 问题: 我们有一个spring的基于MVC的RESTful API,它包含敏感信息。API应该是安全的,但是不希望在每个请求中发送用户的凭据(User/Pass组合)。根据REST指南(和内部业务需求),服务器必须保持无状态。API将由另一台服务器以混搭方式使用。 要求: > 客户端请求使用凭据(不受保护的URL);服务器返回一个安全令牌,该令牌包含足够的信息,供服务器验证未来的请求并保持无状态。

  • 我正在寻找如何使用spring security Oauth2实现双因素身份验证(2FA)的方法。要求是用户只需要对具有敏感信息的特定应用程序进行双因素身份验证。这些webapp都有自己的客户端ID。 我脑海中浮现的一个想法是“误用”范围审批页面,迫使用户输入2FA代码/PIN(或其他)。 示例流如下所示: null null

  • 我正在尝试使用一个Gem devise-two-factor在Devise上实现双因素身份验证。我想验证是在2个步骤,在第一,我将要求用户名和密码。如果用户通过了这一步,那么他将被重定向到OTP的下一页,如果2FA被激活,否则会话将由Devise验证。 如果用户选择了2fa,那么我希望使用Devise来执行所有的身份验证,而不希望在User.validate_and_consume_otp(CUR

  • 在这里,我的场景有点类似于Gmail的双因素认证。当一个用户成功登录(SMS代码发送给用户),然后用另一个页面挑战他输入SMS代码。如果用户正确地获得了SMS代码,他将被显示为安全页面(如Gmail收件箱)。

  • 问题内容: 在(带有)中,我尝试使用以下语句通过基本身份验证访问我的网页: 但是Google Chrome浏览器在控制台中向我发出以下警告: [弃用]其URL包含嵌入式凭据(例如https://user:pass@host/)的子资源请求被阻止。有关更多详细信息,请参见https://www.chromestatus.com/feature/5669008342777856。 在标记的链接中提到该