(八)、SpringBoot 图形验证码实现
2018-03-15 16:58
519 查看
可以前往第一篇博客查看目录结构 --> 这里
一、在core模块validate包下创建一个通用验证码实体类 -> ValidateCode (包含验证码、过期时间、判断是否过期的方法)public class ValidateCode {
private String code;
private LocalDateTime expireTime;
public ValidateCode(String code, int expireTime) {
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireTime);
}
public ValidateCode(String code, LocalDateTime expireTime) {
this.code = code;
this.expireTime = expireTime;
}
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;
}
/**
* 判断时间是否过期
* @return
*/
public boolean isExpried() {
return LocalDateTime.now().isAfter(expireTime);
}
}
二、创建图形验证码实体类ImageCode 继承于 ValidateCode (新增image属性,保存验证图片)public class ImageCode extends ValidateCode{
private BufferedImage image;
public ImageCode(BufferedImage image, String code, int expireTime) {
super(code, expireTime);
this.image = image;
}
public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
super(code, expireTime);
this.image = image;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}
三、创建图形验证码java配置文件,自定义需要的属性public class ImageCodeProperties {
private int width = 67;
private int height = 23;
private int length = 4;
private int expireIn = 60;
private String url;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getExpireIn() {
return expireIn;
}
public void setExpireIn(int expireIn) {
this.expireIn = expireIn;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
四、在ImageCodeProperties上封装多一层通用的ValidateCodePropertiespublic class ValidateCodeProperties {
private ImageCodeProperties image = new ImageCodeProperties();
public ImageCodeProperties getImage() {
return image;
}
public void setImage(ImageCodeProperties image) {
this.image = image;
}
}
五、在SecurityProperties中new 一个ValidateCodeProperties ,集中配置@ConfigurationProperties(prefix = "zeke.security")
public class SecurityProperties {
private BrowserProperties browser = new BrowserProperties();
private ValidateCodeProperties code = new ValidateCodeProperties();
public BrowserProperties getBrowser() {
return browser;
}
public void setBrowser(BrowserProperties browser) {
this.browser = browser;
}
public ValidateCodeProperties getCode() {
return code;
}
public void setCode(ValidateCodeProperties code) {
this.code = code;
}
}
六、需要一个验证码生成的工具,创建ValidateCodeGenerator验证码生成接口
public interface ValidateCodeGenerator {
ValidateCode generator(ServletWebRequest request);
}
七、创建ImageCodeGenerator实现ValidateCodeGenerator接口(实现内容不必深究,百度一堆一堆的,大公司也有自己的内部生成方式)
其中的图片长、宽,验证码长度等,都从SecurtiyProperties中获取(可以在application.properties中配置),例:zeke.security.code.image.length = 4
zeke.security.code.image.width = 100
public class ValidateCodeBeanConfig {
@Autowired
private SecurityProperties securityProperties;
/**
* 如果要更换图形验证码的实现,可以到DemoImageCodeGenerator中实现(加上@Component("imageCodeGenerator")注解即可)
* @return
*/
@Bean
@ConditionalOnMissingBean(name = "imageCodeGenerator")
public ValidateCodeGenerator imageCodeGenerator(){
ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator();
imageCodeGenerator.setSecurityProperties(securityProperties);
return imageCodeGenerator;
}
}
九、创建图形验证码接口 ValidateCodeController@RestController
public class ValidateCodeController {
public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
@Autowired
private ValidateCodeGenerator ImageCodeGenerator;
/**
* 图形验证码生成、保存、发送
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/code/image")
public void createImageCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
ImageCode imageCode = (ImageCode) ImageCodeGenerator.generator(new ServletWebRequest(request));
sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
}
}
十、自定义一个简单的验证码异常public class ValidateCodeException extends AuthenticationException {
public ValidateCodeException(String explanation) {
super(explanation);
}
}
十一、创建一个验证码过滤器,对指定URL进行过滤,验证码错误抛出异常,验证码正确则移除session中保存的验证码
该url也可以在application.properties指定(用逗号分割):zeke.security.code.image.url = /user/*,/userValidateCodeFilter:
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 存放所有需要拦截的URL
*/
private Set<String> urls = new HashSet<>();
private SecurityProperties securityProperties;
private AntPathMat
b372
cher pathMatcher = new AntPathMatcher();
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(),",");
for (String configUrl : configUrls) {
urls.add(configUrl);
}
urls.add("/authentication/form");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
boolean action = false;
for (String url : urls) {
if (pathMatcher.match(url,request.getRequestURI())){
action = true;
}
}
if (action){
try {
validate(new ServletWebRequest(request));
}
catch (ValidateCodeException e) {
authenticationFailureHandler.onAuthenticationFailure(request,response,e);
return;
}
}
filterChain.doFilter(request,response);
}
/**
* 校验提交验证码的合法性
* @param request
* @throws ServletRequestBindingException
*/
private void validate(ServletWebRequest request) throws ServletRequestBindingException {
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY);
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
if (StringUtils.isBlank(codeInRequest)){
throw new ValidateCodeException("验证码的值不能为空");
}
if (codeInSession == null){
throw new ValidateCodeException("验证码不存在");
}
if (codeInSession.isExpried()){
sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
throw new ValidateCodeException("验证码已过期");
}
if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)){
throw new ValidateCodeException("验证码不匹配");
}
sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
}
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
}
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
}
public SessionStrategy getSessionStrategy() {
return sessionStrategy;
}
public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
}
public Set<String> getUrls() {
return urls;
}
public void setUrls(Set<String> urls) {
this.urls = urls;
}
public SecurityProperties getSecurityProperties() {
return securityProperties;
}
public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
十二、更改zeke-login.html中的表单,增加图形验证码<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Page</title>
</head>
<body>
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td>图形验证码: </td>
<td>
<input type="text" name="imageCode"/>
<img src="/code/image"/>
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>
</body>
</html>
十三、在BrowserSecurityConfig中把 /code/image加入放行url ,不然页面会拦截/code/image的请求;
配置ValidateCodeFilter;
十四、启动项目访问localhost/zeke-login.html 测试
一、在core模块validate包下创建一个通用验证码实体类 -> ValidateCode (包含验证码、过期时间、判断是否过期的方法)public class ValidateCode {
private String code;
private LocalDateTime expireTime;
public ValidateCode(String code, int expireTime) {
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireTime);
}
public ValidateCode(String code, LocalDateTime expireTime) {
this.code = code;
this.expireTime = expireTime;
}
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;
}
/**
* 判断时间是否过期
* @return
*/
public boolean isExpried() {
return LocalDateTime.now().isAfter(expireTime);
}
}
二、创建图形验证码实体类ImageCode 继承于 ValidateCode (新增image属性,保存验证图片)public class ImageCode extends ValidateCode{
private BufferedImage image;
public ImageCode(BufferedImage image, String code, int expireTime) {
super(code, expireTime);
this.image = image;
}
public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
super(code, expireTime);
this.image = image;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}
三、创建图形验证码java配置文件,自定义需要的属性public class ImageCodeProperties {
private int width = 67;
private int height = 23;
private int length = 4;
private int expireIn = 60;
private String url;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getExpireIn() {
return expireIn;
}
public void setExpireIn(int expireIn) {
this.expireIn = expireIn;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
四、在ImageCodeProperties上封装多一层通用的ValidateCodePropertiespublic class ValidateCodeProperties {
private ImageCodeProperties image = new ImageCodeProperties();
public ImageCodeProperties getImage() {
return image;
}
public void setImage(ImageCodeProperties image) {
this.image = image;
}
}
五、在SecurityProperties中new 一个ValidateCodeProperties ,集中配置@ConfigurationProperties(prefix = "zeke.security")
public class SecurityProperties {
private BrowserProperties browser = new BrowserProperties();
private ValidateCodeProperties code = new ValidateCodeProperties();
public BrowserProperties getBrowser() {
return browser;
}
public void setBrowser(BrowserProperties browser) {
this.browser = browser;
}
public ValidateCodeProperties getCode() {
return code;
}
public void setCode(ValidateCodeProperties code) {
this.code = code;
}
}
六、需要一个验证码生成的工具,创建ValidateCodeGenerator验证码生成接口
public interface ValidateCodeGenerator {
ValidateCode generator(ServletWebRequest request);
}
七、创建ImageCodeGenerator实现ValidateCodeGenerator接口(实现内容不必深究,百度一堆一堆的,大公司也有自己的内部生成方式)
其中的图片长、宽,验证码长度等,都从SecurtiyProperties中获取(可以在application.properties中配置),例:zeke.security.code.image.length = 4
zeke.security.code.image.width = 100
public class ImageCodeGenerator implements ValidateCodeGenerator { @Autowired private SecurityProperties securityProperties; @Override public ImageCode generator(ServletWebRequest request) { int width = ServletRequestUtils.getIntParameter(request.getRequest(),"width",securityProperties.getCode().getImage().getWidth()); int height = ServletRequestUtils.getIntParameter(request.getRequest(),"height",securityProperties.getCode().getImage().getHeight()); BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); 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, x+x1, y+y1); } String sRand = ""; for (int i = 0; i < securityProperties.getCode().getImage().getLength(); i++){ String rand = String.valueOf(random.nextInt(10)); sRand += rand; g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand, securityProperties.getCode().getImage().getExpireIn()); } 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); } public SecurityProperties getSecurityProperties() { return securityProperties; } public void setSecurityProperties(SecurityProperties securityProperties) { this.securityProperties = securityProperties; } }八、创建ValidateCodeBeanConfig,把ImageCodeGenerator注入到Spring容器中@Configuration
public class ValidateCodeBeanConfig {
@Autowired
private SecurityProperties securityProperties;
/**
* 如果要更换图形验证码的实现,可以到DemoImageCodeGenerator中实现(加上@Component("imageCodeGenerator")注解即可)
* @return
*/
@Bean
@ConditionalOnMissingBean(name = "imageCodeGenerator")
public ValidateCodeGenerator imageCodeGenerator(){
ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator();
imageCodeGenerator.setSecurityProperties(securityProperties);
return imageCodeGenerator;
}
}
九、创建图形验证码接口 ValidateCodeController@RestController
public class ValidateCodeController {
public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
@Autowired
private ValidateCodeGenerator ImageCodeGenerator;
/**
* 图形验证码生成、保存、发送
* @param request
* @param response
* @throws IOException
*/
@GetMapping("/code/image")
public void createImageCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
ImageCode imageCode = (ImageCode) ImageCodeGenerator.generator(new ServletWebRequest(request));
sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);
ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
}
}
十、自定义一个简单的验证码异常public class ValidateCodeException extends AuthenticationException {
public ValidateCodeException(String explanation) {
super(explanation);
}
}
十一、创建一个验证码过滤器,对指定URL进行过滤,验证码错误抛出异常,验证码正确则移除session中保存的验证码
该url也可以在application.properties指定(用逗号分割):zeke.security.code.image.url = /user/*,/userValidateCodeFilter:
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 存放所有需要拦截的URL
*/
private Set<String> urls = new HashSet<>();
private SecurityProperties securityProperties;
private AntPathMat
b372
cher pathMatcher = new AntPathMatcher();
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(),",");
for (String configUrl : configUrls) {
urls.add(configUrl);
}
urls.add("/authentication/form");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
boolean action = false;
for (String url : urls) {
if (pathMatcher.match(url,request.getRequestURI())){
action = true;
}
}
if (action){
try {
validate(new ServletWebRequest(request));
}
catch (ValidateCodeException e) {
authenticationFailureHandler.onAuthenticationFailure(request,response,e);
return;
}
}
filterChain.doFilter(request,response);
}
/**
* 校验提交验证码的合法性
* @param request
* @throws ServletRequestBindingException
*/
private void validate(ServletWebRequest request) throws ServletRequestBindingException {
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY);
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
if (StringUtils.isBlank(codeInRequest)){
throw new ValidateCodeException("验证码的值不能为空");
}
if (codeInSession == null){
throw new ValidateCodeException("验证码不存在");
}
if (codeInSession.isExpried()){
sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
throw new ValidateCodeException("验证码已过期");
}
if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)){
throw new ValidateCodeException("验证码不匹配");
}
sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);
}
public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
}
public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
}
public SessionStrategy getSessionStrategy() {
return sessionStrategy;
}
public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
}
public Set<String> getUrls() {
return urls;
}
public void setUrls(Set<String> urls) {
this.urls = urls;
}
public SecurityProperties getSecurityProperties() {
return securityProperties;
}
public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
十二、更改zeke-login.html中的表单,增加图形验证码<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Page</title>
</head>
<body>
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"/></td>
</tr>
<tr>
<td>图形验证码: </td>
<td>
<input type="text" name="imageCode"/>
<img src="/code/image"/>
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>
</body>
</html>
十三、在BrowserSecurityConfig中把 /code/image加入放行url ,不然页面会拦截/code/image的请求;
配置ValidateCodeFilter;
十四、启动项目访问localhost/zeke-login.html 测试
相关文章推荐
- SpringBoot结合SpringSecurity实现图形验证码功能
- SpringMVC+kaptcha实现图形验证码
- spring boot实现验证码功能
- SpringBoot 集成Kaptcha实现验证码功能实例详解
- SpringBoot+Security 发送短信验证码的实现
- 在springboot中使用google图形验证码Kaptcha
- Spring Boot中验证码实现kaptcha
- springboot+shiro实现验证码
- SpringBoot注册登录(二):注册---验证码kaptcha的实现
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十七):登录验证码实现(Captcha)
- SpringBoot+Shiro学习之“记住我”和“GIF验证码”功能的实现
- SpringBoot+Shiro学习之“记住我”和“GIF验证码”功能的实现
- Spring boot 集成 Kaptcha 实现前后端分离验证码功能
- spring boot下验证码的实现
- SpringBoot实现前端验证码图片生成和校验
- SpringBoot下实现前端验证码图片的生成和校验
- Spring Boot 实现 HTTPS
- 怎样借助Spring boot快速实现一个简单的http服务器
- Springboot 整合 Dubbo/ZooKeeper 实现 SOA 案例解析
- spring boot普通类调用bean实现