springsecurity之图片验证码教程在登录界面图形添加验证码开发生成图像验证码接口在认证流程中加入图像验证码校验重构代码开发生 成图像验证码接口思路:根据随机数生成图片将随机数存到session中将生成的图片写入响应中这里相当于功能,生成图片什么 的不记录了。网上一大堆,记录下这里的一些代码思路由于是公用的,把该服务写在core中图片验证码信息类?12345678910111 2131415161718192021222324252627282930packagecn.mrcode.imooc.spri ngsecurity.securitycore.validate.code;?importjava.awt.image.Buff eredImage;importjava.time.LocalDateTime;?/?图形验证码?@author: zhuqiang?@version:V1.0?@date:2018/8/322:44?/publiccla ssImageCode{?privateBufferedImageimage;?privateStringcode;? privateLocalDateTimeexpireTime;//过期时间??/?@paramimage?@ paramcode?@paramexpireIn过期时间,单位秒?/?publicImageCode(Buffere dImageimage,Stringcode,intexpireIn){?this.image=image;?th is.code=code;?this.expireTime=LocalDateTime.now().plusSeconds (expireIn);?}?//是否过期?publicbooleanisExpried(){returnthis.exp ireTime.isBefore(LocalDateTime.now());?}验证码服务?1234567891011121314 151617181920212223242526272829packagecn.mrcode.imooc.springsecur ity.securitycore.validate.code;/?验证码服务金星棋牌http://www.lrnfk.c om?@author:zhuqiang?@version:V1.0?@date:2018/8/322:4 8?/@RestControllerpublicclassValidateCodeController{?private staticfinalStringSESSION_KEY="SESSION_KEY_IMAGE_CODE";?//这里 又使用了spring的工具类来操作session?privateSessionStrategysessionStrategy =newHttpSessionSessionStrategy();??@GetMapping("/code/image")?p ublicvoidcreateCode(HttpServletRequestrequest,HttpServletResp onseresponse)throwsIOException{?ImageCodeimageCode=createI mageCode(request);?sessionStrategy.setAttribute(newServletWebReq uest(request),SESSION_KEY,imageCode);?response.setContentType(" image/jpeg");?//禁止图像缓存。?response.setHeader("Pragma","no-cache"); ?response.setHeader("Cache-Control","no-cache");?ImageIO.write(i mageCode.getImage(),"JPEG",response.getOutputStream());?}??priv ateImageCodecreateImageCode(HttpServletRequestrequest)throws IOException{?Stringcode=RandomStringUtils.randomAlphanumeric( 4);?BufferedImageimage=createImageCode(80,40,code);?returnn ewImageCode(image,code,60);?}需要把该服务路径在cn.mrcode.imooc.springse curity.securitybrowser.BrowserSecurityConfig中配置放行。在认证流程中加入图像验证码校 验步骤:由之前的源码的探索发现,只要把过滤器添加到spring现有的过滤器链上就可以了;编写验证码过滤器放在UsernamePa sswordAuthenticationFilter过滤器之前?123456789101112131415161718192021 22232425262728293031323334353637383940414243444546474849505152535 45556575859606162636465666768697071727374757677packagecn.m rcode.imooc.springsecurity.securitycore.validate.code;?importorg .apache.commons.lang3.StringUtils;importorg.springframework.secu rity.web.authentication.AuthenticationFailureHandler;importorg.s pringframework.social.connect.web.HttpSessionSessionStrategy;impo rtorg.springframework.social.connect.web.SessionStrategy;import org.springframework.web.bind.ServletRequestBindingException;impor torg.springframework.web.bind.ServletRequestUtils;importorg.spr ingframework.web.context.request.ServletWebRequest;importorg.spr ingframework.web.filter.OncePerRequestFilter;?importjavax.servle t.FilterChain;importjavax.servlet.ServletException;importjavax. servlet.http.HttpServletRequest;importjavax.servlet.http.HttpSer vletResponse;importjava.io.IOException;?/?图片验证码验证过滤器?OnceP erRequestFilterspring提供的,保证在一个请求中只会被调用一次?@author:zhuqiang? @version:V1.0?@date:2018/8/323:24?/publicclassValidateC odeFilterextendsOncePerRequestFilter{?//在初始化本类的地方进行注入?//一般在配 置securityhttp的地方进行添加过滤器?privateAuthenticationFailureHandlerfai lureHandler;?privateSessionStrategysessionStrategy=newHttpSe ssionSessionStrategy();??@Override?protectedvoiddoFilterInterna l(HttpServletRequestrequest,HttpServletResponseresponse,Filte rChainfilterChain)throwsServletException,IOException{?//为登录 请求,并且为post请求?if(StringUtils.equals("/authentication/form",reque st.getRequestURI())?&&StringUtils.equalsAnyIgnoreCase(request.ge tMethod(),"post")){try{?validate(request);}catch(ValidateCod eExceptione){?failureHandler.onAuthenticationFailure(request,r esponse,e);?return;}?}?filterChain.doFilter(request,response);? }??privatevoidvalidate(HttpServletRequestrequest)throwsServl etRequestBindingException{?//拿到之前存储的imageCode信息?ServletWebReque stswr=newServletWebRequest(request);?ImageCodecodeInSession =(ImageCode)sessionStrategy.getAttribute(swr,ValidateCodeContr oller.SESSION_KEY);?//又是一个spring中的工具类,388棋牌http://www.455573.com ?//试问一下,如果不看源码怎么可能知道有这些工具类可用??StringcodeInRequest=ServletRequ estUtils.getStringParameter(request,"imageCode");??if(StringUti ls.isBlank(codeInRequest)){thrownewValidateCodeException("验证码的 值不能为空");?}?if(codeInSession==null){thrownewValidateCodeExce ption("验证码不存在");?}?if(codeInSession.isExpried()){sessionStrateg y.removeAttribute(swr,ValidateCodeController.SESSION_KEY);throw newValidateCodeException("验证码已过期");?}?if(!StringUtils.equals(co deInSession.getCode(),codeInRequest)){thrownewValidateCodeExc eption("验证码不匹配");?}?sessionStrategy.removeAttribute(swr,Validate CodeController.SESSION_KEY);?}??publicAuthenticationFailureHandl ergetFailureHandler(){?returnfailureHandler;?}??publicvoidse tFailureHandler(AuthenticationFailureHandlerfailureHandler){?th is.failureHandler=failureHandler;?}} 把过滤器添加到现有认证流程中?12345 6789ValidateCodeFiltervalidateCodeFilter=newValidateCod eFilter();validateCodeFilter.setFailureHandler(myAuthenticationFa ilureHandler);http?//由源码得知,在最前面的是UsernamePasswordAuthenticationF ilter?.addFilterBefore(validateCodeFilter,UsernamePasswordAuthen ticationFilter.class)?//定义表单登录-身份认证的方式?.formLogin()?.loginPage ("/authentication/require")?.loginProcessingUrl("/authentication/ form") 还需要注意的一个地方就是myAuthenticationFailureHandler中。因为失败会调用这 个处理器;这里和视频中演示的不一样。不会再把异常信息打印到前段页面了。后补:视频中不知道什么时候把LoginType变成了json 类型,所以会抛出异常?12345678910111213if(securityProperties.getBrows er().getLoginType()==LoginType.JSON){?response.setContentType( "application/json;charset=UTF-8");?response.setStatus(HttpStatus. INTERNAL_SERVER_ERROR.value());?response.getWriter().write(object Mapper.writeValueAsString(exception));}else{?//在这里失败跳转不回去了。而且异 常信息也没有打印出来。父类默认打印了死的一句话天地棋牌http://www.dadiqipaigw.cn?//在这里就不往上面扔了,这里就先当做defaultFailureUrl不存在吧?//模拟打印异常信息?response.setContentType("text/html;charset=UTF-8");?response.sendError(HttpStatus.UNAUTHORIZED.value(),exception.getLocalizedMessage());//super.onAuthenticationFailure(request,response,exception);} 注意:之前我一直在说,异常了但是不会显示,不知道去哪里了。这几次调试发现日志不断访问/error,好像这个在自己设置了BrowserSecurityConfig的拦截放行路径后,就一致这样了 |
|