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)、添加验证码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Security登录添加验证码的实现过程

    登录添加验证码是一个非常常见的需求,网上也有非常成熟的解决方案,其实,要是自己自定义登录实现这个并不难,但是如果需要在 Spring Security 框架中实现这个功能,还得稍费一点功夫,本文就和小伙伴来分享下在 Spring Security 框架中如何添加验证码. 关于 Spring Security 基本配置,这里就不再多说,小伙伴有不懂的可以参考我的书<SpringBoot+Vue全栈开发实战>,本文主要来看如何加入验证码功能. 准备验证码 要有验证码,首先得先准备好验证码,本文采用

  • spring security4 添加验证码的示例代码

    spring security是一个很大的模块,本文中只涉及到了自定义参数的认证.spring security默认的验证参数只有username和password,一般来说都是不够用的.由于时间过太久,有些忘,可能有少许遗漏.好了,不废话. spring以及spring security配置采用javaConfig,版本依次为4.2.5,4.0.4 总体思路:自定义EntryPoint,添加自定义参数扩展AuthenticationToken以及AuthenticationProvider进行

  • Spring Security OAuth2集成短信验证码登录以及第三方登录

    前言 基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring Boot环境下使用OAuth2.0,提供了开箱即用的组件.但是在开发过程中我们会发现由于Spring Security OAuth2的组件特别全面,这样就导致了扩展很不方便或者说是不太容易直指定扩展的方案,例如: 图片验证码登录 短信验证码登录 微信小程序登录 第三方系统登录 CAS单点登录

  • Spring Security 图片验证码功能的实例代码

    验证码逻辑 以前在项目中也做过验证码,生成验证码的代码网上有很多,也有一些第三方的jar包也可以生成漂亮的验证码.验证码逻辑很简单,就是在登录页放一个image标签,src指向一个controller,这个Controller返回把生成的图片以输出流返回给页面,生成图片的同时把图片上的文本放在session,登录的时候带过来输入的验证码,从session中取出,两者对比.这位老师讲的用Spring Security集成验证码,大体思路和我说的一样,但更加规范和通用些. spring securi

  • SpringBoot + SpringSecurity 短信验证码登录功能实现

    实现原理 在之前的文章中,我们介绍了普通的帐号密码登录的方式: SpringBoot + Spring Security 基本使用及个性化登录配置. 但是现在还有一种常见的方式,就是直接通过手机短信验证码登录,这里就需要自己来做一些额外的工作了. 对SpringSecurity认证流程详解有一定了解的都知道,在帐号密码认证的过程中,涉及到了以下几个类:UsernamePasswordAuthenticationFilter(用于请求参数获取),UsernamePasswordAuthentica

  • SpringSecurity实现图形验证码功能实例代码

    Spring Security Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 本文重点给大家介绍Spri

  • SpringBoot结合SpringSecurity实现图形验证码功能

    本文介绍了SpringBoot结合SpringSecurity实现图形验证码功能,分享给大家,具体如下: 生成图形验证码 根据随机数生成图片 将随机数存到Session中 将生成的图片写到接口的响应中 生成图形验证码的过程比较简单,和SpringSecurity也没有什么关系.所以就直接贴出代码了 根据随机数生成图片 /** * 生成图形验证码 * @param request * @return */ private ImageCode generate(ServletWebRequest r

  • Spring Security Oauth2.0 实现短信验证码登录示例

    本文介绍了Spring Security Oauth2.0 实现短信验证码登录示例,分享给大家,具体如下: 定义手机号登录令牌 /** * @author lengleng * @date 2018/1/9 * 手机号登录令牌 */ public class MobileAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = SpringSecur

  • SpringBoot+Security 发送短信验证码的实现

    在core模块下properties包中创建SmsCodeProperties 在ValidateCodeProperties中new一个SmsCodeProperties对象,并实现getter.setter方法 在core模块下validate包中创建SmsCodeGenerator实现ValidateCodeGenerator接口 创建SmsCodeSender接口,定义发送短信的抽象方法 实现SmsCodeSender接口 在ValidateCodeBeanConfig中把SmsCode

  • Spring Security实现验证码登录功能

    这篇文章主要介绍了Spring Security实现验证码登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在spring security实现登录注销功能的基础上进行开发. 1.添加生成验证码的控制器. (1).生成验证码 /** * 引入 Security 配置属性类 */ @Autowired private SecurityProperties securityProperties; @Override public ImageC

  • Spring Security 表单登录功能的实现方法

    1.简介 本文将重点介绍使用 Spring Security 登录. 本文将构建在之前简单的 Spring MVC示例 之上,因为这是设置Web应用程序和登录机制的必不可少的. 2. Maven 依赖 要将Maven依赖项添加到项目中,请参阅Spring Security with Maven 一文. 标准的 spring-security-web 和 spring-security-config 都是必需的. 3. Spring Security Java配置 我们首先创建一个扩展 WebSe

  • Spring Security 实现短信验证码登录功能

    之前文章都是基于用户名密码登录,第六章图形验证码登录其实还是用户名密码登录,只不过多了一层图形验证码校验而已:Spring Security默认提供的认证流程就是用户名密码登录,整个流程都已经固定了,虽然提供了一些接口扩展,但是有些时候我们就需要有自己特殊的身份认证逻辑,比如用短信验证码登录,它和用户名密码登录的逻辑是不一样的,这时候就需要重新写一套身份认证逻辑. 开发短信验证码接口 获取验证码 短信验证码的发送获取逻辑和图片验证码类似,这里直接贴出代码. @GetMapping("/code/

  • Spring Security使用单点登录的权限功能

    目录 背景 Spring Security 实现 已经有了单点登录页面,Spring Security怎么登录,不登录可以拿到权限吗 Authentication 继续分析 结论 如何手动登录Spring Security 结论 总结 附 参考 背景 在配置中心增加权限功能 目前配置中心已经包含了单点登录功能,可以通过统一页面进行登录,登录完会将用户写入用户表 RBAC的用户.角色.权限表CRUD.授权等都已经完成 希望不用用户再次登录,就可以使用SpringSecurity的权限控制 Spri

  • Spring Security 实现“记住我”功能及原理解析

    这章继续扩展功能,来一个"记住我"的功能实现,就是说用户在登录一次以后,系统会记住这个用户一段时间,这段时间内用户不需要重新登录就可以使用系统. 记住我功能基本原理 原理说明 用户登录发送认证请求的时候会被UsernamePasswordAuthenticationFilter认证拦截,认证成功以后会调用一个RememberMeService服务,服务里面有一个TokenRepository,这个服务会生成一个Token,然后将Token写入到浏览器的Cookie同时会使用TokenR

  • spring security自定义认证登录的全过程记录

    spring security使用分类: 如何使用spring security,相信百度过的都知道,总共有四种用法,从简到深为: 1.不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo: 2.使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差: 3.spring security和Acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的f

  • 基于 antd pro 的短信验证码登录功能(流程分析)

    概要 最近使用 antd pro 开发项目时遇到个新的需求, 就是在登录界面通过短信验证码来登录, 不使用之前的用户名密码之类登录方式. 这种方式虽然增加了额外的短信费用, 但是对于安全性确实提高了不少. antd 中并没有自带能够倒计时的按钮, 但是 antd pro 的 ProForm components 中倒是提供了针对短信验证码相关的组件. 组件说明可参见: https://procomponents.ant.design/components/form 整体流程 通过短信验证码登录的

  • Spring Security添加验证码的两种方式小结

    目录 一.自定义认证逻辑 二.自定义过滤器 总结 一.自定义认证逻辑 生成验证码工具 <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> </dependency> 添加Kaptcha配置 @Configuration public class K

  • Spring Security实现自动登陆功能示例

    当我们在登录像QQ邮箱这种大多数的网站,往往在登录按键上会有下次自动登录这个选项,勾选后登录成功,在一段时间内,即便退出浏览器或者服务器重启,再次访问不需要用户输入账号密码进行登录,这也解决了用户每次输入账号密码的麻烦. 接下来实现自动登陆. applicatio.properties配置用户名密码 spring.security.user.name=java spring.security.user.password=java controller层实现 @RestController pub

随机推荐