spring boot超级密码登陆

序言

在测试的时候,当其他人修改了某一个用户密码,那么想要登陆该用户进行测试则需要重置数据库,那么此时需要增加一种特殊的登陆方式,不依靠账户:Google Authenticator

Google Authenticator

工作原理

使用一个密钥和当前时间戳来获取一个六位的一次性密码。
使用的算法:TOTP(Time-Based One-Time Password基于时间的一次性密码)
举个栗子:例如使用密钥为DPI45HKISEXU6HG7,那么Google Authenticator会根据你刚刚输入的密钥和当前的时间戳通过算法来获取一个一次性的密码。

使用方法:

1.下载:下载到google插件中
2.获取当前的密钥:本项目是使用base32加密获取:

this.token = base32.encodeAsString(appProperties.getToken().getBytes());

对于当前项目,那么如何获取加密后的密钥呢:base32 online

2.点击加号新建,可以选择手动输入验证码或者通过二维码方式输入:
image.png
image.png

启用Authenticator在项目中

1.在pom.xml引入依赖,重新加载

        <!--one time password-->
        <dependency>
            <groupId>com.j256.two-factor-auth</groupId>
            <artifactId>two-factor-auth</artifactId>
            <version>1.3</version>
        </dependency>

2.调用现成的TOTP算法生成一次性密码

public OneTimePasswordImpl(AppProperties appProperties) {
    // 将token使用base32进行转码,原理同base64
    Base32 base32 = new Base32();
    this.token = base32.encodeAsString(appProperties.getToken().getBytes());
  }

/**
   * 仅允许获取1次,获取成功后code值为null
   *
   * @return
   */
  @Override
  public Optional<String> getPassword() {
    try {
      // 直接调用现成库的方法生成一次性密码
      String password = TimeBasedOneTimePasswordUtil.generateCurrentNumberString(this.token);
      // 每个密码只能用一次,如果生成的密码与当前的密码相同,则说明短时间内请求了两次,返回empty
      if (password.equals(this.password)) {
        return Optional.empty();
      } else {
        this.password = password;
      }
    } catch (GeneralSecurityException e) {
      this.logger.error("生成一次性密码时发生错误");
      e.printStackTrace();
    }

    return Optional.of(this.password);
  }

3.建立自己的密码校验器

/**
 * 自定义密码校验器.
 * 注意:其不能够声明为@Component组件出现,否则将触发DaoAuthenticationProvider的构造函数
 * 从而直接注册DelegatingPasswordEncoder校验器
 */
public class MyBCryptPasswordEncoder extends BCryptPasswordEncoder {
  private final Logger logger = LoggerFactory.getLogger(this.getClass());

  /**
   * 一次性密码.
   */
  private final OneTimePassword oneTimePassword;

  public MyBCryptPasswordEncoder(OneTimePassword oneTimePassword) {
    super();
    this.oneTimePassword = oneTimePassword;
  }

  @Override
  public boolean matches(CharSequence rawPassword, String encodedPassword) {
    if (rawPassword == null) {
      throw new IllegalArgumentException("rawPassword cannot be null");
    }

    // 当有一次性密码(每个密码仅能用一次)且未使用时,验证用户是否输入了超密
    Optional oneTimePassword = this.oneTimePassword.getPassword();

    // 判断是否使用一次性密码(如果密码等于通过算法获取的一次性密码,那么当前为使用超级密码登陆
    if (oneTimePassword.isPresent() && oneTimePassword.get().equals(rawPassword.toString())) {
      logger.warn("当前正在使用超级密码登录");
      return true;
    }

    // 不使用超密则执行正常的密码判断
    return super.matches(rawPassword, encodedPassword);
  }
}

4.使用自己建立的密码校验器替换默认的

@Configuration
@EnableWebSecurity
public class MvcSecurityConfig extends WebSecurityConfigurerAdapter {
  private final BCryptPasswordEncoder passwordEncoder;

  public MvcSecurityConfig(OneTimePassword oneTimePassword) {
    this.passwordEncoder = new MyBCryptPasswordEncoder(oneTimePassword);
    User.setPasswordEncoder(this.passwordEncoder);
  }

  @Bean
  PasswordEncoder passwordEncoder() {
    return this.passwordEncoder;
  }
}

5.验证是否成功

直接使用Authenticator生成的密码进行登录测试:
image.png
右下角标注的是剩余时间,该一次性密码是每个一定时间刷新(好像是60秒)。

总结

此次弄超级密码还是花费自己一定时间的,原因在于方法不对,当接到任务之后就直接复制粘贴,然后去查原理,但是没有仔细去看复制的代码的意思和思路。最后的结果就是事半功倍吧。

127 声望
14 粉丝
0 条评论
推荐阅读
SpringBoot 定时任务依赖注入失败记录
日志系统需要每天推送每天日志统计到钉钉群,至于如何实现向钉钉推送请参考:java(SpringBoot)实现钉钉推送。学长他们的用的是一个固定的Webhook和secrect,这样就会导致一个问题,下次修改推送群那么就需要修改...

郝泽龙_HZ阅读 1.1k

图解浏览器的多进程渲染机制
观察浏览器的任务管理器可以发现,打开浏览器的一个页面需要多个进程,包括浏览器进程、GPU 进程、网络进程、渲染进程等,有插件的话还会包括各种插件进程(Chrome 选项 -&gt; 更多工具 -&gt; 任务管理器)。

兰俊秋雨6阅读 852

前端性能指标
传统性能标准初始化阶段navigationStart:请求开始时间,返回 0unloadEventStart:等于用户代理程序开始前一个文档的卸载事件之前的时间unloadEventEnd:等于用户代理程序完成前一文档的卸载事件之后的时间redire...

散一群逗逼9阅读 984

封面图
使用浏览器录制屏幕
最近有一个录制屏幕的需求,类似于录制软件的操作教程给别人演示。在网上搜了一下录制屏幕,结果大多需要下载安装,而且有收费或者广告。无意间找到一个使用浏览器在线录屏的网站,出于好奇,研究了一下实现的方...

来了老弟5阅读 1.8k评论 10

封面图
与RabbitMQ有关的一些知识
工作中用过一段时间的Kafka,不过主要还是RabbitMQ用的多一些。今天主要来讲讲与RabbitMQ相关的一些知识。一些基本概念,以及实际使用场景及一些注意事项。

lpe2348阅读 1.9k

封面图
spring boot 锁
由于当前的项目中由于多线程操作同一个实体,会出现数据覆盖的问题,后保存的实体把先保存的实体的数据给覆盖了。于是查找了锁的实现的几种方式。但写到最后发现,其实自己可以写sql 更新需要更新的字段即可,这...

weiewiyi3阅读 9.2k

记录java 在遍历中删除元素 以及 mysql5.6版本添加unique失败
遍历中删除List或Queue等数据结构中,如何一边遍历一遍删除?1. 常犯错误ArrayList可能没遇到坑过的人会用增强for循环这么写: {代码...} 但是一运行,结果却抛 java.util.ConcurrentModificationException 异常即...

weiewiyi5阅读 803

127 声望
14 粉丝
宣传栏