增加图形验证码
本文依据学习编码而来
增加图形类ImageCode
public class ImageCode {
private BufferedImage image;
private String code;
private LocalDateTime expireTime;
public ImageCode(BufferedImage image, String code, int expireIn) {
this.image = image;
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
}
public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
this.image = image;
this.code = code;
this.expireTime = expireTime;
}
public boolean isExpire(){
return LocalDateTime.now().isAfter(this.expireTime);
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public LocalDateTime getExpireTime() {
return expireTime;
}
public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
}
@Override
public String toString() {
return "ImageCode{" +
"image=" + image +
", code='" + code + '\'' +
", expireTime=" + expireTime +
'}';
}
}
增加校验验证类
@RestController
public class ValidateController {
public final static String SESSION_KEY_IMAGE_CODE="SESSION_KEY_IMAGE_CODE";
private SessionStrategy sessionStrategy=new HttpSessionSessionStrategy();
@GetMapping("/code/image")
public void createImageCode(HttpServletRequest request, HttpServletResponse response) throws IOException
{
ImageCode imageCode=createImageCode();
sessionStrategy.setAttribute(new ServletRequestAttributes(request),SESSION_KEY_IMAGE_CODE,imageCode);
ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
}
private ImageCode createImageCode(){
//验证码图片宽度
int width=100;
//验证码图片高度
int height=36;
//验证码位数
int length=4;
//验证码过期时间
int expireIn=60;
//生成图片
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
Graphics g=image.createGraphics();
//产生随机数
Random random=new Random();
g.setColor(getRandColor(200,250));
g.fillRect(0,0,width,height);
g.setFont(new Font("Times New Roman",Font.ITALIC,20));
g.setColor(getRandColor(160,200));
for(int i=0;i<155;i++){
int x=random.nextInt(width);
int y=random.nextInt(height);
int x1=random.nextInt(12);
int y1=random.nextInt(12);
g.drawLine(x,y,x1,y1);
}
StringBuilder sb=new StringBuilder();
for(int i=0;i<length;i++){
String rand=String.valueOf(random.nextInt(10));
sb.append(rand);
g.setColor(getRandColor(110,130));
g.drawString(rand,13+i*12,16);
}
g.dispose();
return new ImageCode(image,sb.toString(),expireIn);
}
private Color getRandColor(int fc,int bc){
Random random=new Random();
if(fc>255){
fc=255;
}
if(bc>255){
bc=255;
}
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
}
通过IOImage.wirte方法来实现将图片绑定到输出流中
* Writes an image using an arbitrary <code>ImageWriter</code>
* that supports the given format to an <code>OutputStream</code>.
*
* <p> This method <em>does not</em> close the provided
* <code>OutputStream</code> after the write operation has completed;
* it is the responsibility of the caller to close the stream, if desired.
*
* <p> The current cache settings from <code>getUseCache</code>and
* <code>getCacheDirectory</code> will be used to control caching.
*
* @param im a <code>RenderedImage</code> to be written.
* @param formatName a <code>String</code> containing the informal
* name of the format.
* @param output an <code>OutputStream</code> to be written to.
*
* @return <code>false</code> if no appropriate writer is found.
*
* @exception IllegalArgumentException if any parameter is
* <code>null</code>.
* @exception IOException if an error occurs during writing.
*/
public static boolean write(RenderedImage im,
String formatName,
OutputStream output) throws IOException {
将获取验证码方法的Url地址放到免于验证的地址中
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
// http
.formLogin()//表单认证
.loginPage("/auth/require")
.loginProcessingUrl("/login")
.successHandler(customAuthSuccessHandler)
.failureHandler(customAuthFailHandler)
.and()
.authorizeRequests()//授权配置
.antMatchers("/login.html",
"/auth/require",
"/code/image").permitAll()
.anyRequest()//所有请求
.authenticated()//都需要认证
.and()
.csrf().disable();
增加验证码校验,判断验证码是否有效
@Component
public class ValidateCodeFilter extends OncePerRequestFilter {
Logger logger= LoggerFactory.getLogger(getClass());
private AuthenticationFailureHandler authenticationFailureHandler;
private SessionStrategy sessionStrategy=new HttpSessionSessionStrategy();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(StringUtils.pathEquals("/login",request.getRequestURI()) && "post".equalsIgnoreCase(request.getMethod()))
{
try {
validateCode(new ServletWebRequest(request));
}
catch (ValidateCodeException err){
authenticationFailureHandler.onAuthenticationFailure(request,response,err);
return;
}
}
filterChain.doFilter(request,response);
}
private void validateCode(ServletWebRequest request) throws ServletRequestBindingException, ValidateCodeException {
ImageCode image=(ImageCode) sessionStrategy.getAttribute(request, ValidateController.SESSION_KEY_IMAGE_CODE);
logger.info("imagecode is ..."+image.getCode());
logger.info("imagecode expiretime is "+image.getExpireTime());
String codeInRequest= ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");
if(StringUtils.isEmpty(codeInRequest))
{
throw new ValidateCodeException("验证码不能为空");
}
if(image==null)
{
throw new ValidateCodeException("验证码不存在");
}
if(image.isExpire())
{
sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY_IMAGE_CODE);
throw new ValidateCodeException("验证码已过期");
}
logger.info("用户输入的验证码是"+codeInRequest);
if(!codeInRequest.equalsIgnoreCase(image.getCode()))
{
throw new ValidateCodeException("验证码不匹配");
}
sessionStrategy.removeAttribute(request,ValidateController.SESSION_KEY_IMAGE_CODE);
}
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
}
}
还需要在html中增加图片显示
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form class="login-page" action="/login" method="post">
<div class="form">
<h3>账户登录</h3>
<input type="text" placeholder="用户名" name="username" required="required" />
<input type="password" placeholder="密码" name="password" required="required" />
<span style="display: inline">
<input type="text" name="imageCode" placeholder="验证码" style="width: 50%;">
<img src="/code/image">
</span>
<button type="submit">登录</button>
</div>
</form>
</body>
</html>
测试
输入错误的验证码
日志打印