Springboot+SpringSecurity怎么实现图片验证码登录
Springboot+SpringSecurity怎么实现图片验证码登录
本文小编为大家详细介绍“Springboot+SpringSecurity怎么实现图片验证码登录”,内容详细,步骤清晰,细节处理妥当,希望这篇“Springboot+SpringSecurity怎么实现图片验证码登录”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
效果图
网上大都是直接注入一个AuthenticationFailureHandler,我当时就不明白这个咋注进去的,我这个一写就报错,注入不进去,后来就想自己new一个哇,可以是可以了,但是还报异常,java.lang.IllegalStateException: Cannot call sendError() after the response has been committed,后来想表单验证的时候,失败用的也是这个处理器,就想定义一个全局的处理器,
packagecom.liruilong.hros.config;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.liruilong.hros.Exception.ValidateCodeException;importcom.liruilong.hros.model.RespBean;importorg.springframework.context.annotation.Bean;importorg.springframework.security.authentication.*;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.web.authentication.AuthenticationFailureHandler;importorg.springframework.stereotype.Component;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;/***@Description:*@Author:Liruilong*@Date:2020/2/1123:08*/@ComponentpublicclassMyAuthenticationFailureHandlerimplementsAuthenticationFailureHandler{@OverridepublicvoidonAuthenticationFailure(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,AuthenticationExceptione)throwsIOException,ServletException{httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriterout=httpServletResponse.getWriter();RespBeanrespBean=RespBean.error(e.getMessage());//验证码自定义异常的处理if(einstanceofValidateCodeException){respBean.setMsg(e.getMessage());//Security内置的异常处理}elseif(einstanceofLockedException){respBean.setMsg("账户被锁定请联系管理员!");}elseif(einstanceofCredentialsExpiredException){respBean.setMsg("密码过期请联系管理员!");}elseif(einstanceofAccountExpiredException){respBean.setMsg("账户过期请联系管理员!");}elseif(einstanceofDisabledException){respBean.setMsg("账户被禁用请联系管理员!");}elseif(einstanceofBadCredentialsException){respBean.setMsg("用户名密码输入错误,请重新输入!");}//将hr转化为Stingout.write(newObjectMapper().writeValueAsString(respBean));out.flush();out.close();}@BeanpublicMyAuthenticationFailureHandlergetMyAuthenticationFailureHandler(){returnnewMyAuthenticationFailureHandler();}}
流程
请求登录页,将验证码结果存到基于Servlet的session里,以JSON格式返回验证码,
之后前端发送登录请求,SpringSecurity中处理,自定义一个filter让它继承自OncePerRequestFilter,然后重写doFilterInternal方法,在这个方法中实现验证码验证的功能,如果验证码错误就抛出一个继承自AuthenticationException的验证吗错误的异常消息写入到响应消息中.
之后返回异常信息交给自定义验证失败处理器处理。
下面以这个顺序书写代码:
依赖大家照着import导一下吧,记得有这两个,验证码需要一个依赖,之后还使用了一个工具依赖包,之后是前端代码
<!--图片验证--><dependency><groupId>com.github.whvcse</groupId><artifactId>easy-captcha</artifactId><version>1.6.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency>
<divclass="login-code"><img:src="codeUrl"@click="getCode"></div>
后端代码:
获取验证码,将结果放到session里
packagecom.liruilong.hros.controller;importcom.liruilong.hros.model.RespBean;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importcom.wf.captcha.ArithmeticCaptcha;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.util.HashMap;importjava.util.Map;/***@Description:*@Author:Liruilong*@Date:2019/12/1919:58*/@RestControllerpublicclassLoginController{@GetMapping(value="/auth/code")publicMapgetCode(HttpServletRequestrequest,HttpServletResponseresponse){//算术类型https://gitee.com/whvse/EasyCaptchaArithmeticCaptchacaptcha=newArithmeticCaptcha(111,36);//几位数运算,默认是两位captcha.setLen(2);//获取运算的结果Stringresult=captcha.text();System.err.println("生成的验证码:"+result);//保存//验证码信息Map<String,Object>imgResult=newHashMap<String,Object>(2){{put("img",captcha.toBase64());}};request.getSession().setAttribute("yanzhengma",result);returnimgResult;}}
定义一个VerifyCodeFilter过滤器
packagecom.liruilong.hros.filter;importcom.liruilong.hros.Exception.ValidateCodeException;importcom.liruilong.hros.config.MyAuthenticationFailureHandler;importorg.apache.commons.lang3.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.security.core.AuthenticationException;importorg.springframework.stereotype.Component;importorg.springframework.web.filter.OncePerRequestFilter;importjavax.servlet.FilterChain;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/***@Description:*@Author:Liruilong*@Date:2020/2/719:39*/@ComponentpublicclassVerifyCodeFilterextendsOncePerRequestFilter{@BeanpublicVerifyCodeFiltergetVerifyCodeFilter(){returnnewVerifyCodeFilter();}@AutowiredMyAuthenticationFailureHandlermyAuthenticationFailureHandler;@OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{if(StringUtils.equals("/doLogin",request.getRequestURI())&&StringUtils.equalsIgnoreCase(request.getMethod(),"post")){//1.进行验证码的校验try{StringrequestCaptcha=request.getParameter("code");if(requestCaptcha==null){thrownewValidateCodeException("验证码不存在");}Stringcode=(String)request.getSession().getAttribute("yanzhengma");if(StringUtils.isBlank(code)){thrownewValidateCodeException("验证码过期!");}code=code.equals("0.0")?"0":code;logger.info("开始校验验证码,生成的验证码为:"+code+",输入的验证码为:"+requestCaptcha);if(!StringUtils.equals(code,requestCaptcha)){thrownewValidateCodeException("验证码不匹配");}}catch(AuthenticationExceptione){//2.捕获步骤1中校验出现异常,交给失败处理类进行进行处理myAuthenticationFailureHandler.onAuthenticationFailure(request,response,e);}finally{filterChain.doFilter(request,response);}}else{filterChain.doFilter(request,response);}}}
定义一个自定义异常处理,继承AuthenticationException
packagecom.liruilong.hros.Exception;importorg.springframework.security.core.AuthenticationException;/***@Description:*@Author:Liruilong*@Date:2020/2/87:24*/publicclassValidateCodeExceptionextendsAuthenticationException{publicValidateCodeException(Stringmsg){super(msg);}}
security配置.
在之前的基础上加filter的基础上加了
http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class),验证处理上,验证码和表单验证失败用同一个失败处理器,
packagecom.liruilong.hros.config;importcom.fasterxml.jackson.databind.ObjectMapper;importcom.liruilong.hros.filter.VerifyCodeFilter;importcom.liruilong.hros.model.Hr;importcom.liruilong.hros.model.RespBean;importcom.liruilong.hros.service.HrService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.authentication.*;importorg.springframework.security.config.annotation.ObjectPostProcessor;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.builders.WebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.AuthenticationEntryPoint;importorg.springframework.security.web.access.intercept.FilterSecurityInterceptor;importorg.springframework.security.web.authentication.AuthenticationSuccessHandler;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.security.web.authentication.logout.LogoutSuccessHandler;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;/***@Description:*@Author:Liruilong*@Date:2019/12/1819:11*/@ConfigurationpublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{@AutowiredHrServicehrService;@AutowiredCustomFilterInvocationSecurityMetadataSourcecustomFilterInvocationSecurityMetadataSource;@AutowiredCustomUrlDecisionManagercustomUrlDecisionManager;@AutowiredVerifyCodeFilterverifyCodeFilter;@AutowiredMyAuthenticationFailureHandlermyAuthenticationFailureHandler;@BeanPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.userDetailsService(hrService);}/***@AuthorLiruilong*@Description放行的请求路径*@Date19:252020/2/7*@Param[web]*@returnvoid**/@Overridepublicvoidconfigure(WebSecurityweb)throwsException{web.ignoring().antMatchers("/auth/code","/login","/css/**","/js/**","/index.html","/img/**","/fonts/**","/favicon.ico");}@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.addFilterBefore(verifyCodeFilter,UsernamePasswordAuthenticationFilter.class).authorizeRequests()//.anyRequest().authenticated().withObjectPostProcessor(newObjectPostProcessor<FilterSecurityInterceptor>(){@Overridepublic<OextendsFilterSecurityInterceptor>OpostProcess(Oobject){object.setAccessDecisionManager(customUrlDecisionManager);object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);returnobject;}}).and().formLogin().usernameParameter("username").passwordParameter("password").loginProcessingUrl("/doLogin").loginPage("/login")//登录成功回调.successHandler(newAuthenticationSuccessHandler(){@OverridepublicvoidonAuthenticationSuccess(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,Authenticationauthentication)throwsIOException,ServletException{httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriterout=httpServletResponse.getWriter();Hrhr=(Hr)authentication.getPrincipal();//密码不回传hr.setPassword(null);RespBeanok=RespBean.ok("登录成功!",hr);//将hr转化为StingStrings=newObjectMapper().writeValueAsString(ok);out.write(s);out.flush();out.close();}})//登失败回调.failureHandler(myAuthenticationFailureHandler)//相关的接口直接返回.permitAll().and().logout()//注销登录//.logoutSuccessUrl("").logoutSuccessHandler(newLogoutSuccessHandler(){@OverridepublicvoidonLogoutSuccess(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,Authenticationauthentication)throwsIOException,ServletException{httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriterout=httpServletResponse.getWriter();out.write(newObjectMapper().writeValueAsString(RespBean.ok("注销成功!")));out.flush();out.close();}}).permitAll().and().csrf().disable().exceptionHandling()//没有认证时,在这里处理结果,不要重定向.authenticationEntryPoint(newAuthenticationEntryPoint(){@Overridepublicvoidcommence(HttpServletRequestreq,HttpServletResponseresp,AuthenticationExceptionauthException)throwsIOException,ServletException{resp.setContentType("application/json;charset=utf-8");resp.setStatus(401);PrintWriterout=resp.getWriter();RespBeanrespBean=RespBean.error("访问失败!");if(authExceptioninstanceofInsufficientAuthenticationException){respBean.setMsg("请求失败,请联系管理员!");}out.write(newObjectMapper().writeValueAsString(respBean));out.flush();out.close();}});}}
读到这里,这篇“Springboot+SpringSecurity怎么实现图片验证码登录”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注恰卡编程网行业资讯频道。
推荐阅读
-
vue动态添加删除输入框(springboot vue怎么让数据库显示出来)
springbootvue怎么让数据库显示出来?一般情况下是前端调阅后端接口,来获取到数据库的数据,后端哪里会把数据库的数据整理...
-
springboot实现基于aop的切面日志
本文实例为大家分享了springboot实现基于aop的切面日志的具体代码,供大家参考,具体内容如下通过aop的切面方式实现日志...
-
SpringBoot定时任务功能怎么实现
-
SpringBoot中的@Import注解怎么使用
-
SpringBoot整合Lombok及常见问题怎么解决
-
springboot图片验证码功能模块怎么实现
-
SpringBoot注解的知识点有哪些
SpringBoot注解的知识点有哪些这篇“SpringBoot注...
-
SpringBoot2.x中management.security.enabled=false无效怎么解决
-
springboot怎么禁用某项健康检查
springboot怎么禁用某项健康检查今天小编给大家分享一下sp...
-
SpringBoot2怎么自定义端点