如何绑定Spring用户与领域对象?实现用户专属账户访问
实现内存用户与银行账户绑定的方案
1. 改造账户模型为纯内存类型
首先移除JPA相关注解(无需持久化),保留lombok注解简化代码:
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class SavingsAccount { private int id; private long savings; // 单位为分 }
2. 方案一:通过内存Map关联用户与账户
这种方式简单直接,适合快速测试:
2.1 配置内存用户与账户映射
在Spring Security配置类中,创建内存Map存储用户名到账户的关联,同时初始化测试用户数据:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import java.util.HashMap; import java.util.Map; @Configuration public class SecurityConfig { // 内存存储:用户名 -> 对应账户 private final Map<String, SavingsAccount> userAccountMap = new HashMap<>(); @Bean public UserDetailsService userDetailsService() { // 初始化测试用户及对应账户 SavingsAccount userAccount = new SavingsAccount(1, 10000); // 100元 userAccountMap.put("user", userAccount); UserDetails user = User.withDefaultPasswordEncoder() .username("user") .password("password") .roles("USER") .build(); // 可选:添加第二个测试用户 SavingsAccount adminAccount = new SavingsAccount(2, 50000); // 500元 userAccountMap.put("admin", adminAccount); UserDetails admin = User.withDefaultPasswordEncoder() .username("admin") .password("admin") .roles("ADMIN", "USER") .build(); return new InMemoryUserDetailsManager(user, admin); } // 提供账户查询Bean,供控制器调用 @Bean public Function<String, SavingsAccount> accountLookup() { return username -> userAccountMap.get(username); } }
2.2 控制器中获取当前用户账户
通过Principal获取当前登录用户名,再从内存Map中取出对应账户:
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.security.Principal; import java.util.function.Function; @Controller public class AccountController { private final Function<String, SavingsAccount> accountLookup; public AccountController(Function<String, SavingsAccount> accountLookup) { this.accountLookup = accountLookup; } @GetMapping("/account/balance") @ResponseBody public String getBalance(Principal principal) { String username = principal.getName(); SavingsAccount account = accountLookup.apply(username); if (account == null) { return "账户不存在"; } double balance = account.getSavings() / 100.0; return String.format("当前账户余额:%.2f 元", balance); } }
3. 方案二:自定义UserDetails绑定账户到用户对象
这种方式将账户直接嵌入用户详情对象,关联更紧密:
3.1 自定义UserDetails实现
继承Spring Security的User类,添加账户属性:
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; public class CustomUserDetails extends User { private final SavingsAccount savingsAccount; public CustomUserDetails(String username, String password, Collection<? extends GrantedAuthority> authorities, SavingsAccount savingsAccount) { super(username, password, authorities); this.savingsAccount = savingsAccount; } public SavingsAccount getSavingsAccount() { return savingsAccount; } }
3.2 配置自定义用户详情
在Security配置类中构建自定义用户对象:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @Configuration public class SecurityConfig { @Bean public UserDetailsService userDetailsService() { // 初始化用户与账户 SavingsAccount userAccount = new SavingsAccount(1, 10000); CustomUserDetails user = new CustomUserDetails( "user", "{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW", // 对应密码"password" AuthorityUtils.createAuthorityList("ROLE_USER"), userAccount ); SavingsAccount adminAccount = new SavingsAccount(2, 50000); CustomUserDetails admin = new CustomUserDetails( "admin", "{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW", // 对应密码"admin" AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER"), adminAccount ); return new InMemoryUserDetailsManager(user, admin); } }
注:这里使用预加密的密码值,避免
withDefaultPasswordEncoder()的不安全写法(仅适合快速测试场景)。
3.3 控制器中直接获取绑定的账户
通过Authentication获取当前用户的自定义详情对象:
import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class AccountController { @GetMapping("/account/balance") @ResponseBody public String getBalance(Authentication authentication) { CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); SavingsAccount account = userDetails.getSavingsAccount(); double balance = account.getSavings() / 100.0; return String.format("当前账户余额:%.2f 元", balance); } }
说明
两种方案均为纯内存实现,重启服务后数据会重置,完全满足测试需求。用户登录后只能获取自身绑定的账户信息,符合权限控制要求。
内容的提问来源于stack exchange,提问作者TMOTTM
相关产品推荐
相关产品推荐

