You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何绑定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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 05:30:44