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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。