Spring Security实现验证码登录功能
更新时间:2020年01月15日 09:39:35 作者:炫舞风中
这篇文章主要介绍了Spring Security实现验证码登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
这篇文章主要介绍了Spring Security实现验证码登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
在spring security实现登录注销功能的基础上进行开发。
1、添加生成验证码的控制器。
(1)、生成验证码
/** * 引入 Security 配置属性类 */ @Autowired private SecurityProperties securityProperties; @Override public ImageCode createCode(HttpServletRequest request ) { //如果请求中有 width 参数,则用请求中的,否则用 配置属性中的 int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth()); //高度(宽度) int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight()); //图片验证码字符个数 int length = securityProperties.getLength(); //过期时间 int expireIn = securityProperties.getExpireIn(); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); Random random = new Random(); g.setColor(getRandColor(200, 250)); g.fillRect(0, 0, width, height); g.setFont(new Font("Times New Roman", Font.ITALIC, 20)); g.setColor(getRandColor(160, 200)); for (int i = 0; i < 155; i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x, y, x + xl, y + yl); } String sRand = ""; for (int i = 0; i < length; i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110))); g.drawString(rand, 13 * i + 6, 16); } g.dispose(); return new ImageCode(image, sRand, expireIn); } /** * 生成随机背景条纹 */ private Color getRandColor(int fc, int bc) { Random random = new Random(); if (fc > 255) { fc = 255; } if (bc > 255) { bc = 255; } int r = fc + random.nextInt(bc - fc); int g = fc + random.nextInt(bc - fc); int b = fc + random.nextInt(bc - fc); return new Color(r, g, b); }
(2)、验证码控制器
public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE"; @Autowired private ValidateCodeGenerator imageCodeGenerator; /** * Session 对象 */ private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); @GetMapping("/code/image") public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException { ImageCode imageCode = imageCodeGenerator.createCode(request); //将随机数 放到Session中 sessionStrategy.setAttribute(new ServletWebRequest(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 public class ImageCode { /** * 图片 */ private BufferedImage image; /** * 随机数 */ private String code; /** * 过期时间 */ private LocalDateTime expireTime; public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) { this.image = image; this.code = code; this.expireTime = expireTime; } public ImageCode(BufferedImage image, String code, int expireIn) { this.image = image; this.code = code; //当前时间 加上 设置过期的时间 this.expireTime = LocalDateTime.now().plusSeconds(expireIn); } public boolean isExpried(){ //如果 过期时间 在 当前日期 之前,则验证码过期 return LocalDateTime.now().isAfter(expireTime); } }
@ConfigurationProperties(prefix = "sso.security.code.image") @Component @Data public class SecurityProperties { /** * 验证码宽度 */ private int width = 67; /** * 高度 */ private int height = 23; /** * 长度(几个数字) */ private int length = 4; /** * 过期时间 */ private int expireIn = 60; /** * 需要图形验证码的 url */ private String url; }
(4)、验证
2、添加过滤器,进行验证码验证
@Component @Slf4j public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean { /** * 登录失败处理器 */ @Autowired private AuthenticationFailureHandler failureHandler; /** * Session 对象 */ private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); /** * 创建一个Set 集合 存放 需要验证码的 urls */ private Set<String> urls = new HashSet<>(); /** * spring的一个工具类:用来判断 两字符串 是否匹配 */ private AntPathMatcher pathMatcher = new AntPathMatcher(); @Autowired private SecurityProperties securityProperties; /** * 这个方法是 InitializingBean 接口下的一个方法, 在初始化配置完成后 运行此方法 */ @Override public void afterPropertiesSet() throws ServletException { super.afterPropertiesSet(); //将 application 配置中的 url 属性进行 切割 String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(), ","); //添加到 Set 集合里 urls.addAll(Arrays.asList(configUrls)); //因为登录请求一定要有验证码 ,所以直接 add 到set 集合中 urls.add("/authentication/form"); } @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { boolean action = false; for (String url:urls){ //如果请求的url 和 配置中的url 相匹配 if (pathMatcher.match(url,httpServletRequest.getRequestURI())){ action = true; } } //拦截请求 if (action){ logger.info("拦截成功"+httpServletRequest.getRequestURI()); //如果是登录请求 try { validate(new ServletWebRequest(httpServletRequest)); }catch (ValidateCodeException exception){ //返回错误信息给 失败处理器 failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception); return; } } filterChain.doFilter(httpServletRequest,httpServletResponse); } private void validate(ServletWebRequest request) throws ServletRequestBindingException { //从session中取出 验证码 ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY); //从request 请求中 取出 验证码 String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode"); if (StringUtils.isBlank(codeInRequest)){ logger.info("验证码不能为空"); throw new ValidateCodeException("验证码不能为空"); } if (codeInSession == null){ logger.info("验证码不存在"); throw new ValidateCodeException("验证码不存在"); } if (codeInSession.isExpried()){ logger.info("验证码已过期"); sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY); throw new ValidateCodeException("验证码已过期"); } if (!StringUtils.equals(codeInSession.getCode(),codeInRequest)){ logger.info("验证码不匹配"+"codeInSession:"+codeInSession.getCode() +", codeInRequest:"+codeInRequest); throw new ValidateCodeException("验证码不匹配"); } //把对应 的 session信息 删掉 sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY); }
3、在核心配置BrowserSecurityConfig中添加过滤器配置
@Autowired private ValidateCodeFilter validateCodeFilter; @Override protected void configure(HttpSecurity http) throws Exception { //在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、异常辅助类
public class ValidateCodeException extends AuthenticationException { public ValidateCodeException(String msg, Throwable t) { super(msg, t); } public ValidateCodeException(String msg) { super(msg); } }
5、测试
(1)、不输入验证码
(2)、添加验证码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
Java中对AtomicInteger和int值在多线程下递增操作的测试
这篇文章主要介绍了Java中对AtomicInteger和int值在多线程下递增操作的测试,本文得出AtomicInteger操作 与 int操作的效率大致相差在50-80倍上下的结论,需要的朋友可以参考下2014-09-09Mybatis-Plus接口BaseMapper与Services使用详解
这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2022-05-05
最新评论