Spring Security添加验证码的两种方式小结

下面详细讲解如何给Spring Security添加验证码的两种方式:

方式1:自定义验证码过滤器

  1. 首先创建一个实现javax.servlet.Filter接口的验证码过滤器类VerifyCodeFilter,并在其中生成并输出验证码图片。示例代码:
public class VerifyCodeFilter extends OncePerRequestFilter {

    private static final String SESSION_KEY_VERIFY_CODE = "SESSION_KEY_VERIFY_CODE";

    private static final String REQUEST_PARAMETER_VERIFY_CODE = "verifyCode";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if ("/login".equals(request.getRequestURI()) && "POST".equalsIgnoreCase(request.getMethod())) {
            try {
                validate(request);
            } catch (VerifyCodeException e) {
                request.getSession().setAttribute("SPRING_SECURITY_LAST_EXCEPTION", e);
                request.getRequestDispatcher("/login?error").forward(request, response);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    private void validate(HttpServletRequest request) throws VerifyCodeException {
        String sessionVerifyCode = getSessionVerifyCode(request);
        String requestVerifyCode = getParameterVerifyCode(request);
        if (StringUtils.isBlank(requestVerifyCode) || !sessionVerifyCode.equalsIgnoreCase(requestVerifyCode)) {
            throw new VerifyCodeException("验证码不匹配");
        }
    }

    private String getSessionVerifyCode(HttpServletRequest request) {
        HttpSession session = request.getSession();
        return session.getAttribute(SESSION_KEY_VERIFY_CODE).toString();
    }

    private String getParameterVerifyCode(HttpServletRequest request) {
        return request.getParameter(REQUEST_PARAMETER_VERIFY_CODE);
    }

    private void setSessionVerifyCode(HttpServletRequest request, String verifyCode) {
        HttpSession session = request.getSession();
        session.setAttribute(SESSION_KEY_VERIFY_CODE, verifyCode);
    }

    private void outputImage(HttpServletRequest request, HttpServletResponse response, BufferedImage image) throws IOException {
        response.setContentType("image/png");
        OutputStream out = response.getOutputStream();
        ImageIO.write(image, "png", out);
        out.flush();
        out.close();
    }

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if ("/login".equals(request.getRequestURI()) && "GET".equalsIgnoreCase(request.getMethod())) {
            String verifyCode = verifyCodeGenerator();
            setSessionVerifyCode(request, verifyCode);
            BufferedImage bufferedImage = generateVerifyCodeImage(verifyCode);
            outputImage(request, response, bufferedImage);
            return;
        }
        filterChain.doFilter(request, response);
    }

    private String verifyCodeGenerator() {
        Random random = new Random();
        int bound = 999999;
        int verifyCode = random.nextInt(bound);
        return String.format("%06d", verifyCode);
    }

    private BufferedImage generateVerifyCodeImage(String verifyCode) {
        // 绘制验证码图片的逻辑
        return null;
    }

}
  1. 然后,将验证码过滤器添加到Spring Security的过滤器链中。在Spring Security的配置类上添加如下代码:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class)
            .authorizeRequests()
            ...
    }

    ...
}
  1. 最后,在登录页面的表单中添加验证码输入框,和一个获取验证码图片的链接。示例代码:
<form th:action="@{/login}" method="post">
    <div>
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required autofocus>
    </div>
    <div>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
    </div>
    <div>
        <label for="verify-code">Verify Code:</label>
        <input type="text" id="verify-code" name="verifyCode" required>
    </div>
    <div>
        <a th:href="@{/verifyCode}" target="_blank">Get Verify Code</a>
    </div>
    <div>
        <button type="submit">Log in</button>
    </div>
</form>

方式2:使用Spring Security官方提供的验证码验证功能

  1. 首先,在Spring Security的配置类中添加如下配置类,以开启验证码功能:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                ...
                .and()
                .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/authenticate")
                .failureUrl("/login?error")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                .permitAll()
                .and()
                .csrf()
                .disable()
                .apply(new SmsCodeAuthenticationSecurityConfig())
                .apply(new VerifyCodeSecurityConfig()) // 添加验证码功能
                .apply(new SocialAuthenticationConfig())
                .apply(new RememberMeConfig());
    }

}
  1. 接下来,自定义一个实现org.springframework.security.web.authentication.AuthenticationSuccessHandler接口的类MyAuthenticationSuccessHandler,用于在登录成功之后,将验证码删除。示例代码:
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private final VerificationCodeRepository verificationCodeRepository;

    public MyAuthenticationSuccessHandler(VerificationCodeRepository verificationCodeRepository) {
        this.verificationCodeRepository = verificationCodeRepository;
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String mobile = request.getParameter("mobile");
        verificationCodeRepository.remove(mobile, "LOGIN");
        response.sendRedirect("/");
    }
}
  1. 最后,在登录页面的表单中添加验证码输入框、隐藏域,并在Spring Security的配置类中指定如下配置:
<form th:action="@{/authenticate}" method="post">
    ...
    <div>
        <label for="verify-code">Verify Code:</label>
        <input type="text" id="verify-code" name="verifyCode" required>
        <input type="hidden" name="uuid" th:value="${uuid}">
    </div>
    ...
</form>
@Configuration
public class VerifyCodeSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // 先将验证码过滤器添加到spring security过滤器链
        VerifyCodeFilter verifyCodeFilter = new VerifyCodeFilter();
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);

        // 配置验证码认证通过失败的处理器
        VerifyCodeFilterResultHandler verifyCodeFilterResultHandler = new VerifyCodeFilterResultHandler(verifyCodeFilter);
        http.exceptionHandling()
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
                .accessDeniedHandler(new AccessDeniedHandlerImpl())
                .and()
                .formLogin()
                .successHandler(new MyAuthenticationSuccessHandler(verificationCodeRepository())) // 设置登录成功处理器,在登录成功的时候删除验证码
                .failureHandler(verifyCodeFilterResultHandler) // 登录失败也返回头部信息,其中可能会包含uuid参数
                .and()
                .addFilterBefore(new VerifyCodePersistenceFilter(verificationCodeRepository()), UsernamePasswordAuthenticationFilter.class) // 持久化uuid和验证码的过滤器,搭配在 LoginWithUuidAuthenticationProvider 一起对通过验证码登陆的用户进行认证
                .authenticationProvider(new LoginWithUuidAuthenticationProvider(verificationCodeRepository(), userDetailsService())) // 配置自定义 AuthenticationProvider
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/")
                .permitAll()
                .and()
                .csrf()
                .disable();
    }

}

以上就是向Spring Security中添加验证码的两种方式,你可以根据具体需求选择适合自己的方式。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security添加验证码的两种方式小结 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Spring框架实现依赖注入的原理

    Spring框架通过反射机制和XML配置文件实现依赖注入。本文将从以下几个方面详细解释Spring框架实现依赖注入的原理: 什么是依赖注入? Spring框架中的依赖注入 依赖注入的原理和步骤 示例说明 总结 什么是依赖注入? 依赖注入(Dependency Injection,DI)是一种软件设计模式,指的是在对象之间的关系中,通过构造函数、setter方…

    Java 2023年5月19日
    00
  • Java虚拟机GC日志分析

    下面是关于Java虚拟机GC日志分析的完整攻略: 什么是Java虚拟机GC日志 Java虚拟机的内存管理采用了分代垃圾收集的方式,GC日志是Java虚拟机在垃圾回收时所产生的日志,它里面包含了垃圾回收的很多相关信息,如垃圾回收的原因、结果、执行时间以及内存状态等。 获取GC日志 在使用Java虚拟机时,默认情况下并不会产生GC日志,需要手动开启才可以,一般有…

    Java 2023年5月26日
    00
  • 使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务的方法(推荐)

    使用Spring Cloud Feign作为HTTP客户端调用远程HTTP服务的方法是目前被广泛使用的一种方式,它能够简化我们对HTTP服务的调用过程,提高我们的开发效率。下面就为大家详细讲解一下这个攻略。 什么是Spring Cloud Feign Spring Cloud Feign是基于Netflix Feign实现的一种服务调用方式。它可以让我们以接…

    Java 2023年5月20日
    00
  • SharePoint 2007图文开发教程(5) 体验Event Handler

    SharePoint 2007图文开发教程(5) 体验Event Handler 什么是Event Handler? Event Handler是指一种事件处理程序,常用于在数据更新、插入或删除等操作的过程中执行特定的操作,例如发送邮件通知、记录日志等。在SharePoint中,Event Handler被广泛应用于对列表、文档库等对象的事件进行处理。 如何…

    Java 2023年5月31日
    00
  • 详解在spring中使用JdbcTemplate操作数据库的几种方式

    下面是“详解在spring中使用JdbcTemplate操作数据库的几种方式”的完整攻略。 1. 前言 在Spring开发中,使用JdbcTemplate操作数据库是常见的一种方式,可以方便地完成对数据库的CRUD操作。JdbcTemplate是Spring对JDBC API的封装,使得对数据库的操作更加简单、安全和易于维护。本文将对在Spring中使用Jd…

    Java 2023年5月20日
    00
  • Java的Hibernate框架中一对多的单向和双向关联映射

    Java的Hibernate框架中,一对多关联映射通常用于表示两个表之间的一对多关系。在这种关系中,一个“一”的实体可以关联多个“多”的实体。Hibernate框架支持单向和双向的一对多关联映射。 单向一对多关联映射 在Hibernate框架中,单向一对多关联映射通常是通过在多的一方中定义对一方的外键来实现的。以下是一个示例: 定义“一”的实体 @Entit…

    Java 2023年5月31日
    00
  • 一篇文章带你入门Java基本概念

    一篇文章带你入门Java基本概念 Java是一个广泛应用的高级编程语言,它是一种面向对象的语言,体现了一些在C++中经过多年开发和实践所获得的经验,避免了其它更早的面向对象的语言的一些不足,是一个功能强大且通用性很高的编程语言。 基本概念 Java具有丰富的基本概念,其中一些需要初学者掌握: 类 Java中的类是一个蓝图或者模板,它定义了对象包含的属性和方法…

    Java 2023年5月23日
    00
  • Mybatis如何配置连接池

    MyBatis可以通过配置连接池来提高数据库操作的性能,下面是配置连接池的详细攻略: 步骤1:添加连接池依赖 在pom.xml文件中添加对连接池的依赖,例如: <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部