前言
验证码安全验证也是安全认证一个功能,本文介绍通过Kaptcha在SpringSecurity中如何整合实现。
验证码实现
引入jar包
在pom.xml中添加以下依赖项
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
实例化kaptcha配置对象
核心代码如下:
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import java.util.Properties;
public class KaptchaSingle {
private static KaptchaSingle instance;
private KaptchaSingle() {
}
public static KaptchaSingle getInstance() {
if (instance == null) {
instance = new KaptchaSingle();
}
return instance;
}
public DefaultKaptcha produce() {
Properties properties = new Properties();
properties.put("kaptcha.border", "no");
properties.put("kaptcha.border.color", "105,179,90");
properties.put("kaptcha.textproducer.font.color", "blue");
properties.put("kaptcha.image.width", "100");
properties.put("kaptcha.image.height", "50");
properties.put("kaptcha.textproducer.font.size", "27");
properties.put("kaptcha.session.key", "code");
properties.put("kaptcha.textproducer.char.length", "4");
properties.put("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
properties.put("kaptcha.textproducer.char.string", "0123456789ABCEFGHIJKLMNOPQRSTUVWXYZ");
properties.put("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
properties.put("kaptcha.noise.color", "black");
properties.put("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
properties.put("kaptcha.background.clear.from", "185,56,213");
properties.put("kaptcha.background.clear.to", "white");
properties.put("kaptcha.textproducer.char.space", "3");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
业务模块依赖通用组件模块:
生成验证码控制层
import index.KaptchaSingle;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
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.ByteArrayOutputStream;
@Controller
public class KaptchaController {
@RequestMapping("/verifyCode.jpg")
public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws Exception {
byte[] captchaChallengeAsJpeg = null;
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
// 代码方式创建:DefaultKaptcha
KaptchaSingle single = KaptchaSingle.getInstance();
DefaultKaptcha defaultKaptcha = single.produce();
// 生产验证码字符串并保存到session中
String createText = defaultKaptcha.createText();
httpServletRequest.getSession().setAttribute("verifyCode", createText);
// 使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
BufferedImage challenge = defaultKaptcha.createImage(createText);
ImageIO.write(challenge, "jpg", jpegOutputStream);
} catch (IllegalArgumentException e) {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
// 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
}
}
登录界面调用
......
<input type="text" class="form-control" name="verifyCode" placeholder="验证码" />
......
<img class="verify_img" onclick = "this.src='/verifyCode.jpg?d='+new Date()*1" src="/verifyCode.jpg" title="点击获取">
......
此刻我们就能看到验证码效果了,但注意,如果以及整合了SpringSecurity这需要将 /verifyCode.jpg 在配置中设为无需权限。
......
.antMatchers("/verifyCode.jpg").permitAll()
......
界面效果如图:
SpringSecurity验证码处理
KaptchaFilter
验证码拦截器代码
/**
* Kaptcha 拦截器
*/
public class KaptchaFilter extends AbstractAuthenticationProcessingFilter {
//session 关于验证码
private static final String VERIFYCODE = "verifyCode";
//拦截请求地址
private String servletPath;
public KaptchaFilter(String servletPath, String failureUrl) {
super(servletPath);
this.servletPath = servletPath;
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler(failureUrl));
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if ("POST".equalsIgnoreCase(req.getMethod()) && servletPath.equals(req.getServletPath())) {
String expect = (String) req.getSession().getAttribute(VERIFYCODE);
if (expect != null && !expect.equalsIgnoreCase(req.getParameter(VERIFYCODE))) {
unsuccessfulAuthentication(req, res, new InsufficientAuthenticationException("输入的验证码不正确"));
return;
}
}
chain.doFilter(req, res);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
return null;
}
}
SpringSecurity配置
spring-security 配置文件,添加验证码拦截器,在验证用户密码拦截器之前。
......
http.addFilterBefore(new KaptchaFilter("/login", "/login?error"), UsernamePasswordAuthenticationFilter.class);
......
至此,整个验证码功能实现了。
Kaptcha配置内容
Constant | 描述 | 默认值 |
---|---|---|
kaptcha.border | 图片边框,合法值:yes , no | yes |
kaptcha.border.color | 边框颜色,合法值: r,g,b (and optional alpha) 或者 white,black,blue | black |
kaptcha.image.width | 图片宽 | 200 |
kaptcha.image.height | 图片高 | 50 |
kaptcha.producer.impl | 图片实现类 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | 文本实现类 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | 文本集合,验证码值从此集合中获取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 验证码长度 | 5 |
kaptcha.textproducer.font.names | 字体 | Arial, Courier |
kaptcha.textproducer.font.size | 字体大小 | 40px. |
kaptcha.textproducer.font.color | 字体颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.textproducer.char.space | 文字间隔 | 2 |
kaptcha.noise.impl | 干扰实现类 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.colorv | 干扰 颜色,合法值: r,g,b 或者 white,black,blue. | black |
kaptcha.obscurificator.impl | 图片样式: 水纹 com.google.code.kaptcha.impl.WaterRipple 鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple |
kaptcha.background.impl | 背景实现类 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景颜色渐变,开始颜色 | light grey |
kaptcha.background.clear.to | 背景颜色渐变, 结束颜色 | white |
kaptcha.word.impl | 文字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |