前言:
眼前朋友们对“服务器有哪些认证”大致比较关心,你们都想要剖析一些“服务器有哪些认证”的相关资讯。那么小编也在网络上收集了一些有关“服务器有哪些认证””的相关知识,希望同学们能喜欢,咱们一起来了解一下吧!从本文将开始编写示例的实现。第一个依赖项是身份认证服务器。即使它不是我们所关注的使用 Spring Security 的应用程序,我们也需要它来实现我们的最终结果。为了让您专注于实践中最重要的部分,我列出了实现的一些部分。我在整个示例中都提到了这些内容,并将其留给您作为练习来实现。
在我们的场景中,身份认证服务器连接到一个数据库,在该数据库中存储在请求身份认证事件期间生成的用户凭据和 OTPs。我们需要这个应用程序公开三个端点 ( 图 9 ):
/user/add -- 添加一个用户,用于测试我们后面的实现。/user/auth -- 通过用户的凭证对用户进行身份认证,并使用 OTP 发送短信。我们去掉了发送短信的部分,但你可以把它作为练习来做。/otp/check -- 验证 OTP 值是否为先前认证服务器为特定用户生成的值。
图 9 身份认证服务器的类设计控制器公开调用服务类中定义的逻辑的 REST 端点。这两个存储库是数据库的访问层。我们还编写了一个实用工具类来分离生成要通过 SMS 发送的 OTP 的验证码。
我们创建一个新项目并添加所需的依赖项,如下面的代码片段所示。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency>
我们还需要确保为应用程序创建了数据库。因为我们存储用户凭据 ( 用户名和密码 ),所以需要一个表。我们还需要第二个表来存储与经过身份认证的用户相关联的 OTP 值 ( 图 10 )。
图 10 应用程序数据库有两个表。在第一个表中,应用程序存储用户凭证,而在第二个表中,应用程序存储生成的 OTP 验证码。
使用一个名为 spring 的数据库,并添加脚本来创建 schema.sql 文件中所需的两个表。 切记将 schema.sql 文件放置在项目的 resources 文件夹中,因为 Spring Boot 会在这里拾取它来执行脚本。 在下一个代码段中,您可以找到我的 schema.sql 文件的内容。 (如果您不喜欢使用 schema.sql 文件的方法,则可以随时手动创建数据库结构,也可以使用自己喜欢的任何其他方法。)
CREATE TABLE IF NOT EXISTS `spring`.`user` ( `username` VARCHAR(45) NULL, `password` TEXT NULL, PRIMARY KEY (`username`));CREATE TABLE IF NOT EXISTS `spring`.`otp` ( `username` VARCHAR(45) NOT NULL, `code` VARCHAR(45) NULL, PRIMARY KEY (`username`));
在 application.properties 文件中,我们提供了 Spring Boot 创建数据源所需的参数。 下一个代码片段显示了 application.properties 文件的内容:
spring.datasource.url=jdbc:mysql://localhost/springspring.datasource.username=rootspring.datasource.password=spring.datasource.initialization-mode=always
还为这个应用程序的依赖项添加了 Spring Security。我对身份认证服务器这样做的唯一原因是获得 BCryptPasswordEncoder ,我喜欢使用它来散列存储在数据库中的用户密码。为了使示例简短并与我们的目的相关,我没有在业务逻辑服务器和身份认证服务器之间实现身份认证。但是我想把这个留给您作为稍后的练习,在完成这个实际示例之后。对于我们在本文中讨论的实现,项目的配置类如清单 1 所示。
练习
更改本文中的应用程序,以验证业务逻辑服务器和身份认证服务器之间的请求:
通过使用对称密钥
使用非对称密钥对
为了解决这个练习,您可能会发现参考我们在前面文章中的示例(在过滤器链中已存在的过滤前添加过滤器)。
清单 1 身份认证服务器的配置类
@Configurationpublic class ProjectConfig extends WebSecurityConfigurerAdapter { // 定义密码编码器以哈希存储在数据库中的密码 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); // 禁用 CSRF,这样我们就可以直接调用应用程序的所有端点 http.authorizeRequests() //允许所有无需身份认证就可调用 .anyRequest().permitAll(); }}
配置类就绪后,我们可以继续定义到数据库的连接。因为我们使用 Spring Data JPA,所以我们需要编写 JPA 实体,然后是存储库,因为我们有两个表,所以我们定义了两个 JPA 实体和两个存储库接口。下面的清单显示了 User 实体的定义。它表示存储用户凭证的用户表。
清单 2 User 实体
@Entitypublic class User { @Id private String username; private String password; // Omitted getters and setters}
下一个清单展示了第二个实体 Otp。这个实体表示 otp 表,应用程序在其中存储为经过身份认证的用户生成的otp。
清单 3 Otp 实体
@Entitypublic class Otp { @Id private String username; private String code; // Omitted getters and setters}
清单 4 展示了 User 实体的 Spring Data JPA 存储库。在这个接口中,我们定义了一个根据用户名检索用户的方法。在验证用户名和密码的第一步中,我们需要它。
清单 4 UserRepository 接口
public interface UserRepository extends JpaRepository<User, String> { Optional<User> findUserByUsername(String username);}
清单 5 给出了用于 Otp 实体的 Spring Data JPA 存储库。在这个接口中,我们定义了一个根据用户名检索 OTP 的方法。我们需要在第二个验证步骤中使用这个方法,在这个步骤中,我们验证用户的 OTP。
清单 5 OtpRepository 接口
public interface OtpRepository extends JpaRepository<Otp, String> { Optional<Otp> findOtpByUsername(String username);}
存储库和实体就绪后,我们就可以处理应用程序的逻辑了。为此,我创建了一个称为 UserService 的服务类。如清单 6 所示,该服务依赖于存储库和密码编码器。因为我们使用这些对象来实现应用程序逻辑,所以我们需要自动装配它们。
清单 6 自动装配 UserService 类中的依赖项
@Service@Transactionalpublic class UserService { @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserRepository userRepository; @Autowired private OtpRepository otpRepository;}
接下来,我们需要定义一个方法来添加用户。您可以在下面的清单中找到这个方法的定义。
清单 7 addUser() 方法的定义
@Service@Transactionalpublic class UserService { // Omitted code public void addUser(User user) { user.setPassword(passwordEncoder.encode(user.getPassword())); userRepository.save(user); }}
业务逻辑服务器需要什么? 它需要一种发送用户名和密码以进行身份认证的方法。认证成功后,认证服务器会为用户生成一个 OTP,并通过短信发送。下面的清单显示了 auth() 方法的定义,该方法实现了这个逻辑。
清单 8 实现第一个步骤身份验证
@Service@Transactionalpublic class UserService { // Omitted code public void auth(User user) { //搜索数据库中的用户 Optional<User> o = userRepository.findUserByUsername(user.getUsername()); //如果该用户存在,则会验证其密码 if(o.isPresent()) { User u = o.get(); if (passwordEncoder.matches( user.getPassword(), u.getPassword())) { //如果密码正确,则会生成一个新的 OTP renewOtp(u); } else { //如果密码不正确或用户名不存在,则会抛出异常 throw new BadCredentialsException("Bad credentials."); } } else { //如果密码不正确或用户名不存在,则会抛出异常 throw new BadCredentialsException("Bad credentials."); } } private void renewOtp(User u) { //为 OTP 生成一个随机值 String code = GenerateCodeUtil.generateCode(); //按用户名查找OTP Optional<Otp> userOtp =otpRepository.findOtpByUsername(u.getUsername()); if (userOtp.isPresent()) { //如果此用户名存在 OTP,则更新其值 Otp otp = userOtp.get(); otp.setCode(code); } else { //如果这个用户名的 OTP 不存在,则用生成的值创建一个新记录 Otp otp = new Otp(); otp.setUsername(u.getUsername()); otp.setCode(code); otpRepository.save(otp); } } // Omitted code}
下一个清单展示了 GenerateCodeUtil 类。我们在清单 8 中使用这个类来生成新的 OTP 值。
清单 9 生成 OTP
public final class GenerateCodeUtil { private GenerateCodeUtil() {} public static String generateCode() { String code; try { // 创建一个生成随机 int 值的 SecureRandom 实例 SecureRandom random = SecureRandom.getInstanceStrong(); //生成0到8,999之间的值。我们给每个生成的值加1000。这样,我们得到1000到9999(4位随机码)之间的值。 int c = random.nextInt(9000) + 1000; //将int转换为String并返回它 code = String.valueOf(c); } catch (NoSuchAlgorithmException e) { throw new RuntimeException( "Problem when generating the random code."); } return code; }}
UserService 中需要的最后一个方法是验证用户的 OTP。您可以在下面的清单中找到此方法。
清单 10 验证 OTP
@Service@Transactionalpublic class UserService { / Omitted code public boolean check(Otp otpToValidate) { Optional<Otp> userOtp = //按用户名搜索 OTP otpRepository.findOtpByUsername( otpToValidate.getUsername()); if (userOtp.isPresent()) { //如果数据库中存在 OTP,并且与从业务逻辑服务器接收到的 OTP 相同,则返回 true。 Otp otp = userOtp.get(); if (otpToValidate.getCode().equals(otp.getCode())) { return true; } } //否则,它将返回 false。 return false; } // Omitted code}
最后,在这个应用程序中,我们公开了控制器提供的逻辑。下面的清单定义了这个控制器。
清单 11 AuthController 类的定义
@RestControllerpublic class AuthController { @Autowired private UserService userService; @PostMapping("/user/add") public void addUser(@RequestBody User user) { userService.addUser(user); } @PostMapping("/user/auth") public void auth(@RequestBody User user) { userService.auth(user); } //如果 OTP 有效,则 HTTP 响应将返回状态 200 OK;否则,状态为 200。 否则,状态值为 403 Forbidden。 @PostMapping("/otp/check") public void check(@RequestBody Otp otp, HttpServletResponse response) { if (userService.check(otp)) { response.setStatus(HttpServletResponse.SC_OK); } else { response.setStatus(HttpServletResponse.SC_FORBIDDEN); } }}
有了这个设置,我们现在就有了身份认证服务器。让我们开始它,并确保端点按我们期望的方式工作。为了测试身份认证服务器的功能,我们需要:
通过调用 /user/add 端点向数据库添加新用户;通过检查数据库中的 users 表,验证是否正确添加了用户;调用第 1 步中添加的用户的 /user/auth 端点;验证应用程序是否生成了一个 OTP 并将其存储在 OTP 表中;使用步骤 3 中生成的 OTP 来验证 /otp/check 端点是否按预期工作.
我们首先将用户添加到身份认证服务器的数据库中。我们至少需要一个用户来进行身份认证。我们可以通过调用在身份认证服务器中创建的 /user/add 端点来添加用户。因为我们没有在身份认证服务器应用程序中配置端口,所以我们使用缺省端口,即 8080。这是调用:
curl -XPOST -H "content-type: application/json" -d "{\"username\":\"xiaohua\",\"password\":\"12345\"}"
在使用前面代码片段提供的 curl 命令添加用户之后,我们检查数据库以验证添加的记录是否正确。在我的案例中,我可以看到以下细节:
Username: xiaohuaPassword: $2a$10$.bI9ix.Y0m70iZitP.RdSuwzSqgqPJKnKpRUBQPGhoRvHA.1INYmy
应用程序在将密码存储到数据库之前将其哈希,这是预期的行为。请记住,我们在身份认证服务器中特别为此使用了 BCryptPasswordEncoder。
注意
记住,在我们密码实现文章的讨论中,BCryptPasswordEncoder 使用 bcrypt 作为哈希算法。使用 bcrypt,输出是基于 salt (盐) 生成的,这意味着您可以为相同的输入获得不同的输出。对于本例,相同密码的散列在您的情况下是不同的。你可以在 David Wong (Manning, 2020)的《真实世界密码学》第2章中找到关于哈希函数的更多细节和精彩讨论:。
我们有一个用户,所以让我们通过调用 /user/auth 端点来为该用户生成一个 OTP。下面的代码片段提供了您可以使用的 cURL 命令:
curl -XPOST -H "content-type: application/json" -d "{\"username\":\"xiaohua\",\"password\":\"12345\"}" http:/./localhost:8080/user/auth
在数据库的 otp 表中,应用程序生成并存储一个随机的四位数验证码。在我的例子中,它的值是 8527。
测试身份认证服务器的最后一步是调用 /otp/check 端点,并验证当 OTP 响应中返回一个 HTTP 200 OK 状态码,如果 OTP 错误则返回 403 Forbidden 状态码。下面的代码片段展示了正确的 OTP 值的测试,以及错误的OTP 值的测试。如果 OTP 值正确:
curl -v -XPOST -H "content-type: application/json" -d "{\"username\":\"xiaohua\",\"code\":\"8527\"}" http:/./localhost:8080/otp/check
响应状态:
...< HTTP/1.1 200...
如果OTP值错误:
curl -v -XPOST -H "content-type: application/json" -d "{\"username\":\"xiaohua\",\"code\":\"8888\"}" http:/./localhost:8080/otp/check
响应状态为:
...< HTTP/1.1 403...
我们刚刚证明了身份认证服务器组件是有效的 !现在,我们可以深入研究下一个组件——业务逻辑服务器,我们为它编写了当前实际示例的大部分 Spring Security 配置。
标签: #服务器有哪些认证