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 <a href="#">Register</a> Here</h3>
</div>
</div>
</body>
</html>
测试步骤这里就不写了,自行测试即可
转载:https://blog.csdn.net/weixin_45612794/article/details/105893322
查看评论