序言
在测试的时候,当其他人批改了某一个用户明码,那么想要登陆该用户进行测试则须要重置数据库,那么此时须要减少一种非凡的登陆形式,不依附账户: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. 点击加号新建,能够抉择手动输出验证码或者通过二维码形式输出:
启用 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 生成的明码进行登录测试:
右下角标注的是剩余时间,该一次性明码是每个肯定工夫刷新(如同是 60 秒)。
总结
此次弄超级明码还是破费本人肯定工夫的,起因在于办法不对,当接到工作之后就间接复制粘贴,而后去查原理,然而没有认真去看复制的代码的意思和思路。最初的后果就是事倍功半吧。