飞道的博客

spring-security(二):过滤器实现图片验证码

271人阅读  评论(0)

1.添加验证码相关依赖

<!--用于获取验证码 不可用于生产环境-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>

2.配置一个kaptcha实例

@Bean
    public Producer captcha(){
        //配置图形验证码的基本参数
        final Properties properties = new Properties();
        //图片宽度
        properties.setProperty("kaptcha.image.width","150");
        //图片长度
        properties.setProperty("kaptcha.image.height","50");
        //字符集
        properties.setProperty("kaptcha.textproducer.char.string","0123456789");
        properties.setProperty("kaptcha.textproducer.char.length","4");
        final Config config = new Config(properties);
        //使用默认的图片验证码实现,当然也可以自定义实现
        final DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

3.创建一个控制器,用于获取验证码

import com.google.code.kaptcha.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
@Controller
public class CaptchaController {
    @Autowired
    private Producer captchaProducer;

    @GetMapping("/captcha.jpg")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //设置内容类型
        response.setContentType("img/jpeg");
        //创建验证码文本
        final String capText = captchaProducer.createText();
        //将验证码文本设置到session
        request.getSession().setAttribute("captcha",capText);
        //创建验证码图片
        final BufferedImage bi = captchaProducer.createImage(capText);
        //获取响应输出流
        final ServletOutputStream out = response.getOutputStream();
        //将图片验证码数据写入到响应输出流
        ImageIO.write(bi,"jpg",out);
        try {
            out.flush();
        }finally {
            out.close();
        }

    }
}

4.自定义校验失败异常类

public class VerificationCodeException extends AuthenticationException {
    public VerificationCodeException() {
        super("图片验证码校验失败!");
    }
}

5.自定义验证失败处理器

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                        AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("text/html;charset=utf-8");
        final PrintWriter out = response.getWriter();
        final HashMap<String, Object> result = new HashMap<>();
        result.put("error_code", "401");
        result.put("message", exception.getMessage());
        result.put("class", exception.getClass().toString());
        //JSONUtil为huTool包下的
        out.write(JSONUtil.toJsonStr(result));
        out.close();
    }
}

6.自定义编码校验过滤器

虽然在spring Security的过滤器链中对过滤器没有特殊要求,只要继承了Filter即可,但是在Spring体系中,推荐使用OncePerRequestFilter来实现,它可以确保一次请求只会通过一次该过滤器。

import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Objects;

public class VerificationCodeFilter extends OncePerRequestFilter {
    private AuthenticationFailureHandler authenticationFailureHandler = new MyAuthenticationFailureHandler();

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        //非登录请求不校验验证码
        if (!"/auth/form".equals(httpServletRequest.getRequestURI())) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        } else {
            try {
                verificationCode(httpServletRequest);
                filterChain.doFilter(httpServletRequest, httpServletResponse);
            } catch (VerificationCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
            }
        }
    }

    private void verificationCode(HttpServletRequest httpServletRequest) {
        final String requestCode = httpServletRequest.getParameter("captcha");
        final HttpSession session = httpServletRequest.getSession();
        final String savedCode = (String) session.getAttribute("captcha");
        if (StringUtils.isEmpty(savedCode)) {
            //随手清除验证码,无论是失败,还是成功。客户端应该在登录失败时刷新验证码
            session.removeAttribute("captcha");
        }
        //校验不通过,抛出异常
        if (StringUtils.isEmpty(requestCode) || StringUtils.isEmpty(savedCode) || !Objects.equals(requestCode,
                savedCode)) {
            throw new VerificationCodeException();
        }
    }
}

7.将过滤器配置到Securuty的过滤链中

package com.demo.springsecuritydemo.config;

import com.demo.springsecuritydemo.filters.VerificationCodeFilter;
import com.demo.springsecuritydemo.handler.MyAuthenticationFailureHandler;
import com.demo.springsecuritydemo.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;


@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //开放captcha.jpg的访问权限
                .antMatchers("/captcha.jpg").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                //跳转登录页位置
                .loginPage("/myLogin.html")
                //与登录页面的表单action属性对应
                .loginProcessingUrl("/auth/form")
                //成功登录执行的操作
                .successHandler((httpServletRequest, httpServletResponse, authentication) -> {
                    httpServletResponse.setContentType("application/json;charset=UTF-8");
                    final PrintWriter out = httpServletResponse.getWriter();
                    out.write("{\"error_code\":\"0\",\"message\":\"欢迎登录系统!\"}");
                })
                //失败执行的操作
                .failureHandler(new MyAuthenticationFailureHandler())
                .permitAll()
                .and()
                .csrf().disable();

        //将过滤器添加到UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

8.最后修改表单登录页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <meta http-equiv="Content-Type" content="text/html;charset = utf-8">
    <style>
        /*some style*/
    </style>
</head>
<body>
<div class="login">
    <h2>Acced Form</h2>
    <div class="login-top">
        <h1>Login Form</h1>
        <form action="/auth/form" method="post">
            <input type="text" name="username" placeholder="username">
            <input type="text" name="password" placeholder="password">
            <div style="display: flex">
                <!--新增图片验证码的输入框-->
                <input type="text" name="captcha" placeholder="captcha">
                <!--图片指向获取图片验证码的api-->
                <img src="/captcha.jpg" alt="captcha" height="50px" width="150px" style="margin-left: 20px">
            </div>
            <div class="forgot">
                <a href="#">forgot Password</a>
                <input type="submit" value="Login">
            </div>
        </form>
    </div>
    <div class="login-bottom">
        <h3>New User &nbsp;<a href="#">Register</a>&nbsp; Here</h3>
    </div>

</div>

</body>
</html>

测试步骤这里就不写了,自行测试即可


转载:https://blog.csdn.net/weixin_45612794/article/details/105893322
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场