图片验证码
- 图片验证码生成接口
- 认证流程加入图片验证码校验
- 图片验证码重构
1.图片验证码生成接口
- 调用com.google.code.kaptcha.Producer生成图片验证码
- 将随机数存到session缓存中
- 将生成的图片写到响应流中
图片验证码封装类 ImageCaptchaVo
-
package com.rui.tiger.auth.core.captcha;
-
-
import lombok.Data;
-
-
import java.awt.image.BufferedImage;
-
import java.time.LocalDateTime;
-
-
/**
-
* 图片验证码信息对象
-
* @author CaiRui
-
* @Date 2018/12/9 18:03
-
*/
-
@Data
-
public
class ImageCaptchaVo {
-
/**
-
* 图片验证码
-
*/
-
private BufferedImage image;
-
/**
-
* 验证码
-
*/
-
private String code;
-
/**
-
* 失效时间 这个通常放在缓存中或维护在数据库中
-
*/
-
private LocalDateTime expireTime;
-
-
public ImageCaptchaVo(BufferedImage image, String code, int expireAfterSeconds){
-
this.image = image;
-
this.code = code;
-
//多少秒后
-
this.expireTime = LocalDateTime.now().plusSeconds(expireAfterSeconds);
-
}
-
-
public ImageCaptchaVo(BufferedImage image, String code, LocalDateTime expireTime){
-
this.image = image;
-
this.code = code;
-
this.expireTime = expireTime;
-
}
-
-
/**
-
* 是否失效
-
* @return
-
*/
-
public boolean isExpried() {
-
return LocalDateTime.now().isAfter(expireTime);
-
}
-
-
}
图片验证码服务类 CaptchaController 注意这个路径/captcha/image要在BrowserSecurityConfig配置中放行
-
package com.rui.tiger.auth.core.captcha;
-
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
-
import org.springframework.social.connect.web.SessionStrategy;
-
import org.springframework.web.bind.annotation.GetMapping;
-
import org.springframework.web.bind.annotation.RestController;
-
import org.springframework.web.context.request.ServletWebRequest;
-
-
import javax.imageio.ImageIO;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
import java.io.IOException;
-
-
/**
-
* 验证码控制器
-
* @author CaiRui
-
* @date 2018-12-10 12:13
-
*/
-
@RestController
-
public
class CaptchaController {
-
-
public
static
final String IMAGE_CAPTCHA_SESSION_KEY=
"image_captcha_session_key";
-
private
static
final String FORMAT_NAME=
"JPEG";
-
-
@Autowired
-
private CaptchaGenerate captchaGenerate;
-
//spring session 工具类
-
private SessionStrategy sessionStrategy=
new HttpSessionSessionStrategy();
-
-
/**
-
* 获取图片验证码
-
* @param request
-
* @param response
-
* @throws IOException
-
*/
-
@GetMapping(
"/captcha/image")
-
public void createKaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
-
//1.接口生成验证码
-
ImageCaptchaVo imageCaptcha= (ImageCaptchaVo) captchaGenerate.generate();
-
//2.保存到session中
-
sessionStrategy.setAttribute(
new ServletWebRequest(request), IMAGE_CAPTCHA_SESSION_KEY, imageCaptcha);
-
//3.写到响应流中
-
response.setHeader(
"Cache-Control",
"no-store, no-cache");
// 没有缓存
-
response.setContentType(
"image/jpeg");
-
ImageIO.write(imageCaptcha.getImage(),FORMAT_NAME,response.getOutputStream());
-
}
-
-
}
CaptchaGenerate 接口及其实现类
-
package com.rui.tiger.auth.core.captcha;
-
-
/**
-
* 验证码生成接口
-
*
-
* @author CaiRui
-
* @date 2018-12-10 12:03
-
*/
-
public
interface CaptchaGenerate {
-
/**
-
* 生成图片验证码
-
*
-
* @return
-
*/
-
ImageCaptchaVo generate();
-
}
-
package com.rui.tiger.auth.core.captcha;
-
-
import com.google.code.kaptcha.Producer;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.stereotype.Service;
-
-
import java.awt.image.BufferedImage;
-
-
/**
-
* 图片验证码生成接口
-
*
-
* @author CaiRui
-
* @date 2018-12-10 12:07
-
*/
-
@Service(
"imageCaptchaGenerate")
-
public
class ImageCaptchaGenerate implements CaptchaGenerate {
-
-
@Autowired
-
private Producer producer;
//config bean中配置
-
-
@Override
-
public ImageCaptchaVo generate() {
-
String code = producer.createText();
-
BufferedImage bufferedImage = producer.createImage(code);
-
return
new ImageCaptchaVo(bufferedImage, code,
60 *
5);
//5分钟过期
-
}
-
}
Producer配置类 KaptchaGenerateConfig
-
package com.rui.tiger.auth.core.config;
-
-
import com.google.code.kaptcha.impl.DefaultKaptcha;
-
import com.google.code.kaptcha.util.Config;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
-
import java.util.Properties;
-
-
/**
-
* 验证码生成配置类
-
* @author CaiRui
-
* @date 2018-12-10 12:09
-
*/
-
@Configuration
-
public
class KaptchaGenerateConfig {
-
-
//TODO 配置项放在配置文件中
-
@Bean
-
public DefaultKaptcha producer() {
-
Properties properties =
new Properties();
-
properties.put(
"kaptcha.border",
"no");
-
properties.put(
"kaptcha.textproducer.font.color",
"black");
-
properties.put(
"kaptcha.textproducer.char.space",
"5");
-
Config config =
new Config(properties);
-
DefaultKaptcha defaultKaptcha =
new DefaultKaptcha();
-
defaultKaptcha.setConfig(config);
-
return defaultKaptcha;
-
}
-
}
2.认证流程加入图片验证码校验
通过上篇的源码分析spring-security原理,其实就是过滤器链上的各个过滤器协同工作,思路如下:
- 编写我们的自定义图片验证码过滤器
- 将它放在UsernamePasswordAuthenticationFilter表单过滤器之前
验证码过滤器
-
package com.rui.tiger.auth.core.captcha;
-
-
import lombok.Getter;
-
import lombok.Setter;
-
import org.apache.commons.lang.StringUtils;
-
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
-
import org.springframework.social.connect.web.SessionStrategy;
-
import org.springframework.web.bind.ServletRequestBindingException;
-
import org.springframework.web.bind.ServletRequestUtils;
-
import org.springframework.web.context.request.ServletWebRequest;
-
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 java.io.IOException;
-
-
/**
-
* 图片验证码过滤器
-
* OncePerRequestFilter 过滤器只会调用一次
-
*
-
* @author CaiRui
-
* @date 2018-12-10 12:23
-
*/
-
public
class CaptchaFilter extends OncePerRequestFilter {
-
-
//一般在配置类中进行注入
-
@Setter
-
@Getter
-
private AuthenticationFailureHandler failureHandler;
-
-
private SessionStrategy sessionStrategy =
new HttpSessionSessionStrategy();
-
-
@Override
-
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
-
//表单登录的post请求
-
if (StringUtils.equals(
"/authentication/form", request.getRequestURI())
-
&& StringUtils.equalsIgnoreCase(
"post", request.getMethod())) {
-
try {
-
validate(request);
-
}
catch (CaptchaException captchaException) {
-
//失败调用我们的自定义失败处理器
-
failureHandler.onAuthenticationFailure(request, response, captchaException);
-
//后续流程终止
-
return;
-
}
-
}
-
filterChain.doFilter(request, response);
-
-
}
-
-
/**
-
* 图片验证码校验
-
*
-
* @param request
-
*/
-
private void validate(HttpServletRequest request) throws ServletRequestBindingException {
-
// 拿到之前存储的imageCode信息
-
ServletWebRequest swr =
new ServletWebRequest(request);
-
ImageCaptchaVo imageCodeInSession = (ImageCaptchaVo) sessionStrategy.getAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
-
String codeInRequest = ServletRequestUtils.getStringParameter(request,
"imageCode");
-
-
if (StringUtils.isBlank(codeInRequest)) {
-
throw
new CaptchaException(
"验证码的值不能为空");
-
}
-
if (imageCodeInSession ==
null) {
-
throw
new CaptchaException(
"验证码不存在");
-
}
-
if (imageCodeInSession.isExpried()) {
-
sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
-
throw
new CaptchaException(
"验证码已过期");
-
}
-
if (!StringUtils.equals(imageCodeInSession.getCode(), codeInRequest)) {
-
throw
new CaptchaException(
"验证码不匹配");
-
}
-
//验证通过 移除缓存
-
sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
-
}
-
}
自定义验证码异常
-
package com.rui.tiger.auth.core.captcha;
-
-
import org.springframework.security.core.AuthenticationException;
-
-
/**
-
* 自定义验证码异常
-
* @author CaiRui
-
* @date 2018-12-10 12:43
-
*/
-
public
class CaptchaException extends AuthenticationException {
-
-
public CaptchaException(String msg, Throwable t) {
-
super(msg, t);
-
}
-
-
public CaptchaException(String msg) {
-
super(msg);
-
}
-
}
将过滤器加入到浏览器权限配置中
-
package com.rui.tiger.auth.browser.config;
-
-
import com.rui.tiger.auth.core.authentication.TigerAuthenticationFailureHandler;
-
import com.rui.tiger.auth.core.authentication.TigerAuthenticationSuccessHandler;
-
import com.rui.tiger.auth.core.captcha.CaptchaFilter;
-
import com.rui.tiger.auth.core.properties.SecurityProperties;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
import org.springframework.security.crypto.password.PasswordEncoder;
-
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-
-
/**
-
* 浏览器security配置类
-
*
-
* @author CaiRui
-
* @date 2018-12-4 8:41
-
*/
-
@Configuration
-
public
class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
-
-
@Autowired
-
private SecurityProperties securityProperties;
-
@Autowired
-
private TigerAuthenticationFailureHandler tigerAuthenticationFailureHandler;
-
@Autowired
-
private TigerAuthenticationSuccessHandler tigerAuthenticationSuccessHandler;
-
-
/**
-
* 密码加密解密
-
*
-
* @return
-
*/
-
@Bean
-
public PasswordEncoder passwordEncoder() {
-
return
new BCryptPasswordEncoder();
-
}
-
-
@Override
-
protected void configure(HttpSecurity http) throws Exception {
-
//加入图片验证码过滤器
-
CaptchaFilter captchaFilter=
new CaptchaFilter();
-
captchaFilter.setFailureHandler(tigerAuthenticationFailureHandler);
-
//图片验证码放在认证之前
-
http.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)
-
.formLogin()
-
.loginPage(
"/authentication/require")
//自定义登录请求
-
.loginProcessingUrl(
"/authentication/form")
//自定义登录表单请求
-
.successHandler(tigerAuthenticationSuccessHandler)
-
.failureHandler(tigerAuthenticationFailureHandler)
-
.and()
-
.authorizeRequests()
-
.antMatchers(securityProperties.getBrowser().getLoginPage(),
-
"/authentication/require",
"/captcha/image")
//此路径放行 否则会陷入死循环
-
.permitAll()
-
.anyRequest()
-
.authenticated()
-
.and()
-
.csrf().disable()
//跨域关闭
-
;
-
}
-
-
}
前段标准登录界面加入验证码改造
-
<html>
-
<head>
-
<meta charset="UTF-8">
-
<title>登录
</title>
-
</head>
-
<body>
-
<h2>标准登录页面
</h2>
-
<h3>表单登录
</h3>
-
<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="/captcha/image">
-
</td>
-
</tr>
-
<tr>
-
<td colspan="2">
<button type="submit">登录
</button>
</td>
-
</tr>
-
</table>
-
</form>
-
</body>
-
</html>
ok 我们项目重新启动下 来看下自定义的验证码过滤器是否可用,直接浏览器输入我们的登录地址http://localhost:8070/tiger-login.html
可以看到图片验证码已经成功显示出来了,我们来看看验证逻辑是否可用,试下不输入验证码
这是因为我们自定义的失败处理器,打印了全部的错误堆栈信息我们来调整下,调整后如下
-
package com.rui.tiger.auth.core.authentication;
-
-
import com.alibaba.fastjson.JSON;
-
import com.rui.tiger.auth.core.model.enums.LoginTypeEnum;
-
import com.rui.tiger.auth.core.properties.SecurityProperties;
-
import com.rui.tiger.auth.core.support.SimpleResponse;
-
import lombok.extern.slf4j.Slf4j;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.http.HttpStatus;
-
import org.springframework.security.core.AuthenticationException;
-
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
-
import org.springframework.stereotype.Component;
-
-
import javax.servlet.ServletException;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
import java.io.IOException;
-
-
/**
-
* 认证失败处理器
-
* @author CaiRui
-
* @date 2018-12-6 12:40
-
*/
-
@Component(
"tigerAuthenticationFailureHandler")
-
@Slf4j
-
public
class TigerAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
-
-
@Autowired
-
private SecurityProperties securityProperties;
-
-
@Override
-
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
-
log.info(
"登录失败");
-
if (LoginTypeEnum.JSON.equals(securityProperties.getBrowser().getLoginType())) {
-
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
-
response.setContentType(
"application/json;charset=UTF-8");
-
response.getWriter().write(JSON.toJSONString(
new SimpleResponse(exception.getMessage())));
-
}
else {
-
// 如果用户配置为跳转,则跳到Spring Boot默认的错误页面
-
super.onAuthenticationFailure(request, response, exception);
-
}
-
-
}
-
}
这次我们试下一个错误的验证码登录看下
ok 我们的自定义验证码生效,其它的情况可以自行调试 下面我们将对验证码进行重构
3.图片验证码重构
- 验证码生成的基本参数可以配置
- 验证码拦截的接口可以配置
- 验证码的生成逻辑可以配置
3.1验证码生成的基本参数可以配置
图片验证码基本配置类
-
package com.rui.tiger.auth.core.properties;
-
-
/**
-
* 图片验证码配置类,根据情况添加具体配置项
-
*
-
* @author CaiRui
-
* @date 2018-12-11 9:02
-
* @see com.google.code.kaptcha.Constants
-
*/
-
public
class ImageCaptchaProperties {
-
-
/**
-
* 验证码长度
-
*/
-
private
int size =
4;
-
-
/**
-
* 宽度
-
*/
-
private
int width;
-
/**
-
* 高度
-
*/
-
private
int height;
-
/**
-
* 失效秒数
-
*/
-
private
int expireAfterSecondes =
3 *
60;
//默认三分钟
-
/**
-
* 验证码拦截的路径 多个路径以,(逗号)进行分割
-
*/
-
private String interceptImageUrl;
-
-
-
-
public int getSize() {
-
return size;
-
}
-
-
public void setSize(int size) {
-
this.size = size;
-
}
-
-
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 getExpireAfterSecondes() {
-
return expireAfterSecondes;
-
}
-
-
public void setExpireAfterSecondes(int expireAfterSecondes) {
-
this.expireAfterSecondes = expireAfterSecondes;
-
}
-
-
public String getInterceptImageUrl() {
-
return interceptImageUrl;
-
}
-
-
public void setInterceptImageUrl(String interceptImageUrl) {
-
this.interceptImageUrl = interceptImageUrl;
-
}
-
-
}
加入到总配置类中
-
package com.rui.tiger.auth.core.properties;
-
-
import org.springframework.boot.context.properties.ConfigurationProperties;
-
-
/**
-
* 权限配置文件父类(注意这里不用lombok 会读取不到)
-
* 这里会有很多权限配置子模块
-
*
-
* @author CaiRui
-
* @date 2018-12-6 8:41
-
*/
-
-
@ConfigurationProperties(value =
"tiger.auth", ignoreInvalidFields =
true)
-
public
class SecurityProperties {
-
-
/**
-
* 浏览器配置类
-
*/
-
private BrowserProperties browser =
new BrowserProperties();
-
/**
-
* 图片验证码配置类
-
*/
-
private ImageCaptchaProperties imageCaptcha =
new ImageCaptchaProperties();
-
-
-
public BrowserProperties getBrowser() {
-
return browser;
-
}
-
-
public void setBrowser(BrowserProperties browser) {
-
this.browser = browser;
-
}
-
-
public ImageCaptchaProperties getImageCaptcha() {
-
return imageCaptcha;
-
}
-
-
public void setImageCaptcha(ImageCaptchaProperties imageCaptcha) {
-
this.imageCaptcha = imageCaptcha;
-
}
-
}
验证码生成参数从配置中获取
-
package com.rui.tiger.auth.core.config;
-
-
import com.google.code.kaptcha.Constants;
-
import com.google.code.kaptcha.impl.DefaultKaptcha;
-
import com.google.code.kaptcha.util.Config;
-
import com.rui.tiger.auth.core.properties.SecurityProperties;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
-
import java.util.Properties;
-
-
/**
-
* 验证码生成配置类
-
*
-
* @author CaiRui
-
* @date 2018-12-10 12:09
-
* @see Constants
-
*/
-
@Configuration
-
public
class CaptchaGenerateConfig {
-
-
@Autowired
-
private SecurityProperties securityProperties;
//验证码配置获取
-
-
// 参见 https://blog.csdn.net/larger5/article/details/79522105 DefaultKaptcha配置生成
-
@Bean
-
public DefaultKaptcha producer() {
-
Properties properties =
new Properties();
-
properties.put(
"kaptcha.border",
"no");
-
//常量配置Constants和直接字符串配置都可以
-
properties.put(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR,
"black");
-
//验证码长度 put 不生效 注意
-
//properties.put("kaptcha.textproducer.char.length", securityProperties.getImageCaptcha().getSize());
-
properties.setProperty(
"kaptcha.textproducer.char.length", String.valueOf(securityProperties.getImageCaptcha().getSize()));
-
-
Config config =
new Config(properties);
-
DefaultKaptcha defaultKaptcha =
new DefaultKaptcha();
-
defaultKaptcha.setConfig(config);
-
return defaultKaptcha;
-
}
-
}
3.2 验证码拦截的接口可以配置
过滤器加入拦截的url匹配,对原来的进行重构
-
package com.rui.tiger.auth.core.captcha;
-
-
import com.rui.tiger.auth.core.properties.SecurityProperties;
-
import lombok.Getter;
-
import lombok.Setter;
-
import lombok.extern.slf4j.Slf4j;
-
import org.apache.commons.lang.StringUtils;
-
import org.springframework.beans.factory.InitializingBean;
-
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
-
import org.springframework.social.connect.web.SessionStrategy;
-
import org.springframework.util.AntPathMatcher;
-
import org.springframework.web.bind.ServletRequestBindingException;
-
import org.springframework.web.bind.ServletRequestUtils;
-
import org.springframework.web.context.request.ServletWebRequest;
-
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 java.io.IOException;
-
import java.util.HashSet;
-
import java.util.Set;
-
import java.util.stream.Collectors;
-
import java.util.stream.Stream;
-
-
/**
-
* 图片验证码过滤器
-
* OncePerRequestFilter 过滤器只会调用一次
-
*
-
* @author CaiRui
-
* @date 2018-12-10 12:23
-
*/
-
@Setter
-
@Getter
-
@Slf4j
-
public
class CaptchaFilter extends OncePerRequestFilter implements InitializingBean {
-
-
//一般在配置类中进行注入
-
-
private AuthenticationFailureHandler failureHandler;
-
-
private SecurityProperties securityProperties;
-
-
/**
-
* 验证码拦截的路径
-
*/
-
private Set<String> interceptUrlSet =
new HashSet<>();
-
-
//session工具类
-
private SessionStrategy sessionStrategy =
new HttpSessionSessionStrategy();
-
//路径匹配工具类
-
private AntPathMatcher antPathMatcher =
new AntPathMatcher();
-
-
/**
-
* @throws ServletException
-
*/
-
-
@Override
-
public void afterPropertiesSet() throws ServletException {
-
super.afterPropertiesSet();
-
//其它配置的需要验证码验证的路径
-
String configInterceptUrl = securityProperties.getImageCaptcha().getInterceptImageUrl();
-
if (StringUtils.isNotBlank(configInterceptUrl)) {
-
String[] configInterceptUrlArray = StringUtils.split(configInterceptUrl,
",");
-
interceptUrlSet = Stream.of(configInterceptUrlArray).collect(Collectors.toSet());
-
}
-
//登录请求验证
-
interceptUrlSet.add(
"/authentication/form");
-
}
-
-
@Override
-
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
-
-
log.info(
"验证码验证请求路径:[{}]", request.getRequestURI());
-
boolean action =
false;
// 默认不放行
-
for (String url : interceptUrlSet) {
-
if (antPathMatcher.match(url, request.getRequestURI())) {
-
action =
true;
-
}
-
}
-
if (action) {
-
try {
-
validate(request);
-
}
catch (CaptchaException captchaException) {
-
//失败调用我们的自定义失败处理器
-
failureHandler.onAuthenticationFailure(request, response, captchaException);
-
//后续流程终止
-
return;
-
}
-
-
}
-
//后续过滤器继续执行
-
filterChain.doFilter(request, response);
-
}
-
-
/**
-
* 图片验证码校验
-
*
-
* @param request
-
*/
-
private void validate(HttpServletRequest request) throws ServletRequestBindingException {
-
// 拿到之前存储的imageCode信息
-
ServletWebRequest swr =
new ServletWebRequest(request);
-
ImageCaptchaVo imageCodeInSession = (ImageCaptchaVo) sessionStrategy.getAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
-
String codeInRequest = ServletRequestUtils.getStringParameter(request,
"imageCode");
-
-
if (StringUtils.isBlank(codeInRequest)) {
-
throw
new CaptchaException(
"验证码的值不能为空");
-
}
-
if (imageCodeInSession ==
null) {
-
throw
new CaptchaException(
"验证码不存在");
-
}
-
if (imageCodeInSession.isExpried()) {
-
sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
-
throw
new CaptchaException(
"验证码已过期");
-
}
-
if (!StringUtils.equals(imageCodeInSession.getCode(), codeInRequest)) {
-
throw
new CaptchaException(
"验证码不匹配");
-
}
-
//验证通过 移除缓存
-
sessionStrategy.removeAttribute(swr, CaptchaController.IMAGE_CAPTCHA_SESSION_KEY);
-
}
-
}
配置类中加入调整后的过滤器相关配置
-
package com.rui.tiger.auth.browser.config;
-
-
import com.rui.tiger.auth.core.authentication.TigerAuthenticationFailureHandler;
-
import com.rui.tiger.auth.core.authentication.TigerAuthenticationSuccessHandler;
-
import com.rui.tiger.auth.core.captcha.CaptchaFilter;
-
import com.rui.tiger.auth.core.properties.SecurityProperties;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
import org.springframework.security.crypto.password.PasswordEncoder;
-
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-
-
/**
-
* 浏览器security配置类
-
*
-
* @author CaiRui
-
* @date 2018-12-4 8:41
-
*/
-
@Configuration
-
public
class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
-
-
@Autowired
-
private SecurityProperties securityProperties;
-
@Autowired
-
private TigerAuthenticationFailureHandler tigerAuthenticationFailureHandler;
-
@Autowired
-
private TigerAuthenticationSuccessHandler tigerAuthenticationSuccessHandler;
-
-
/**
-
* 密码加密解密
-
*
-
* @return
-
*/
-
@Bean
-
public PasswordEncoder passwordEncoder() {
-
return
new BCryptPasswordEncoder();
-
}
-
-
@Override
-
protected void configure(HttpSecurity http) throws Exception {
-
//加入图片验证码过滤器
-
CaptchaFilter captchaFilter =
new CaptchaFilter();
-
captchaFilter.setFailureHandler(tigerAuthenticationFailureHandler);
-
captchaFilter.setSecurityProperties(securityProperties);
-
captchaFilter.afterPropertiesSet();
-
-
//图片验证码放在认证之前
-
http.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)
-
.formLogin()
-
.loginPage(
"/authentication/require")
//自定义登录请求
-
.loginProcessingUrl(
"/authentication/form")
//自定义登录表单请求
-
.successHandler(tigerAuthenticationSuccessHandler)
-
.failureHandler(tigerAuthenticationFailureHandler)
-
.and()
-
.authorizeRequests()
-
.antMatchers(securityProperties.getBrowser().getLoginPage(),
-
"/authentication/require",
"/captcha/image")
//此路径放行 否则会陷入死循环
-
.permitAll()
-
.anyRequest()
-
.authenticated()
-
.and()
-
.csrf().disable()
//跨域关闭
-
;
-
}
-
-
}
测试配置文件调整如下
-
#数据源
-
spring:
-
datasource:
-
driverClassName: com.mysql.jdbc.Driver
-
url:
jdbc:
mysql:/
/my.yunout.com:3306/tiger_study?allowMultiQueries=
true&useUnicode=
true&characterEncoding=UTF-
8
-
username: root
-
password: root
-
# 配置Druid连接池
-
type: com.alibaba.druid.pool.DruidDataSource
-
session:
-
store-
type: none
-
-
# Tomcat
-
server:
-
port:
8070
-
connection-
timeout:
5000ms
-
-
#自定义权限配置
-
tiger:
-
auth:
-
browser:
-
#loginPage: /demo-login.html # 这里可以配置成自己的非标准登录界面
-
loginType: JSON
-
imageCaptcha:
-
interceptImageUrl: /user/*,
/pay/confirm
# 这些路径验证码也要拦截校验
ok 我们来测试下 是否可用
3.3 验证码的生成逻辑可以配置
主要是利用@Conditional系列注解,可参看@Conditional系列注解
-
package com.rui.tiger.auth.core.config;
-
-
import com.google.code.kaptcha.Producer;
-
import com.rui.tiger.auth.core.captcha.CaptchaGenerate;
-
import com.rui.tiger.auth.core.captcha.ImageCaptchaGenerate;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
-
/**
-
* 验证码Bean生成配置类
-
*
-
* @author CaiRui
-
* @date 2018-12-12 8:41
-
*/
-
@Configuration
-
public
class CaptchaGenerateBeanConfig {
-
-
@Bean
-
// spring 容器中如果存在imageCaptchaGenerate的bean就不会再初始化该bean了
-
//可参见:https://www.cnblogs.com/yixianyixian/p/7346894.html 这篇博文
-
@ConditionalOnMissingBean(name =
"imageCaptchaGenerate")
-
public CaptchaGenerate imageCaptchaGenerate() {
-
ImageCaptchaGenerate imageCaptchaGenerate =
new ImageCaptchaGenerate();
-
return imageCaptchaGenerate;
-
}
-
-
}
原来的service去掉注解
-
package com.rui.tiger.auth.core.captcha;
-
-
import com.google.code.kaptcha.Producer;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.stereotype.Service;
-
-
import java.awt.image.BufferedImage;
-
-
/**
-
* 图片验证码生成接口
-
*
-
* @author CaiRui
-
* @date 2018-12-10 12:07
-
*/
-
//@Service("imageCaptchaGenerate")
-
-
public
class ImageCaptchaGenerate implements CaptchaGenerate {
-
-
@Autowired
-
private Producer producer;
//config bean中配置
-
-
@Override
-
public ImageCaptchaVo generate() {
-
String code = producer.createText();
-
BufferedImage bufferedImage = producer.createImage(code);
-
return
new ImageCaptchaVo(bufferedImage, code,
60 *
5);
//5分钟过期
-
}
-
}
demo项目中写个测试类看是否生效,这里我们不生成,只是打印下日志
-
package com.rui.tiger.auth.demo.service;
-
-
import com.rui.tiger.auth.core.captcha.CaptchaGenerate;
-
import com.rui.tiger.auth.core.captcha.ImageCaptchaVo;
-
import lombok.extern.slf4j.Slf4j;
-
import org.springframework.stereotype.Component;
-
-
/**
-
* @author CaiRui
-
* @date 2018-12-12 9:05
-
*/
-
@Component(
"imageCaptchaGenerate")
-
@Slf4j
-
public
class DemoImageCaptchaGenerateTest implements CaptchaGenerate {
-
-
/**
-
* 测试覆盖 测试通过将其@Component去掉 保证正常流程执行
-
*
-
* @return
-
* @see com.rui.tiger.auth.core.config.CaptchaGenerateBeanConfig
-
*/
-
@Override
-
public ImageCaptchaVo generate() {
-
log.info(
"自定义验证码实现 覆盖原始的生成测试");
-
return
null;
-
}
-
}
文章转载至:https://blog.csdn.net/ahcr1026212/article/details/84949389