通过要求用户提供第二种身份验证,双重身份验证为您的Web应用程序增加了一层额外的安全保护。 常见的第二个因素包括:

  • 验证码生物识别电子邮件或短信代码

让我们探讨如何利用Nexmo向现有的Web应用程序中添加双重身份验证。

Before you begin

为了跟随本教程,您将需要以下内容:

  • A general understanding of Java and Enterprise Java technologies
  • The Java SDK installed on your computer
  • A Nexmo Developer Account along with an API key and secret
  • A clone of the getting-started branch on GitHub

Get the Code

克隆入门科。

git clone https://github.com/cr0wst/demo-twofactor.git -b getting-started
cd demo-twofactor

Let’s See What We’re Working With

The example application is built using Spring Boot. If you have Gradle installed on your system, you should be able to execute the bootRun task to start the application.

如果没有,则无后顾之忧; 仓库包含一个Gradle包装器,仍然可以让您执行任务。

./gradlew bootRun

这将下载所有依赖项,编译应用程序,然后启动嵌入式服务器。

Once the server has been started, you should be able to navigate to http://localhost:8080 to see the sample application.

共三页:

  • The home page - accessible by everybody.
  • The login page - allows users to enter a username and password (default is demo/demo).
  • The secret page - accessible only by users with the Role.USERS role.

Adding Two-Factor Authentication

用户登录时,我们唯一的接受标准是他们提供了用户名和密码。 如果此信息被盗怎么办? 我们可以使用的什么东西实际上位于用户附近?

我保证您和我们的用户中有将近90%会触手可及。 一部手机。

运作方式如下:

  1. 用户将照常登录到您的应用程序。他们将被提示输入四位数的验证码。同时,四位数的验证码将发送到他们帐户中的电话号码。 如果他们的帐户上没有电话号码,我们将允许他们绕过两因素身份验证。他们输入的代码将被检查以确保与我们发送给他们的代码相同。

We are going to utilize the Nexmo Verify API to generate the code and to check and see if the code they entered is valid.

Creating a New Role

第一步将是创建一个新角色。 该角色将用于使经过身份验证的用户处于炼狱状态,直到我们验证了他们的身份。

添加PRE_VERIFICATION_USER角色角色枚举。

// src/main/net/smcrow/demo/twofactor/user/Role.java
public enum Role implements GrantedAuthority {USER, PRE_VERIFICATION_USER;// ...
}

为了将其用作默认角色,我们需要更新getAuthorities()的方法标准用户详细信息类。

// src/main/net/smcrow/demo/twofactor/user/StandardUserDetails.java
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {Set<GrantedAuthority> authorities = new HashSet<>();authorities.add(Role.PRE_VERIFICATION_USER);return authorities;
}

Handling Verification Information

Nexmo将为我们提供要求编号确认用户提供的代码时将使用该代码。 我们可以通过多种方式存储此信息。 在本教程中,我们将其持久化到数据库中。

Storing Verification Information

首先,建立一个验证中的课程校验包。

// src/main/net/smcrow/demo/twofactor/verify/Verification.java
@Entity
public class Verification {@Id@Column(unique = true, nullable = false)private String phone;@Column(nullable = false)private String requestId;@Column(nullable = false)private Date expirationDate;@PersistenceConstructorpublic Verification() {// Empty constructor for JPA}public Verification(String phone, String requestId, Date expirationDate) {this.phone = phone;this.requestId = requestId;this.expirationDate = expirationDate;}// ... Getters and Setters
}

注意,我们还存储了截止日期。 默认情况下,Verify API请求仅有效五分钟。 在以下情况之一时,它们将从表中删除:

  • 用户已成功验证其身份。它们已过期。

我们将利用Spring Scheduler定期清理它们。

Working with the Verification Information

创建验证库界面校验包。

// src/mainnet/smcrow/demo/twofactor/verify/VerificationRepository.java
@Repository
public interface VerificationRepository extends JpaRepository<Verification, String> {Optional<Verification> findByPhone(String phone);void deleteByExpirationDateBefore(Date date);
}

Deleting Expired Requests

在里面两个因素包,创建以下配置类。

// src/main/net/smcrow/demo/twofactor/ScheduleConfiguration.java
@Configuration
@EnableScheduling
public class ScheduleConfiguration {@Autowiredprivate VerificationRepository verificationRepository;@Scheduled(fixedDelay = 1000)@Transactionalpublic void purgeExpiredVerifications() {verificationRepository.deleteByExpirationDateBefore(new Date());}
}

这将设置一个计划的命令,使其每秒钟执行一次,以查询是否已过期验证实体并删除它们。

Setting Up the Nexmo Client

We will be using the nexmo-java client for interacting with Nexmo.

Declare the Dependency

首先,在build.gradle文件。

dependencies {// .. other dependenciescompile('com.nexmo:client:3.3.0')
}

Provide Information

现在,在application.properties文件。

# Add your nexmo credentials
nexmo.api.key=your-api-key
nexmo.api.secret=your-api-secret

Define the Beans

接下来,我们将定义NexmoClient和验证客户作为豆。 这将允许Spring将它们作为依赖项注入到我们的NexmoVerificationService。

将以下定义添加到双重因素应用类。

// src/main/net/smcrow/demo/twofactor/TwofactorApplication.java
@Bean
public NexmoClient nexmoClient(Environment environment) {AuthMethod auth = new TokenAuthMethod(environment.getProperty("nexmo.api.key"),environment.getProperty("nexmo.api.secret"));return new NexmoClient(auth);
}@Bean
public VerifyClient nexmoVerifyClient(NexmoClient nexmoClient) {return nexmoClient.getVerifyClient();
}

Create the NexmoVerificationService

我们将创建一个服务,该服务将允许我们指示客户提出请求。

添加NexmoVerificationService到校验包。

// src/main/net/smcrow/demo/twofactor/verify/NexmoVerificationService.java
@Service
public class NexmoVerificationService {private static final String APPLICATION_BRAND = "2FA Demo";private static final int EXPIRATION_INTERVALS = Calendar.MINUTE;private static final int EXPIRATION_INCREMENT = 5;@Autowiredprivate VerificationRepository verificationRepository;@Autowiredprivate UserRepository userRepository;@Autowiredprivate VerifyClient verifyClient;public Verification requestVerification(String phone) throws VerificationRequestFailedException {Optional<Verification> matches = verificationRepository.findByPhone(phone);if (matches.isPresent()) {return matches.get();}return generateAndSaveNewVerification(phone);}public boolean verify(String phone, String code) throws VerificationRequestFailedException {try {Verification verification = retrieveVerification(phone);if (verifyClient.check(verification.getRequestId(), code).getStatus() == 0) {verificationRepository.delete(phone);return true;}return false;} catch (VerificationNotFoundException e) {requestVerification(phone);return false;} catch (IOException | NexmoClientException e) {throw new VerificationRequestFailedException(e);}}private Verification retrieveVerification(String phone) throws VerificationNotFoundException {Optional<Verification> matches = verificationRepository.findByPhone(phone);if (matches.isPresent()) {return matches.get();}throw new VerificationNotFoundException();}private Verification generateAndSaveNewVerification(String phone) throws VerificationRequestFailedException {try {VerifyResult result = verifyClient.verify(phone, APPLICATION_BRAND);if (StringUtils.isBlank(result.getErrorText())) {String requestId = result.getRequestId();Calendar now = Calendar.getInstance();now.add(EXPIRATION_INTERVALS, EXPIRATION_INCREMENT);Verification verification = new Verification(phone, requestId, now.getTime());return verificationRepository.save(verification);}} catch (IOException | NexmoClientException e) {throw new VerificationRequestFailedException(e);}throw new VerificationRequestFailedException();}
}

此类中有两种主要方法:

  • requestVerficiation用来请求验证。校验 which is used to 校验 the provided code provided by the user.

The requestVerification Method

该方法首先检查看看我们是否已经有针对该用户电话号码的待定验证请求。 如果用户尝试再次登录到应用程序,这可以使我们向用户提供相同的请求ID。

如果没有任何先前的验证,则需要一个新的验证码并将其保存到数据库中。 如果由于某种原因,我们无法为他们分配新的代码,验证RequestFailedException被抛出。

将此例外添加到校验包。

// src/main/net/smcrow/demo/twofactor/verify/VerificationRequestFailedException.java
public class VerificationRequestFailedException extends Throwable {public VerificationRequestFailedException() {this("Failed to verify request.");}public VerificationRequestFailedException(String message) {super(message);}public VerificationRequestFailedException(Throwable cause) {super(cause);}
}

The verify Method

的校验方法将请求ID和代码发送到Nexmo进行验证。 如果验证成功,Nexmo将返回零状态。 成功验证后,验证实体已从数据库中删除,并且真正返回。

如果我们找不到验证实体,也许它已过期,我们请求一个新的实体并返回false。 如果有任何问题需要验证,我们会抛出一个验证RequestFailedException。

的检索验证方法会抛出一个验证NotFoundException如果验证找不到。

将此例外添加到校验包。

// src/main/net/smcrow/demo/twofactor/verify/VerificationNotFoundException.javapublic class VerificationNotFoundException extends Throwable {public VerificationNotFoundException() {this("Failed to find verification.");}public VerificationNotFoundException(String message) {super(message);}
}

Using the NexmoVerificationService

我们将使用该服务发送代码和验证代码。 验证成功后,便会发送代码。

Triggering the Request for Verification

让我们实施一个自定义AuthenticationSuccessHandler用户成功通过身份验证后将被调用。

添加TwoFactorAuthenticationSuccessHandler到校验包。

// src/main/net/smcrow/demo/twofactor/verify/TwoFactorAuthenticationSuccessHandler.java
@Component
public class TwoFactorAuthenticationSuccessHandler implements AuthenticationSuccessHandler {private static final String VERIFICATION_URL = "/verify";private static final String INDEX_URL = "/";@Autowiredprivate NexmoVerificationService verificationService;@Autowiredprivate UserRepository userRepository;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {String phone = ((StandardUserDetails) authentication.getPrincipal()).getUser().getPhone();if (phone == null || !requestAndRegisterVerification(phone)) {bypassVerification(request, response, authentication);return;}new DefaultRedirectStrategy().sendRedirect(request, response, VERIFICATION_URL);}private boolean requestAndRegisterVerification(String phone) {try {return verificationService.requestVerification(phone) != null;} catch (VerificationRequestFailedException e) {return false;}}private void bypassVerification(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {verificationService.updateAuthentication(authentication);new DefaultRedirectStrategy().sendRedirect(request, response, INDEX_URL);}
}

用户成功通过身份验证后,我们将检查他们是否具有电话号码。

如果他们有电话号码,则将密码发送到他们的设备。 如果他们没有电话号码,或者我们无法发送代码,则允许他们绕过验证。

的绕过验证方法依赖于updateAuthentication的方法NexmoVerificationService。

将此添加到NexmoVerificationService:

// src/main/net/smcrow/demo/twofactor/verify/NexmoVerificationService.java
public void updateAuthentication(Authentication authentication) {Role role = retrieveRoleFromDatabase(authentication.getName());List<GrantedAuthority> authorities = new ArrayList<>();authorities.add(role);Authentication newAuthentication = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(),authentication.getCredentials(),authorities);SecurityContextHolder.getContext().setAuthentication(newAuthentication);
}private Role retrieveRoleFromDatabase(String username) {Optional<User> match = userRepository.findByUsername(username);if (match.isPresent()) {return match.get().getRole();}throw new UsernameNotFoundException("Username not found.");
}

此方法用于分配角色在数据库中为当前用户定义并删除PRE_VERIFICATION_USER 角色.

Prompting The User for a Code

Once the user has been sent a code, they are forwarded to the verification page. Let’s work on creating that page next.

创建一个新的HTML文件,名为verify.html在里面资源/模板目录。

<!DOCTYPE html>
<html lang="en"xmlns:th="http://www.thymeleaf.org"xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"layout:decorator="default">
<head><meta charset="UTF-8" /><title>Two Factor Authorization Demo</title>
</head>
<body>
<div layout:fragment="content" class="container"><div class="col-lg-12 alert alert-danger text-center" th:if="${param.error}">There was an error with your login.</div><div class="col-lg-4 offset-lg-4 text-left"><form th:action="@{/verify}" method="post"><h1>Verify</h1><p>A text message has been sent to your mobile device. Please enter the code below:</p><div class="form-group"><label for="code">Verification Code</label><input type="text" class="form-control" id="code" name="code" placeholder="4-Digit Code" /></div><button type="submit" class="btn btn-primary">Verify</button></form></div>
</div>
</body>
</html>

我们还需要一个控制器来将页面提供给用户。 创建验证控制器在里面校验包。

// src/main/net/smcrow/demo/twofactor/verify/VerificationController.java
@Controller
public class VerificationController {@Autowiredprivate NexmoVerificationService verificationService;@PreAuthorize("hasRole('PRE_VERIFICATION_USER')")@GetMapping("/verify")public String index() {return "verify";}@PreAuthorize("hasRole('PRE_VERIFICATION_USER')")@PostMapping("/verify")public String verify(@RequestParam("code") String code, Authentication authentication) {User user = ((StandardUserDetails) authentication.getPrincipal()).getUser();try {if (verificationService.verify(user.getPhone(), code)) {verificationService.updateAuthentication(authentication);return "redirect:/";}return "redirect:verify?error";} catch (VerificationRequestFailedException e) {// Having issues generating keys let them through.verificationService.updateAuthentication(authentication);return "redirect:/";}}
}

该控制器通过指数方法,并通过来处理表单提交校验方法。

只有拥有以下内容的用户才能访问此页面:PRE_VERIFICATION_USER角色。 成功验证后,updateAuthentication方法再次被用来将其保留为原来的角色。

Finishing Up the Verification Chain

最后一步是更新AppSecurityConfiguration使用我们的TwoFactorAuthenticationSuccessHandler。

修改AppSecurityConfiguration连接我们的处理程序并通过successHandler方法。

// src/main/net/smcrow/demo/twofactor/AppSecurityConfiguration.java
@Autowired
private TwoFactorAuthenticationSuccessHandler twoFactorAuthenticationSuccessHandler;@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {// Webjar resourceshttpSecurity.authorizeRequests().antMatchers("/webjars/**").permitAll().and().formLogin().loginPage("/login").permitAll().successHandler(twoFactorAuthenticationSuccessHandler).and().logout().permitAll();
}

Try it Out!

您需要将您的电话号码添加到data.sql文件。

We aren’t going to be doing any validation on the phone number, and it needs to be in E.164 format.

INSERT INTO user (username, password, role, phone) VALUES('phone', 'phone', 'USER', 15555555555);

您现在应该启动并运行了。 启动应用程序,然后尝试登录。假设您的API密钥,API机密和种子电话号码正确; 您应该会收到一条包含四位数代码的短信。

What Did We Do?

我们做了一个很多东西的。

简而言之,我们实施了两因素身份验证以更好地保护我们的应用程序。 我们这样做是:

  • 创建一个自定义AuthenticationSuccessHandler在向他们提供代码后将用户转发到验证页面。使用nexmo-java库,将其包装在NexmoVerificationService,以向我们的用户发送验证码。利用Spring Scheduler删除过期的验证码。建立一个页面供用户输入他们的验证码。

Check out the final code from this tutorial on GitHub.

Looking Ahead

有两种方法可以实现两因素身份验证。 如果您对示例代码中使用的任何框架和技术感到好奇,请参考以下清单:

  • Spring Boot
  • Spring Security
  • Gradle

Don’t forget that you can be a Nexmo contributor to the nexmo-java client.

from: https://dev.to//cr0wst/beefing-up-your-spring-security-with-two-factor-authentication-4m5p

两因素身份验证增强您的Spring Security相关推荐

  1. mongodb启用身份验证_为您的Web应用程序启用两因素身份验证

    mongodb启用身份验证 支持两因素身份验证(2FA)几乎总是一个好主意,尤其是对于后台系统. 2FA有许多不同的形式,其中一些包括SMS,TOTP甚至是硬件令牌 . 启用它们需要类似的流程: 用户 ...

  2. 为您的Web应用程序启用两因素身份验证

    支持两因素身份验证(2FA)几乎总是一个好主意,尤其是对于后台系统. 2FA有许多不同的形式,其中一些包括SMS,TOTP甚至是硬件令牌 . 启用它们需要类似的流程: 用户转到其个人资料页面(如果要在 ...

  3. laravel集成谷歌验证_如何将Google的两因素身份验证添加到Laravel

    laravel集成谷歌验证 Laravel is a wonderful PHP framework that makes building applications with PHP a lot o ...

  4. 如何在Raspberry Pi上设置两因素身份验证

    Kiklas/ShutterstockKiklas /快门 The Raspberry Pi is everywhere now, which is why it's caught the eye o ...

  5. 火狐和chrome_Firefox,Chrome和Edge都将支持WebAuthn的硬件两因素身份验证

    火狐和chrome Logging into Gmail or Facebook could soon mean plugging in a USB device, potentially makin ...

  6. 如何使用Google Authenticator在ASP.NET Core中设置两因素身份验证

    介绍 (Introduction) In this article, we are going to learn how to perform two-factor authentication in ...

  7. 亚马逊一直停留在身份验证_如何为您的Amazon帐户启用两因素身份验证

    亚马逊一直停留在身份验证 Two-Factor Authentication (2FA) is a great security tool, and we always recommend it. M ...

  8. facebook换手机验证_Facebook升级了两因素身份验证:这是设置方法

    facebook换手机验证 Facebook has had two-factor authentication for a long time, but it used to be called L ...

  9. twitter验证_如何为Twitter启用两因素身份验证

    twitter验证 Two-Factor Authentication (2FA) is a great security tool as it makes it harder for attacke ...

最新文章

  1. jQuery 插入元素
  2. 【知识便利贴】ImageNet得的比Best Paper还牛的Longuet-Higgins奖是什么?
  3. 系统垃圾清理.cmd
  4. SpringMVC 注解 : @ModelAttribute
  5. linux 路由跟踪命令_一文掌握linux系统路由跟踪指令traceroute
  6. 声音模仿_澳洲这种鸟堪称“超级声音模仿秀”,比八哥还牛,却正遭山火毁灭...
  7. 天线3db波束宽度_天线尺寸与频率,口径与波宽的关系
  8. Linux命令行运行多线程程序 和 QT集成IDE下运行多线程程序的问题。
  9. android service开启前台通知
  10. @property详细解读
  11. 树状数组(Binary Indexed Tree),看这一篇就够了
  12. 我的北京生活,2018面向新的开始
  13. JSP文件的运行过程
  14. jQuery 流星雨特效
  15. Python基于改进Unet的新冠肺炎等级分割系统(源码&教程)
  16. 阿里云服务器ssh经常掉线的解决办法
  17. Win10系统的截图功能 几种快捷键 以及QQ附带的截图功能
  18. 21天学通c语言思维导图,21天思维导图学习 ----开发脑洞、助力成长
  19. 基于C++实现MFC简单的纸牌小游戏,附纸牌游戏引擎源码!
  20. 【JPCS出版】2022年第三届控制理论与应用国际会议(ICoCTA 2022)

热门文章

  1. php 转ascii编码,php与ascii码的转换
  2. gtx1660是什么级别的_显卡天梯图秒懂GTX1660Ti性能 GTX1660Ti相当于什么显卡
  3. 微信公众平台开发-入门教程
  4. python图像分析_python数字图像处理(一)图像的常见操作
  5. vue-baidu-map使用setMapStyleV2自定义样式
  6. 人工智能产品普及的今天,软件测试人员也在自我进化
  7. 智商一般学计算机,IQ最高的十大专业公布 智商不高慎选
  8. OSG_64位动态链接库+静态链接库的使用
  9. win10如何关闭自动更新及修改更新时间
  10. 新晋云计算工程师就业的感受和经验分享