Spring Security实现验证码登录功能
这篇文章主要介绍了SpringSecurity实现验证码登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
在springsecurity实现登录注销功能的基础上进行开发。
1、添加生成验证码的控制器。
(1)、生成验证码
/** *引入Security配置属性类 */ @Autowired privateSecurityPropertiessecurityProperties; @Override publicImageCodecreateCode(HttpServletRequestrequest){ //如果请求中有width参数,则用请求中的,否则用配置属性中的 intwidth=ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth()); //高度(宽度) intheight=ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight()); //图片验证码字符个数 intlength=securityProperties.getLength(); //过期时间 intexpireIn=securityProperties.getExpireIn(); BufferedImageimage=newBufferedImage(width,height,BufferedImage.TYPE_INT_RGB); Graphicsg=image.getGraphics(); Randomrandom=newRandom(); g.setColor(getRandColor(200,250)); g.fillRect(0,0,width,height); g.setFont(newFont("TimesNewRoman",Font.ITALIC,20)); g.setColor(getRandColor(160,200)); for(inti=0;i<155;i++){ intx=random.nextInt(width); inty=random.nextInt(height); intxl=random.nextInt(12); intyl=random.nextInt(12); g.drawLine(x,y,x+xl,y+yl); } StringsRand=""; for(inti=0;i255){ fc=255; } if(bc>255){ bc=255; } intr=fc+random.nextInt(bc-fc); intg=fc+random.nextInt(bc-fc); intb=fc+random.nextInt(bc-fc); returnnewColor(r,g,b); }
(2)、验证码控制器
publicstaticfinalStringSESSION_KEY="SESSION_KEY_IMAGE_CODE"; @Autowired privateValidateCodeGeneratorimageCodeGenerator; /** *Session对象 */ privateSessionStrategysessionStrategy=newHttpSessionSessionStrategy(); @GetMapping("/code/image") publicvoidcreateCode(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException{ ImageCodeimageCode=imageCodeGenerator.createCode(request); //将随机数放到Session中 sessionStrategy.setAttribute(newServletWebRequest(request),SESSION_KEY,imageCode); request.getSession().setAttribute(SESSION_KEY,imageCode); //写给response响应 response.setHeader("Cache-Control","no-store,no-cache"); response.setContentType("image/jpeg"); ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream()); }
(3)、其它辅助类
@Data publicclassImageCode{ /** *图片 */ privateBufferedImageimage; /** *随机数 */ privateStringcode; /** *过期时间 */ privateLocalDateTimeexpireTime; publicImageCode(BufferedImageimage,Stringcode,LocalDateTimeexpireTime){ this.image=image; this.code=code; this.expireTime=expireTime; } publicImageCode(BufferedImageimage,Stringcode,intexpireIn){ this.image=image; this.code=code; //当前时间加上设置过期的时间 this.expireTime=LocalDateTime.now().plusSeconds(expireIn); } publicbooleanisExpried(){ //如果过期时间在当前日期之前,则验证码过期 returnLocalDateTime.now().isAfter(expireTime); } }
@ConfigurationProperties(prefix="sso.security.code.image") @Component @Data publicclassSecurityProperties{ /** *验证码宽度 */ privateintwidth=67; /** *高度 */ privateintheight=23; /** *长度(几个数字) */ privateintlength=4; /** *过期时间 */ privateintexpireIn=60; /** *需要图形验证码的url */ privateStringurl; }
(4)、验证
2、添加过滤器,进行验证码验证
@Component @Slf4j publicclassValidateCodeFilterextendsOncePerRequestFilterimplementsInitializingBean{ /** *登录失败处理器 */ @Autowired privateAuthenticationFailureHandlerfailureHandler; /** *Session对象 */ privateSessionStrategysessionStrategy=newHttpSessionSessionStrategy(); /** *创建一个Set集合存放需要验证码的urls */ privateSeturls=newHashSet<>(); /** *spring的一个工具类:用来判断两字符串是否匹配 */ privateAntPathMatcherpathMatcher=newAntPathMatcher(); @Autowired privateSecurityPropertiessecurityProperties; /** *这个方法是InitializingBean接口下的一个方法,在初始化配置完成后运行此方法 */ @Override publicvoidafterPropertiesSet()throwsServletException{ super.afterPropertiesSet(); //将application配置中的url属性进行切割 String[]configUrls=StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(),","); //添加到Set集合里 urls.addAll(Arrays.asList(configUrls)); //因为登录请求一定要有验证码,所以直接add到set集合中 urls.add("/authentication/form"); } @Override protectedvoiddoFilterInternal(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,FilterChainfilterChain)throwsServletException,IOException{ booleanaction=false; for(Stringurl:urls){ //如果请求的url和配置中的url相匹配 if(pathMatcher.match(url,httpServletRequest.getRequestURI())){ action=true; } } //拦截请求 if(action){ logger.info("拦截成功"+httpServletRequest.getRequestURI()); //如果是登录请求 try{ validate(newServletWebRequest(httpServletRequest)); }catch(ValidateCodeExceptionexception){ //返回错误信息给失败处理器 failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception); return; } } filterChain.doFilter(httpServletRequest,httpServletResponse); } privatevoidvalidate(ServletWebRequestrequest)throwsServletRequestBindingException{ //从session中取出验证码 ImageCodecodeInSession=(ImageCode)sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY); //从request请求中取出验证码 StringcodeInRequest=ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode"); if(StringUtils.isBlank(codeInRequest)){ logger.info("验证码不能为空"); thrownewValidateCodeException("验证码不能为空"); } if(codeInSession==null){ logger.info("验证码不存在"); thrownewValidateCodeException("验证码不存在"); } if(codeInSession.isExpried()){ logger.info("验证码已过期"); sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY); thrownewValidateCodeException("验证码已过期"); } if(!StringUtils.equals(codeInSession.getCode(),codeInRequest)){ logger.info("验证码不匹配"+"codeInSession:"+codeInSession.getCode()+",codeInRequest:"+codeInRequest); thrownewValidateCodeException("验证码不匹配"); } //把对应的session信息删掉 sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY); }
3、在核心配置BrowserSecurityConfig中添加过滤器配置
@Autowired privateValidateCodeFiltervalidateCodeFilter; @Override protectedvoidconfigure(HttpSecurityhttp)throwsException{ //在UsernamePasswordAuthenticationFilter过滤器前加一个过滤器来搞验证码 http.addFilterBefore(validateCodeFilter,UsernamePasswordAuthenticationFilter.class) //表单登录方式 .formLogin() .loginPage("/authentication/require") //登录需要经过的url请求 .loginProcessingUrl("/authentication/form") .passwordParameter("pwd") .usernameParameter("user") .successHandler(mySuccessHandler) .failureHandler(myFailHandler) .and() //请求授权 .authorizeRequests() //不需要权限认证的url .antMatchers("/authentication/*","/code/image").permitAll() //任何请求 .anyRequest() //需要身份认证 .authenticated() .and() //关闭跨站请求防护 .csrf().disable(); //默认注销地址:/logout http.logout(). //注销之后跳转的页面 logoutSuccessUrl("/authentication/require"); }
4、异常辅助类
publicclassValidateCodeExceptionextendsAuthenticationException{ publicValidateCodeException(Stringmsg,Throwablet){ super(msg,t); } publicValidateCodeException(Stringmsg){ super(msg); } }
5、测试
(1)、不输入验证码
(2)、添加验证码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。