Springboot+Springsecurity实现图⽚验证码验证
最近⼀个项⽬做权限管理使⽤了 Spring boot+ Spring security +oauth2.0 ,但是产品设计登录界⾯时添加了 图⽚验证码功能,
要知道 spring security 在登录时 只需要输⼊账号密码即可,所以刚开始为了⽅便在⾃定义的登录页⾯使⽤jQuery.submit()⽅法拦截了表单提交时间,在其中进⾏验证码的验证,但是测试⼈员测试后说存在暴⼒破解和撞库风险,需要账号,密码与验证码同时提交验证,并作出登录错误次数限制,所以只能想其他解决办法。
验证码实现:s
这⾥简单叙述⼀下spring security的登录验证机制:
Spring security使⽤众多的过滤器对url进⾏拦截,以此来进⾏权限管理。Spring security不允许我们修改默认的filter实现,但是可以加⼊⾃⼰的filter。登录验证的流程是,⽤户登陆会被AuthenticationProcessingFilter拦截,调⽤AuthenticationManager的实现,⽽AuthenticationManager会调⽤ProviderManager来获取⽤户验证信息。如果验证通过会将⽤户的权限信息封装成User对象放到spring 的全局缓存SecurityContextHolder中,以备后⾯访问资源时使⽤。忽略验证⽤户信息的部分,我们可以通过AuthenticationProcessingFilter来检验验证码,并达到验证失败时拒绝⽤户登录的⽬的。
⾃定义过滤器:
@Log
public class CaptchaAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private String processUrl;
private MyAuthenctiationFailureHandler myAuthenctiationFailureHandler;
public CaptchaAuthenticationFilter(String defaultFilterProcessesUrl,MyAuthenctiationFailureHandler myAuthenctiationFailureHandler) {
super(defaultFilterProcessesUrl);
this.processUrl = defaultFilterProcessesUrl;
setAuthenticationFailureHandler(myAuthenctiationFailureHandler);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (processUrl.ServletPath()) && "POST".Method())) {
Object expect = Session().getAttribute(Oauth2Const.VERIFYCODE_SESSION_KEY);
String code = Parameter("code");
log.info("========expect: " + expect + " code:" + code);
try {
validImage(req, res, code, expect);
} catch (AuthenticationException e) {
return;
}
}
chain.doFilter(request, response);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws AuthenticationException {
return null;
}
/**
* @param
* @param
* @param
* @param code    获取的验证码参数
* @param verCode session中保存的验证码
* @throws IOException
* @throws ServletException
*/
public void validImage(HttpServletRequest req, HttpServletResponse res, String code, Object verCode)  {
String verCodeStr;
if (null == verCode) {
throw new InsufficientAuthenticationException(Oauth2Const.VERIFYCODE_FAILURE);
} else {
verCodeStr = String();
}
LocalDateTime localDateTime = (LocalDateTime) Session().getAttribute("codeTime");
long past = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long now = w().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
if (verCodeStr == null || code == null || code.isEmpty() || !verCodeStr.equalsIgnoreCase(code)) {
throw new InsufficientAuthenticationException(Oauth2Const.VERIFYCODE_ERROR);
} else if ((now - past) / 1000 / 60 > 2) {//两分钟
throw new InsufficientAuthenticationException(Oauth2Const.VERIFYCODE_EXPIRED);
} else {
//验证成功,删除存储的验证码
}
}
}
}
processUrl是spring security拦截的请求地址,failureUrl是验证失败时的跳转地址。在⽣成验证码的时候,要将验证码存到session中。验证时从session中获取验证码,并将session中的验证码移除,否则可以重复登录(使⽤浏览器的重发功能)。
MyAuthenctiationFailureHandler :⾃定义登录失败处理器。
void validImage(HttpServletRequest req, HttpServletResponse res, String code, Object verCode)该⽅法是图⽚验证码验证⽅法。
unsuccessfulAuthentication(req, res, new InsufficientAuthenticationException(Oauth2Const.VERIFYCODE_ERROR)); 验证失败会执⾏MyAuthenctiationFailureHandler 中的 onAuthenticationFailure ()⽅法。
然后将⾃⼰的过滤器加到spring security的过滤器链中。如:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(new CaptchaAuthenticationFilter("/login", myAuthenctiationFailureHandler), UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/").hasRole("USER")
.antMatchers("/index").hasRole("USER")
.antMatchers("/message/*").hasRole("USER")
jquery怎么进行验证
.anyRequest().permitAll()
.
and().formLogin().loginPage("/login").defaultSuccessUrl("/index").failureUrl("/login?error1").permitAll()
.and().rememberMe().tokenValiditySeconds(60*60*7).key("message")
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll();
}
}
贴⼀张图⽚⼤家就⼀⽬了然了,我们是在UsernamePasswordAuthenticationFilter 之前插⼊了我们⾃定义的过滤器,先进⾏图⽚验证码的验证。验证成功 后继续进⾏账号密码的登录认证。
账号锁定这⾥给⼀个实现思路吧,在⾃定义登录失败处理器MyAuthenctiationFailureHandler中,记录账号异常信息 “Bad credentials”  的次数,可以保存在redis中设置过期时间,超过⼀定次数 修改 ⽤户状态,进⾏锁定。这样可以结局暴⼒撞库的问题,提⾼系统安全性。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。