第七章 验证码整合


验证码生成工具可以使用hutool依赖库进行。

验证码生成接口代码


import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.captcha.generator.MathGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Controller
public class CaptchaController {

    @GetMapping(value = "code/getCaptchaCode")
    public void getCaptchaCode(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse) throws IOException {
        //ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 5);
        CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 1000);
        circleCaptcha.setGenerator(new MathGenerator());
        String code = circleCaptcha.getCode();
        log.info("生成的图片验证码为:{}",code);
        //将验证码存储到session中
        httpServletRequest.getSession().setAttribute("CAPTCHA CODE",code);
        httpServletResponse.setContentType("image/png");
        //将图片写到响应流里
        ImageIO.write(circleCaptcha.getImage(),"PNG",httpServletResponse.getOutputStream());
    }
}

写好验证码之后即可在WebSecurity实现配置类中将验证码接口放开:


import com.example.springsecuritystudy4.filter.ValidateCodeFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/code/getCaptchaCode") //将验证码接口放开
                .permitAll()
                .anyRequest()
                .authenticated();

        http.formLogin()
                .loginPage("/toLogin") // 配置登录页面
                .usernameParameter("uname") // 用户名参数
                .passwordParameter("pwd") // 密码参数
                .loginProcessingUrl("/login/doLogin")  //单击登录后进入url
                .failureForwardUrl("/toLogin") // 登录失败
                .successForwardUrl("/toIndex") //登录成功
                .permitAll();  //放开

        http.logout().logoutSuccessUrl("/toLogin");  //配置退出成功登录页面
        http.csrf().disable(); //关闭跨域请求保护
    }
}

利用过滤器来验证验证码

过滤器需要继承实现OncePerRequestFilter接口。以下为实现例子

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
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 java.io.IOException;

@Slf4j
@Component
public class ValidateCodeFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // TODO 判断路径 是否是/login/doLogin
        String requestURL = request.getRequestURI();
        if(!requestURL.equals("/login/doLogin")){ //不是登录请求,直接放行
            doFilter(request,response,filterChain);
        }

    }

    private void validateCode(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) throws ServletException, IOException {
        //TODO 1、从前端获取验证码
        String code = request.getParameter("code");
        //TODO 2、从session中获取验证码
        String captchaCode = (String) request.getSession().getAttribute("CAPTCHA_CODE");
        //TODO 3、判断二者是否相等
        if(StringUtils.isEmpty(code)){
            request.getSession().setAttribute("captcha_code_error","请输入验证码");
            return;
        }
        if(StringUtils.isEmpty(captchaCode)){
            request.getSession().setAttribute("captcha_code_error","验证码错误");
        }
        request.getSession().setAttribute("CAPTCHA_CODE","");
        this.doFilter(request,response,filterChain);
    }
}

写好过滤器后即可在配置类里面使用过滤器:

import com.example.springsecuritystudy4.filter.ValidateCodeFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    ValidateCodeFilter validateCodeFilter;

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //在用户名密码认证过滤器前添加验证码过滤器
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests()
                .mvcMatchers("/code/getCaptchaCode")
                .permitAll()
                .anyRequest()
                .authenticated();

        http.formLogin()
                .loginPage("/toLogin") // 配置登录页面
                .usernameParameter("uname") // 用户名参数
                .passwordParameter("pwd") // 密码参数
                .loginProcessingUrl("/login/doLogin")  //单击登录后进入url
                .failureForwardUrl("/toLogin") // 登录失败
                .successForwardUrl("/toIndex") //登录成功
                .permitAll();  //放开

        http.logout().logoutSuccessUrl("/toLogin");  //配置退出成功登录页面
        http.csrf().disable(); //关闭跨域请求保护
    }
}