Spring Security 图片验证码功能的实例代码

下面我会给出关于“Spring Security 图片验证码功能的实例代码”的详细攻略。首先,我们需要思考一下问题,什么是图片验证码,为什么要使用它。

图片验证码就是在需要用户输入验证码时,生成一张随机的图片,用户需要识别图片中的验证码才能通过验证。由于图片验证码中的验证码是随机生成的,所以可以有效地避免机器人或爬虫等自动化程序的攻击。

在Spring Security中,我们可以使用项目中已经集成的验证码组件,或是自定义生成验证码的方法。下面给出两个示例,帮助大家更好地理解Spring Security中如何实现验证码功能。

示例一:使用项目中集成的验证码组件

首先,我们编写一个自定义的 AuthenticationProvider,用来验证用户输入的用户名和密码是否正确。

public class MyAuthenticationProvider implements AuthenticationProvider {

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 省略从数据库中读取用户信息的代码

        // 如果用户不存在,或者密码不正确,抛出异常
        if(user == null || !password.equals(user.getPassword())) {
            throw new BadCredentialsException("用户名或密码不正确");
        }

        // 验证通过,返回一个认证对象
        return new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

接着,在Spring Security的配置文件中,我们需要添加验证码过滤器。这个过滤器会在用户登录时生成一个验证码,并将验证码放到Session中。用户输入的验证码会与Session中的验证码进行对比,从而验证验证码的正确性。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationProvider authenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .addFilterBefore(new VerifyCodeFilter(), UsernamePasswordAuthenticationFilter.class) // 添加验证码过滤器
            .authenticationProvider(authenticationProvider);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }
}

最后,我们需要编写验证码生成和验证码校验的代码。这里我们使用Spring Security项目中集成的验证码组件,只需要在配置文件中添加下面的代码即可。需要注意的是,这里的 verifyCodeParameterverifyCodeSessionAttribute 配置为了与默认值保持一致。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 省略其他配置代码

    @Bean
    public DefaultKaptcha captchaProducer() {
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.image.width", "100");
        properties.setProperty("kaptcha.image.height", "40");
        Config config = new Config(properties);
        DefaultKaptcha captchaProducer = new DefaultKaptcha();
        captchaProducer.setConfig(config);
        return captchaProducer;
    }

    @Autowired
    private DefaultKaptcha captchaProducer;

    @Bean
    public VerifyCodeFilter verifyCodeFilter() {
        return new VerifyCodeFilter();
    }

    private class VerifyCodeFilter extends OncePerRequestFilter {

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            if(!"/login".equals(request.getRequestURI())) {
                filterChain.doFilter(request, response);
                return;
            }

            // 生成验证码
            String text = captchaProducer.createText();
            BufferedImage image = captchaProducer.createImage(text);

            // 将验证码放到Session中
            request.getSession().setAttribute("verifyCode", text);
            response.setHeader("Cache-Control", "no-store");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setContentType("image/jpeg");

            // 输出验证码图片
            try(ServletOutputStream out = response.getOutputStream()) {
                ImageIO.write(image, "jpeg", out);
                out.flush();
            }
        }
    }

    @Bean
    public VerifyCodeService verifyCodeService() {
        return new VerifyCodeService();
    }

    private class VerifyCodeService implements InitializingBean {

        private AbstractHttpSessionApplicationInitializer httpSessionApplicationInitializer;

        public VerifyCodeService() {}

        public boolean validate(HttpServletRequest request, HttpServletResponse response) {
            String verifyCode = (String)request.getSession().getAttribute("verifyCode");
            String inputVerifyCode = request.getParameter("verifyCode");
            request.getSession().removeAttribute("verifyCode");
            return verifyCode != null && verifyCode.equalsIgnoreCase(inputVerifyCode);
        }

        @Override
        public void afterPropertiesSet() throws Exception {  
            // 获取httpSessionApplicationInitializer实例
            ApplicationContext context = ApplicationContextProvider.getApplicationContext(); 
            httpSessionApplicationInitializer = context.getBean(AbstractHttpSessionApplicationInitializer.class); }
        }
    }

示例二:自定义生成验证码

如果我们不想使用Spring Security项目中集成的验证码组件,我们也可以编写自定义的验证码生成和验证码校验的代码。

首先,我们需要定义一个 CaptchaGenerator 类,用来生成验证码。这个类的实现方式可以很简单,比如下面的代码:

public class CaptchaGenerator {

    public static final String DEFAULT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    private static final Random rand = new Random();

    public static String generate(int length) {
        char[] chars = DEFAULT_CHARACTERS.toCharArray();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < length; i++) {
            char c = chars[rand.nextInt(chars.length)];
            sb.append(c);
        }
        return sb.toString();
    }
}

接着,我们需要编写验证码生成和验证码校验的过滤器。这里的代码与示例一大致相同,只是验证码的生成和校验方式有所不同。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // 省略其他配置代码

    @Autowired
    private CaptchaGenerator captchaGenerator;

    @Bean
    public VerifyCodeFilter verifyCodeFilter() {
        return new VerifyCodeFilter();
    }

    private class VerifyCodeFilter extends OncePerRequestFilter {

        public static final String SESSION_ATTRIBUTE_NAME = "captcha";

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            if(!"/login".equals(request.getRequestURI())) {
                filterChain.doFilter(request, response);
                return;
            }

            // 生成验证码
            String captcha = captchaGenerator.generate(4);
            byte[] captchaBytes = captcha.getBytes();
            request.getSession().setAttribute(SESSION_ATTRIBUTE_NAME, captchaBytes);
            response.setContentType("image/png");

            // 输出验证码图片
            try (ServletOutputStream out = response.getOutputStream()) {
                ImageIO.write(createImage(captchaBytes), "png", out);
                out.flush();
            }
        }

        private BufferedImage createImage(byte[] captcha) {
            BufferedImage image = new BufferedImage(100, 40, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = image.createGraphics();
            Font font = new Font("Georgia", Font.PLAIN, 18);

            // 绘制验证码文本
            for(int i = 0, x = 10; i < captcha.length; i++, x += 20) {
                g.setFont(font);
                g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
                g.drawString(String.valueOf((char)captcha[i]), x, 20);
            }

            // 绘制干扰线
            for(int i = 0; i < 6; i++) {
                g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255)));
                g.drawLine(rand.nextInt(80), rand.nextInt(30) + 5, rand.nextInt(80), rand.nextInt(30) + 5);
            }

            return image;
        }
    }

    @Bean
    public VerifyCodeService verifyCodeService() {
        return new VerifyCodeService();
    }

    private class VerifyCodeService implements InitializingBean {

        private AbstractHttpSessionApplicationInitializer httpSessionApplicationInitializer;

        public VerifyCodeService() {}

        public boolean validate(HttpServletRequest request, HttpServletResponse response) {
            byte[] captcha = (byte[])request.getSession().getAttribute(VerifyCodeFilter.SESSION_ATTRIBUTE_NAME);
            String inputCaptcha = request.getParameter("captcha");

            return captcha != null && inputCaptcha != null && Arrays.equals(captcha, inputCaptcha.getBytes());
        }

        @Override
        public void afterPropertiesSet() throws Exception {  
            // 获取httpSessionApplicationInitializer实例
            ApplicationContext context = ApplicationContextProvider.getApplicationContext(); 
            httpSessionApplicationInitializer = context.getBean(AbstractHttpSessionApplicationInitializer.class); }   
        }
    }

最后需要注意的是,在配置文件中添加验证码过滤器时,需要将它加到用户名密码过滤器的前面。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationProvider authenticationProvider;

    @Autowired
    private VerifyCodeFilter verifyCodeFilter;

    // 省略其他配置代码

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout()
                .permitAll()
                .and()
            .addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class) // 添加验证码过滤器
            .authenticationProvider(authenticationProvider);
    }

    // 省略其他配置代码
}

以上就是关于“Spring Security 图片验证码功能的实例代码”的详细攻略,希望能够对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security 图片验证码功能的实例代码 - Python技术站

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

相关文章

  • java登录验证码实现代码

    实现Java登录验证码的代码,可以使用Java的第三方开源框架Kaptcha。下面是详细步骤。 Kaptcha安装 下载jar包 在Kaptcha官网上下载最新的jar包。 导入jar包 将下载的jar包导入项目的Classpath路径下。 Kaptcha使用 添加JSP页面代码 在需要验证码的登录页面的form标签中添加如下代码: “`html 验证码

    Java 2023年5月23日
    00
  • jsp使用cookie存储中文示例分享

    使用Cookie存储中文字符是Java Web开发常见的一个问题,本攻略主要介绍使用JSP存储中文字符到Cookie中的方法。具体操作如下: 1. 添加Cookie 使用JSP的Cookie类的setValue()函数,可以在Cookie中存储中文字符。示例代码如下: <% //新建一个中文Cookie Cookie cookie = new Cook…

    Java 2023年6月15日
    00
  • String类型转localDate,date转localDate的实现代码

    首先,我们需要了解Java中日期类型的概念。在Java 8之前,我们通常使用java.util.Date类来处理日期,但是这个类在很多方面都存在问题。因此,在Java 8 中引入了java.time包,提供了全新的日期和时间API,其中LocalDate是处理日期的主要类之一。 String类型转LocalDate 将String类型转换为LocalDate…

    Java 2023年5月20日
    00
  • java异常处理拦截器详情

    Java异常处理拦截器(Exception Handler)是一个对应用程序中的异常做出响应的组件。它可以捕捉并处理应用程序中发生的异常,从而使程序能够从异常中恢复并继续执行。在Java中,Exception Handler是通过异常处理代码块(try-catch)或者异常处理方法(throws)来实现的。 下面我们将具体讲解如何使用Java异常处理拦截器:…

    Java 2023年5月27日
    00
  • Java基础知识之BufferedReader流的使用

    Java基础知识之BufferedReader流的使用 BufferedReader是Java中一个常用的字符输入流,常用于读取文本文件中的数据。相较于其他的字符输入流,BufferedReader具有缓冲功能,能够更加高效地读取文件的内容。本文将详细介绍BufferedReader流的使用,包括如何创建、如何读取文件内容、如何关闭流等。 创建Buffere…

    Java 2023年5月26日
    00
  • Mybatis各种查询接口使用详解

    Mybatis各种查询接口使用详解 Mybatis是一款优秀的持久层框架,提供了不同的查询接口来满足各种复杂查询需求。本文将详细讲解Mybatis各种查询接口的使用方法。 基本查询 select 使用select查询数据非常简单,只需要在Mapper接口定义对应的方法,返回值为查询结果即可。 <!– Mapper.xml –> <sel…

    Java 2023年5月19日
    00
  • SpringBoot定时任务设计之时间轮案例原理详解

    SpringBoot定时任务设计之时间轮案例原理详解 本文将详细介绍SpringBoot定时任务设计之时间轮案例,讲解时间轮的基本原理和实现方式,以及如何在SpringBoot中实现定时任务的调度。 基本原理 时间轮是一种常见的定时任务调度算法,它的基本原理是将时间线性化,并按照固定的时间间隔划分成若干个时间槽,将任务按照配合它触发时间所在的时间槽进行存储和…

    Java 2023年5月20日
    00
  • 基于重定向RedirectAttributes的用法解析

    基于重定向 RedirectAttributes 的用法解析 在 Spring MVC 中,经常会使用重定向来实现一些跳转的功能。而 RedirectAttributes 则是在使用重定向时用于向跳转页面传递数据的对象。 RedirectAttributes 的用法 使用 RedirectAttributes 一般需要按以下步骤进行: 在处理请求的方法中通过…

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