ToB企服应用市场:ToB评测及商务社交产业平台

标题: 使用 MongoDB 在 Spring Boot 中构建安全的 RBAC 系统 [打印本页]

作者: 万万哇    时间: 2024-10-3 12:04
标题: 使用 MongoDB 在 Spring Boot 中构建安全的 RBAC 系统
先容

您是否曾经构建过应用程序,然后突然意识到需要以更精细的方式管理用户访问权限?也许您已经硬编码了一些管理检查或在整个代码库中分散了权限逻辑。信赖我,我经历过这种情况,维护起来并不好玩。
这就是基于脚色的访问控制 (RBAC) 的作用所在。这是一种基于用户脚色管理用户权限的标准化方法,可让您的应用程序更安全、更易于维护。在这篇文章中,我将引导您使用 MongoDB 在 Spring Boot 应用程序中实现 RBAC。我们将先容从设置项目到保护您的端点的全部内容。

先决条件

在深入研究之前,请确保您已举行以下设置:

您还需要对以下内容有基本的相识:

设置项目

1. 创建一个新的 Spring Boot 项目

首先,让我们设置我们的 Spring Boot 项目。您可以使用 Spring Initializr 或您最喜好的 IDE。包罗以下依赖项:

2. 更新 pom.xml

确保您的 pom.xml 文件包罗必要的依赖项:
  1. <project ...>
  2.     <!-- ... other configurations ... -->
  3.     <dependencies>
  4.         <!-- Spring Boot Starter Web -->
  5.         <dependency>
  6.             <groupId>org.springframework.boot</groupId>
  7.             <artifactId>spring-boot-starter-web</artifactId>
  8.         </dependency>
  9.         <!-- Spring Boot Starter Security -->
  10.         <dependency>
  11.             <groupId>org.springframework.boot</groupId>
  12.             <artifactId>spring-boot-starter-security</artifactId>
  13.         </dependency>
  14.         <!-- Spring Data MongoDB -->
  15.         <dependency>
  16.             <groupId>org.springframework.boot</groupId>
  17.             <artifactId>spring-boot-starter-data-mongodb</artifactId>
  18.         </dependency>
  19.         <!-- Lombok (Optional) -->
  20.         <dependency>
  21.             <groupId>org.projectlombok</groupId>
  22.             <artifactId>lombok</artifactId>
  23.             <version>1.18.24</version>
  24.             <scope>provided</scope>
  25.         </dependency>
  26.         <!-- ... other dependencies ... -->
  27.     </dependencies>
  28.     <!-- ... other configurations ... -->
  29. </project>
复制代码
设置 MongoDB

1. 添加 MongoDB 连接详细信息

我们需要将应用程序连接到 MongoDB。在您的 application.properties 或中 application.yml,添加以下内容:
  1. spring.data.mongodb.uri=mongodb://localhost:27017/rbac_db
复制代码
请随意将 rbac_db 替换为您喜好的数据库名称。
定义实体

现在,让我们定义 RBAC 系统的核心实体:Permission、Role 和 User。
1. Permission.java

  1. package com.example.rbac.model;
  2. import org.springframework.data.annotation.Id;
  3. import org.springframework.data.mongodb.core.mapping.Document;
  4. import lombok.Data;
  5. @Data
  6. @Document(collection = "permissions")
  7. public class Permission {
  8.     @Id
  9.     private String id;
  10.     private String name;
  11. }
复制代码
2. Role.java

  1. package com.example.rbac.model;
  2. import org.springframework.data.annotation.Id;
  3. import org.springframework.data.mongodb.core.mapping.Document;
  4. import lombok.Data;
  5. import java.util.Set;
  6. @Data
  7. @Document(collection = "roles")
  8. public class Role {
  9.     @Id
  10.     private String id;
  11.     private String name;
  12.     private Set<Permission> permissions;
  13. }
复制代码
3. User.java

  1. package com.example.rbac.model;
  2. import org.springframework.data.annotation.Id;
  3. import org.springframework.data.mongodb.core.mapping.Document;
  4. import lombok.Data;
  5. import java.util.Set;
  6. @Data
  7. @Document(collection = "users")
  8. public class User {
  9.     @Id
  10.     private String id;
  11.     private String username;
  12.     private String password; // We'll store hashed passwords
  13.     private Set<Role> roles;
  14. }
复制代码
简要阐明:始终以哈希格式存储暗码。稍后我们将先容如何对暗码举行哈希处理。
创建存储库

存储库是我们的应用程序和数据库之间的桥梁。让我们为我们的实体创建它们
1. UserRepository.java

  1. package com.example.rbac.repository;
  2. import org.springframework.data.mongodb.repository.MongoRepository;
  3. import com.example.rbac.model.User;
  4. public interface UserRepository extends MongoRepository<User, String> {
  5.     User findByUsername(String username);
  6. }
复制代码
2. RoleRepository.java

  1. package com.example.rbac.repository;
  2. import org.springframework.data.mongodb.repository.MongoRepository;
  3. import com.example.rbac.model.Role;
  4. public interface RoleRepository extends MongoRepository<Role, String> {
  5.     Role findByName(String name);
  6. }
复制代码
3. PermissionRepository.java

  1. package com.example.rbac.repository;
  2. import org.springframework.data.mongodb.repository.MongoRepository;
  3. import com.example.rbac.model.Permission;
  4. public interface PermissionRepository extends MongoRepository<Permission, String> {
  5.     Permission findByName(String name);
  6. }
复制代码
实现 UserDetailsService

为了与 Spring Security 集成,我们将实现一个自定义的 UserDetailsService。
CustomUserDetailsService.java
  1. package com.example.rbac.service;
  2. import org.springframework.security.core.userdetails.*;
  3. import org.springframework.stereotype.Service;
  4. import com.example.rbac.model.User;
  5. import com.example.rbac.model.Role;
  6. import com.example.rbac.model.Permission;
  7. import com.example.rbac.repository.UserRepository;
  8. import java.util.*;
  9. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  10. @Service
  11. public class CustomUserDetailsService implements UserDetailsService {
  12.     private final UserRepository userRepository;
  13.     public CustomUserDetailsService(UserRepository userRepository) {
  14.        this.userRepository = userRepository;
  15.     }
  16.     @Override
  17.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  18.        User user = userRepository.findByUsername(username);
  19.        if (user == null) {
  20.            throw new UsernameNotFoundException("User not found");
  21.        }
  22.        return new org.springframework.security.core.userdetails.User(
  23.            user.getUsername(),
  24.            user.getPassword(),
  25.            getAuthorities(user.getRoles())
  26.        );
  27.     }
  28.     private Collection<SimpleGrantedAuthority> getAuthorities(Set<Role> roles) {
  29.         Set<SimpleGrantedAuthority> authorities = new HashSet<>();
  30.         for (Role role : roles) {
  31.            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
  32.            for (Permission permission : role.getPermissions()) {
  33.                authorities.add(new SimpleGrantedAuthority(permission.getName()));
  34.            }
  35.         }
  36.         return authorities;
  37.     }
  38. }
复制代码
这里发生了什么?

设置 Spring Security

现在,让我们设置 Spring Security 来使用我们的自定义 UserDetailsService。
SecurityConfig.java
  1. package com.example.rbac.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.security.config.annotation.authentication.builders.*;
  5. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  6. import org.springframework.security.web.SecurityFilterChain;
  7. import com.example.rbac.service.CustomUserDetailsService;
  8. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  9. @Configuration
  10. public class SecurityConfig {
  11.     private final CustomUserDetailsService userDetailsService;
  12.     public SecurityConfig(CustomUserDetailsService userDetailsService) {
  13.         this.userDetailsService = userDetailsService;
  14.     }
  15.     @Bean
  16.     public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  17.         http
  18.             .csrf(csrf -> csrf.disable())
  19.             .authorizeHttpRequests(authz -> authz
  20.                 .requestMatchers("/admin/**").hasRole("ADMIN")
  21.                 .requestMatchers("/user/**").hasRole("USER")
  22.                 .anyRequest().authenticated()
  23.             )
  24.             .formLogin(form -> form
  25.                 .loginPage("/login").permitAll()
  26.             )
  27.             .logout(logout -> logout.permitAll());
  28.         return http.build();
  29.     }
  30.     @Bean
  31.     public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
  32.         return http
  33.             .getSharedObject(AuthenticationManagerBuilder.class)
  34.             .userDetailsService(userDetailsService)
  35.             .passwordEncoder(passwordEncoder())
  36.             .and()
  37.             .build();
  38.     }
  39.     @Bean
  40.     public BCryptPasswordEncoder passwordEncoder() {
  41.         return new BCryptPasswordEncoder();
  42.     }
  43. }
复制代码
要点:

安全管理暗码

安全至关紧张,尤其是用户暗码。确保在存储暗码之前对其举行哈希处理。
UserService.java
  1. package com.example.rbac.service;
  2. import org.springframework.stereotype.Service;
  3. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  4. import com.example.rbac.model.User;
  5. import com.example.rbac.repository.UserRepository;
  6. @Service
  7. public class UserService {
  8.     private final UserRepository userRepository;
  9.     private final BCryptPasswordEncoder passwordEncoder;
  10.     public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
  11.        this.userRepository = userRepository;
  12.        this.passwordEncoder = passwordEncoder;
  13.     }
  14.     public void saveUser(User user) {
  15.         user.setPassword(passwordEncoder.encode(user.getPassword()));
  16.         userRepository.save(user);
  17.     }
  18. }
复制代码
为什么选择 BCrypt?
BCrypt 是一种盛行的哈希算法,专为哈希暗码而筹划。它包罗盐以防止彩虹表攻击,并且计算密集型以防止暴力攻击。
定义控制器和端点

是时候设置我们的 REST 控制器来处理传入的请求了。
1. AdminController.java

  1. package com.example.rbac.controller;
  2. import org.springframework.web.bind.annotation.*;
  3. @RestController
  4. @RequestMapping("/admin")
  5. public class AdminController {
  6.     @GetMapping("/dashboard")
  7.     public String adminDashboard() {
  8.         return "Welcome to the Admin Dashboard!";
  9.     }
  10. }
复制代码
2. UserController.java

  1. package com.example.rbac.controller;
  2. import org.springframework.web.bind.annotation.*;
  3. @RestController
  4. @RequestMapping("/user")
  5. public class UserController {
  6.     @GetMapping("/profile")
  7.     public String userProfile() {
  8.         return "Welcome to your Profile!";
  9.     }
  10. }
复制代码
处理身份验证

我们将创建一个简朴的控制器来处理登录请求。
WebController.java
  1. package com.example.rbac.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. @Controller
  5. public class WebController {
  6.     @GetMapping("/login")
  7.     public String login() {
  8.         return "login"; // This should correspond to a Thymeleaf template
  9.     }
  10. }
复制代码
不要忘记视图!
如果您使用 Thymeleaf,请确保 src/main/resources/templates/ 下有一个 login.html 模板。
创建初始数据

为了测试我们的应用程序,让我们预加载一些脚色、权限和用户。
DataLoader.java
  1. package com.example.rbac.config;
  2. import org.springframework.boot.CommandLineRunner;
  3. import org.springframework.stereotype.Component;
  4. import com.example.rbac.repository.RoleRepository;
  5. import com.example.rbac.repository.PermissionRepository;
  6. import com.example.rbac.repository.UserRepository;
  7. import com.example.rbac.model.Permission;
  8. import com.example.rbac.model.Role;
  9. import com.example.rbac.model.User;
  10. import com.example.rbac.service.UserService;
  11. import java.util.Set;
  12. @Component
  13. public class DataLoader implements CommandLineRunner {
  14.     private final RoleRepository roleRepository;
  15.     private final PermissionRepository permissionRepository;
  16.     private final UserService userService;
  17.     public DataLoader(RoleRepository roleRepository, PermissionRepository permissionRepository, UserService userService) {
  18.         this.roleRepository = roleRepository;
  19.         this.permissionRepository = permissionRepository;
  20.         this.userService = userService;
  21.     }
  22.     @Override
  23.     public void run(String... args) throws Exception {
  24.         // Create Permissions
  25.         Permission readPermission = new Permission();
  26.         readPermission.setName("READ_PRIVILEGE");
  27.         permissionRepository.save(readPermission);
  28.         Permission writePermission = new Permission();
  29.         writePermission.setName("WRITE_PRIVILEGE");
  30.         permissionRepository.save(writePermission);
  31.         // Create Roles
  32.         Role adminRole = new Role();
  33.         adminRole.setName("ADMIN");
  34.         adminRole.setPermissions(Set.of(readPermission, writePermission));
  35.         roleRepository.save(adminRole);
  36.         Role userRole = new Role();
  37.         userRole.setName("USER");
  38.         userRole.setPermissions(Set.of(readPermission));
  39.         roleRepository.save(userRole);
  40.         // Create Users
  41.         User adminUser = new User();
  42.         adminUser.setUsername("admin");
  43.         adminUser.setPassword("admin123"); // Password will be hashed
  44.         adminUser.setRoles(Set.of(adminRole));
  45.         userService.saveUser(adminUser);
  46.         User normalUser = new User();
  47.         normalUser.setUsername("user");
  48.         normalUser.setPassword("user123"); // Password will be hashed
  49.         normalUser.setRoles(Set.of(userRole));
  50.         userService.saveUser(normalUser);
  51.     }
  52. }
复制代码
发生了什么?

测试应用程序

让我们确保统统按预期运行。
1. 运行应用程序

启动您的 Spring Boot 应用程序:
  1. mvn spring-boot:run
复制代码
2. 访问登录页面

导航至 http://localhost:8080/login。您应该会看到您的登录页面。
3. 测试用户身份验证

管理员用户

登录后,实行访问:

普通用户

登录后,实行访问:

结论

就如许!我们使用 Spring Boot 和 MongoDB 构建了一个简朴但强大的 RBAC 系统。以下是我们所完成工作的简要回顾:

下一步:
您可以通过添加更多脚色、权限和安全端点来扩展此应用程序。您还可以集成 JWT 举行无状态身份验证或添加前端以与您的 API 交互。
其他最佳实践

虽然我们已经先容了底子知识,但另有以下一些最佳做法可供思量:

文章原地址

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4