生成验证码 controller
@Controller
public class KaptchaController {
private final Producer producer;
@Autowired
public KaptchaController(Producer producer) {
this.producer = producer;
}
@GetMapping("/vc.jpg")
public void getVerifyCode(HttpServletResponse response, HttpSession session) throws IOException {
response.setContentType("image/png");
String code = producer.createText();
session.setAttribute("kaptcha", code);//可以更换成 redis 实现
BufferedImage bi = producer.createImage(code);
ServletOutputStream os = response.getOutputStream();
ImageIO.write(bi, "jpg", os);
}
}
自定义验证码异常类
public class KaptchaNotMatchException extends AuthenticationException {
public KaptchaNotMatchException(String msg) {
super(msg);
}
public KaptchaNotMatchException(String msg, Throwable cause) {
super(msg, cause);
}
}
自定义filter验证验证码
public class KaptchaFilter extends UsernamePasswordAuthenticationFilter {
public static final String KAPTCHA_KEY = "kaptcha";//默认值
private String kaptcha = KAPTCHA_KEY;
public String getKaptcha() {
return kaptcha;
}
public void setKaptcha(String kaptcha) {
this.kaptcha = kaptcha;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//1.判断是否是 post 方式
if (request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//2.获取验证码
String kaptcha = request.getParameter(getKaptcha());
String sessionKaptcha = (String) request.getSession().getAttribute("kaptcha");
if (!ObjectUtils.isEmpty(kaptcha) && !ObjectUtils.isEmpty(sessionKaptcha) &&
kaptcha.equalsIgnoreCase(sessionKaptcha)) {
return super.attemptAuthentication(request, response);
}
throw new KaptchaNotMatchException("验证码输入错误!");
}
}
放行以及配置验证码 filter
@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
@Autowired
public WebSecurityConfigurer(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public KaptchaFilter kaptchaFilter() throws Exception {
KaptchaFilter kaptchaFilter = new KaptchaFilter();
//指定接收验证码请求参数名
kaptchaFilter.setKaptcha("kaptcha");
//指定认证管理器
kaptchaFilter.setAuthenticationManager(authenticationManagerBean());
//指定认证成功和失败处理
kaptchaFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
kaptchaFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
//指定处理登录
kaptchaFilter.setFilterProcessesUrl("/doLogin");
kaptchaFilter.setUsernameParameter("uname");
kaptchaFilter.setPasswordParameter("passwd");
return kaptchaFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeHttpRequests()
.mvcMatchers("/vc.jpg").permitAll()
.mvcMatchers("/login.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
...
http.addFilterAt(kaptchaFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
登录页面添加验证码
<form method="post" th:action="@{/doLogin}">
用户名:<input name="uname" type="text"/><br>
密码:<input name="passwd" type="password"/><br>
验证码: <input name="kaptcha" type="text"/> <img alt="" th:src="@{/vc.jpg}"><br>
<input type="submit" value="登录"/>
</form>