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

yizhihongxing

下面详细讲解如何给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日

相关文章

  • java异常处理的简单练习

    Java异常处理的简单练习攻略 在Java编程中,异常处理是一个至关重要的话题。当程序执行时出现错误时,如果我们不进行处理,程序就会崩溃,并输出一些不必要的错误信息。因此,我们需要使用Java异常处理机制来捕获这些异常,并采取适当的行动来处理它们。 简单的Java异常处理练习题 现在,我们来考虑一个简单的Java异常处理练习题。假设我们要编写一个程序,从用户…

    Java 2023年5月27日
    00
  • 一名优秀的程序员是这样炼成的

    一名优秀的程序员是这样炼成的 成为一名优秀的程序员,并不容易,需要进行长期的努力和学习。以下是成为一名优秀的程序员的攻略: 1. 基础扎实 基础扎实是成为一名优秀程序员的必要条件,包括但不限于以下方面: 编程语言基础:熟练掌握至少一门主流编程语言,包括其语法、数据类型、变量、运算符、流程控制等基础知识。 数据结构和算法:熟悉常见的数据结构和算法,掌握它们的时…

    Java 2023年5月26日
    00
  • JavaScript中的数组特性介绍

    关于JavaScript中的数组特性,我们可以从以下几个方面进行介绍: 数组的创建和初始化 JavaScript中的数组可以使用字面量和构造函数两种方式进行创建和初始化。其中,字面量方式如下: const array = [‘a’, ‘b’, ‘c’]; 构造函数方式如下: const array = new Array(‘a’, ‘b’, ‘c’); 需要…

    Java 2023年5月26日
    00
  • dl、dt、dd 标记来改造163邮箱的广告条

    如果想要改造网页上的广告条,可以使用HTML中的dl、dt、dd标记来达到目的。下面是详细的攻略: 1.使用dl、dt、dd标记 dl标记用于定义一个描述列表(description list),dt标记用于定义列表项中的项目名称(即定义术语或名称),dd标记用于定义项目的描述。可以使用这些标记分别定义广告条的标题、说明和一个链接。 2.示例一 下面是一个针…

    Java 2023年6月15日
    00
  • Spring菜鸟教你看源码冲面试

    Spring菜鸟教你看源码冲面试的完整攻略 1.1 学会使用IDEA导入Spring源码 – 首先下载Spring源码,可以在官网或者GitHub上找到,解压缩后可以得到整个项目的源代码。 – 打开IDEA,选择“File” -> “Open” -> “下载好的Spring源码” -> “OK”。 – 等待IDEA加载完整个项目,可以在左侧…

    Java 2023年5月19日
    00
  • 详解springboot的多种配置方式

    详解Spring Boot的多种配置方式 在Spring Boot中,我们可以使用多种方式进行配置。通过了解这些配置方式,可以更好地理解Spring Boot的运作机制,并根据需求选用最适合的配置方式。 1. 属性文件配置 Spring Boot支持使用配置文件的方式进行配置,而属性文件就是其中一种。属性文件的格式为.properties或者.yml,其中.…

    Java 2023年5月15日
    00
  • java实现登录之后抓取数据

    下面是Java实现登录之后抓取数据的完整攻略: 一、概述 当我们需要抓取某个网站上的数据时,通常需要先登录该网站,这样才能访问该网站的受保护资源。本篇攻略将会讲解如何使用Java实现模拟登录,并抓取登录后的页面数据。 二、准备 为了实现模拟登录,我们需要用到Java的HttpClient和Jsoup库。HttpClient用于发送HTTP请求,而Jsoup用…

    Java 2023年5月19日
    00
  • JavaWEB中Servlet的生命周期详解

    JavaWEB中Servlet的生命周期详解 Servlet是JavaEE中常用的组件之一,它的生命周期与Web应用程序的生命周期吻合。在这篇文章中,我们将深入了解Servlet的生命周期以及其中的每个部分。 Servlet的生命周期 Servlet的生命周期可以分为三个部分:初始化、服务和销毁。 初始化 在Servlet第一次被创建时,web容器会调用其i…

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