百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

SpringBoot的Security安全控制—企业项目中的SpringSecurity操作

liuian 2025-07-27 21:59 98 浏览

企业项目中的Spring Security操作

面的章节从内置数据入手开始介绍Spring Security的入门案例。在实际的企业级开发中,一般不会把用户名和密码固定在代码或者配置文件中,而是直接在数据库中查询用户的账号和密码,再将其和用户输入的账号和密码进行对比并认证,最终完成用户的认证和授权查询。下面使用国内目前常用的两个数据库操作框架(Spring Data JPA和MyBatis)完成对SpringSecurity的查询和认证,读者只需要掌握其中的一个框架即可,建议优先选用自己熟悉的框架。

实战:基于JPA的Spring Boot Security操作

新建一个spring-security-db-demo项目,具体步骤如下:

(1)在pom.xml中添加Spring Security和JPA,即MySQL和Web开发所需要的依赖,代码如下:

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.3.10.RELEASE</version>

<relativePath/>

</parent>

<groupId>com.example</groupId>

<artifactId>spring-security-db-demo</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>spring-security-db-demo</name>

<description>Demo project for Spring Boot</description>

<properties>

<java.version>11</java.version>

</properties>

<dependencies>

<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

<exclusions>

<exclusion>

<groupId>org.junit.vintage</groupId>

<artifactId>junit-vintage-engine</artifactId>

</exclusion>

</exclusions>

</dependency>

<!--spring data jpa-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<!--spring security-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-security</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<!--thymeleaf模板-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-thymeleaf</artifactId>

</dependency>

<!--thymeleaf中使用的Spring Security标签-->

<dependency>

<groupId>org.thymeleaf.extras</groupId>

<artifactId>thymeleaf-extras-springsecurity5</artifactId>

<!-- <version>3.0.3.RELEASE</version>-->

</dependency>

<dependency>

<groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.projectlombok</groupId>

<artifactId>lombok</artifactId>

<optional>true</optional>

</dependency>

<dependency>

<groupId>cn.hutool</groupId>

<artifactId>hutool-all</artifactId>

<version>5.5.7</version>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

(2)为了让项目开发具有多样性,本次使用的配置文件格式是yml,在application.yml中添加项目配置,用来配置数据库的连接信息。使用sys数据库的配置信息如下:

server:

port: 8080

spring:

datasource:

username: root

password: 123456

url: jdbc:mysql://127.0.0.1:3306/sys

driver-class-name: com.mysql.cj.jdbc.Driver

jpa:

hibernate: ddl-auto: update

database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

open-in-view: false

(3)开始编写项目代码,新建Security的配置文件WebSecurityConfig.java:

package com.example.springsecuritydbdemo.config;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.Authentication

Provider;

import

org.springframework.security.authentication.dao.DaoAuthentication

Provider;

import org.springframework.security.config.annotation.authentication.

builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.method.

configuration.EnableGlobalMethodSecurity;

import org.springframework.security.config.annotation.web.builders.

HttpSecurity;

import

org.springframework.security.config.annotation.web.configuration.

WebSecurityConfigurerAdapter;

import

org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.crypto.factory.PasswordEncoder

Factories;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.security.web.authentication.rememberme.

JdbcTokenRepositoryImpl;

import org.springframework.security.web.authentication.rememberme.

PersistentTokenRepository;

import javax.sql.DataSource;/**

* 开启security注解

*/

@Configuration

@EnableGlobalMethodSecurity(securedEnabled = true)

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

private UserDetailsService userDetailsService;

@Autowired

private PersistentTokenRepository persistentTokenRepository;

@Override

protected void configure(AuthenticationManagerBuilder auth) throws

Exception {

auth.authenticationProvider(authenticationProvider());

}

@Override

protected void configure(HttpSecurity http) throws Exception {

//关闭csrf

http.csrf().disable();

// 自定义登录页面

http.formLogin()

.loginPage("/loginPage") // 登录页面的

URL

// 登录访问路径,不用自己处理逻辑,只需要定义URL即可

.loginProcessingUrl("/login")

.failureUrl("/exception") // 登录失败时跳

转的路径

.defaultSuccessUrl("/index", true); // 登录成功后跳

转的路径

// URL的拦截与放行,除//loginPage、/hello、/exception和/*.jpg之外的

路径都会被拦截

http.authorizeRequests()

.antMatchers("/loginPage", "/hello", "/exception",

"/*.jpg").permitAll()

.anyRequest().authenticated();

// 注销用户

http.logout().logoutUrl("/logout");

// 记住密码(自动登录) http.rememberMe().tokenRepository(persistentTokenRepository).

tokenValiditySeconds(60 * 60).userDetailsService(userDetailsService);

}

/**

* 登录提示

*/

@Bean

public AuthenticationProvider authenticationProvider() {

DaoAuthenticationProvider provider = new

DaoAuthenticationProvider();

// 显示用户找不到异常,默认不论用户名和密码哪个错误,都提示密码错误

provider.setHideUserNotFoundExceptions(false);

provider.setPasswordEncoder(passwordEncoder());

provider.setUserDetailsService(userDetailsService);

return provider;

}

/**

* 密码加密器

*/

@Bean

public PasswordEncoder passwordEncoder() {

return

PasswordEncoderFactories.createDelegatingPasswordEncoder();

}

/**

* 记住密码,并存储Token

*/

@Bean

public PersistentTokenRepository

persistentTokenRepository(DataSourcedataSource) {

// 数据存储在数据库中

JdbcTokenRepositoryImpl jdbcTokenRepository = new

JdbcTokenRepositoryImpl();

jdbcTokenRepository.setDataSource(dataSource);

return jdbcTokenRepository;

}

}

(4)新建Web请求的UserControllerjava入口文件,并定义其访问的URL:

package com.example.springsecuritydbdemo.controller;

import lombok.extern.slf4j.Slf4j;

import org.springframework.security.access.annotation.Secured;

import org.springframework.security.core.AuthenticationException;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;

@Controller

@Slf4j

public class UserController {

@ResponseBody

@RequestMapping("/hello")

public String hello() {

return "hello";

}

/**

* 登录页面

*/

@GetMapping("/loginPage")

public String login() {

return "login";

}

/**

* Security 认证异常处理

*/

@GetMapping("/exception")

public String error(HttpServletRequest request) {

// 获取Spring Security的AuthenticationException异常并抛出,由全局异

常统一处理

AuthenticationException exception = (AuthenticationException)

WebUtils.getSessionAttribute(request,

"SPRING_SECURITY_LAST_EXCEPTION");

if (exception != null) {

throw exception;

}

return "redirect:/loginPage";

}

@GetMapping({"/index", "/"})

public String index() {

return "index";

}

@ResponseBody

@GetMapping("/role/teacher")

@Secured({"ROLE_teacher", "ROLE_admin"})

public String teacher() {

return "模拟获取老师数据";

}

@ResponseBody

@GetMapping("/role/admin")

@Secured({"ROLE_admin"})

public String admin() {

return "模拟获取管理员数据";

}

@ResponseBody

@GetMapping("/role/student")

@Secured({"ROLE_student", "ROLE_admin"})

public String student() {

return "模拟获取学生数据";

}

}

(5)新建UserDao.java文件和AuthoritiesDao.java文件进行数据库的操作。

UserDao.java文件的内容如下:

package com.example.springsecuritydbdemo.dao;

import com.example.springsecuritydbdemo.entity.Authorities;

import org.springframework.data.jpa.repository.JpaRepository;

import

org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface AuthoritiesDao extends

JpaRepository<Authorities, Integer>, JpaSpecificationExecutor

<Authorities> {

}

AuthoritiesDao.java文件的内容如下:

package com.example.springsecuritydbdemo.dao;

import com.example.springsecuritydbdemo.entity.Users;

import org.springframework.data.jpa.repository.JpaRepository;

import

org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface UsersDao extends

JpaRepository<Users, Integer>, JpaSpecificationExecutor<Users>

{

Users findByUsername(String username);

}

(6)新建数据库的表对应的实体类Authorities、PersistentLogins和Users。Authorities类如下:

package com.example.springsecuritydbdemo.entity;

import lombok.Getter;

import lombok.Setter;

import javax.persistence.*;

import java.util.HashSet;

import java.util.Set;

@Getter

@Setter

@Entity

@Table(name = "authorities")

public class Authorities {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

private String authority;

@ManyToMany(mappedBy = "authorities", cascade = CascadeType.ALL)

private Set<Users> users = new HashSet<>();

}

PersistentLogins类的内容如下:

package com.example.springsecuritydbdemo.entity;

import lombok.Getter;

import lombok.Setter;

import javax.persistence.Entity;

import javax.persistence.Id;

import javax.persistence.Table;

import java.util.Date;@Getter

@Setter

@Entity

@Table(name = "persistent_logins")

public class PersistentLogins {

@Id

private String series;

private String username;

private String token;

private Date last_used;

}

Users类的内容如下:

package com.example.springsecuritydbdemo.entity;

import lombok.Getter;

import lombok.Setter;

import javax.persistence.*;

import java.util.HashSet;

import java.util.Set;

@Getter

@Setter

@Entity

@Table(name = "users")

public class Users {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

private Integer id;

private String username;

private String password;

@ManyToMany(targetEntity = Authorities.class, cascade =

CascadeType.ALL)

@JoinTable(name = "users_authorities", joinColumns = @JoinColumn(name = "users_id",

referencedColumnName = "id"),

inverseJoinColumns = @JoinColumn(name =

"authorities_id",referencedColumnName = "id"))

private Set<Authorities> authorities = new HashSet<>();

}

(7)设置项目的全局异常处理:

package com.example.springsecuritydbdemo.exception;

import lombok.extern.slf4j.Slf4j;

import org.springframework.security.access.AccessDeniedException;

import org.springframework.security.authentication.BadCredentials

Exception;

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.servlet.ModelAndView;

/**

* 全局异常处理

*/

@ControllerAdvice

@Slf4j

public class GlobalExceptionHandler {

@ExceptionHandler(RuntimeException.class)

public ModelAndView exception(Exception e) {

log.info(e.toString());

ModelAndView modelAndView = new ModelAndView();

modelAndView.setViewName("error");

if (e instanceof BadCredentialsException) {

// 密码错误

modelAndView.addObject("msg", "密码错误");

} else if (e instanceof AccessDeniedException) {

// 权限不足

modelAndView.addObject("msg", e.getMessage());

} else { // 其他

modelAndView.addObject("msg", "系统错误");

}

return modelAndView;

}

}

(8)设置用户的服务类,代码如下:

package com.example.springsecuritydbdemo.service;

import com.example.springsecuritydbdemo.dao.UsersDao;

import com.example.springsecuritydbdemo.entity.Authorities;

import com.example.springsecuritydbdemo.entity.Users;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGranted

Authority;

import org.springframework.security.core.userdetails.*;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;

import java.util.Set;

@Service("userDetailsService")

@Slf4j

public class UserDetailService implements UserDetailsService {

@Autowired

private UsersDao usersDao;

@Override

@Transactional

public UserDetails loadUserByUsername(String s) throws

UsernameNotFound

Exception {

Users users = usersDao.findByUsername(s); // 用户不存在

if (users == null) {

log.error("用户名:[{}]不存在", s);

throw new UsernameNotFoundException("用户名不存在");

}

// 获取该用户的角色信息

Set<Authorities> authoritiesSet = users.getAuthorities();

ArrayList<GrantedAuthority> list = new ArrayList<>();

for (Authorities authorities : authoritiesSet) {

list.add(new

SimpleGrantedAuthority(authorities.getAuthority()));

}

return new User(users.getUsername(), users.getPassword(),

list);

}

}

(9)新建Spring Boot项目的启动类:

package com.example.springsecuritydbdemo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import

org.springframework.security.config.annotation.web.configuration.

EnableWebSecurity;

@EnableWebSecurity

@SpringBootApplication

public class SpringSecurityDbDemoApplication {

public static void main(String[] args) {

SpringApplication.run(SpringSecurityDbDemoApplication.class,

args);

}

}

提示:在启动项目之前需要配置好数据库。本书使用MySQL 8。数据库的配置信息保存在application.yml文件中,读者可以根据实际情况修改数据库信息,确认无误后即可启动项目。

访问
http://localhost:8080/loginPage
即可可以看到登录页面,如图5.10所示。使用账号admin和密码123456登录后,可以看到admin拥有的权限,如图5.11所示。退出admin后使用账号student和密码123456登录,查看student拥有的权限,如图5.12所示。可以看到,不同的用户拥有不同的权限,从而实现使用JPA控制不同用户权限的目的。

可以看到,不同的账号访问,拥有不同的权限,权限不同看到的数据也不同。

实战:基于MyBatis的Spring Boot Security操作

基于5.3.1小节的代码,全部注释掉UserDao.java文件和AuthoritiesDao.java文件,修改后缀名为UserDao.java.bak和AuthoritiesDao.java.bak,再修改entity包中的实体类。

主要步骤如下:

(1)移除pom.xml中的JPA依赖,在pom.xml中添加MyBatis的依赖:

<!--spring data jpa-->

<!--<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>-->

<dependency>

<groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId>

<version>2.1.1</version>

</dependency>

(2)修改
SpringSecurityDbDemoApplication.java文件,增加一个MyBatis的配置注解:

@MapperScan("com.example.springsecuritydbdemo.dao")

(3)修改entity包中所有的实体类,去除所有的JPA注解。

Authorities类的文件内容如下:

package com.example.springsecuritydbdemo.entity;

import lombok.Data;

@Data

public class Authorities {

private Integer id;

private String authority;

}

PersistentLogins类的文件内容如下:

package com.example.springsecuritydbdemo.entity;

import lombok.Data;

import java.util.Date;

@Data

public class PersistentLogins {

private String series;

private String username;

private String token;

private Date last_used;

}

Users类的文件内容如下:

package com.example.springsecuritydbdemo.entity;

import lombok.Data;

import java.util.HashSet;

import java.util.Set;

@Data

public class Users {

private Integer id;

private String username;

private String password;

private Set<Authorities> authorities = new HashSet<>();

}

(4)修改Dao包中的数据库操作接口,添加查询用户的方法:

package com.example.springsecuritydbdemo.dao;

import com.example.springsecuritydbdemo.entity.Authorities;

import org.apache.ibatis.annotations.Mapper;

import org.apache.ibatis.annotations.Param;

import org.apache.ibatis.annotations.ResultType;

import org.apache.ibatis.annotations.Select;

import java.util.Set;

@Mapper

public interface AuthoritiesDao {

@Select("select a.* from authorities a LEFT JOIN users_authorities

b " +

"on a.id=b.authorities_id where b.users_id=#{userId}")

@ResultType(Set.class)

Set<Authorities> findByUserId(@Param("userId") Integer userId);

}

(5)添加查询用户和保存用户的方法:

package com.example.springsecuritydbdemo.dao;

import com.example.springsecuritydbdemo.entity.Users;

import org.apache.ibatis.annotations.Mapper;

import org.apache.ibatis.annotations.Param;

import org.apache.ibatis.annotations.Select;

@Mapper

public interface UsersDao {

@Select("select * from users where username=#{username}")

Users findByUsername(@Param("username") String username);

void save(Users users);

}

(6)在sys数据库中执行SQL语句,用来创建3张表,代码如下:

CREATE TABLE `authorities` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`authority` varchar(255) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `persistent_logins` (

`series` varchar(100) NOT NULL,

`username` varchar(255) DEFAULT NULL,

`token` varchar(255) DEFAULT NULL,

`last_used` datetime DEFAULT NULL,

PRIMARY KEY (`series`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `users` (

`id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL,

`password` varchar(255) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

修改完成后启动项目,再次访问http://localhost:8080,如同5.3.1小节的例子一样登录不同的账号,确认不同的用户拥有不同的权限。通过以上开发实践可以看到,在一些简单的数据库操作中,JPA不需要编写SQL语句,这样会明显地提高开发效率,使用起来也非常方便。

相关推荐

驱动网卡(怎么从新驱动网卡)
驱动网卡(怎么从新驱动网卡)

网卡一般是指为电脑主机提供有线无线网络功能的适配器。而网卡驱动指的就是电脑连接识别这些网卡型号的桥梁。网卡只有打上了网卡驱动才能正常使用。并不是说所有的网卡一插到电脑上面就能进行数据传输了,他都需要里面芯片组的驱动文件才能支持他进行数据传输...

2026-01-30 00:37 liuian

win10更新助手装系统(微软win10更新助手)

1、点击首页“系统升级”的按钮,给出弹框,告诉用户需要上传IMEI码才能使用升级服务。同时给出同意和取消按钮。华为手机助手2、点击同意,则进入到“系统升级”功能华为手机助手华为手机助手3、在检测界面,...

windows11专业版密钥最新(windows11专业版激活码永久)

 Windows11专业版的正版密钥,我们是对windows的激活所必备的工具。该密钥我们可以通过微软商城或者通过计算机的硬件供应商去购买获得。获得了windows11专业版的正版密钥后,我...

手机删过的软件恢复(手机删除过的软件怎么恢复)
手机删过的软件恢复(手机删除过的软件怎么恢复)

操作步骤:1、首先,我们需要先打开手机。然后在许多图标中找到带有[文件管理]文本的图标,然后单击“文件管理”进入页面。2、进入页面后,我们将在顶部看到一行文本:手机,最新信息,文档,视频,图片,音乐,收藏,最后是我们正在寻找的[更多],单击...

2026-01-29 23:55 liuian

一键ghost手动备份系统步骤(一键ghost 备份)

  步骤1、首先把装有一键GHOST装系统的U盘插在电脑上,然后打开电脑马上按F2或DEL键入BIOS界面,然后就选择BOOT打USDHDD模式选择好,然后按F10键保存,电脑就会马上重启。  步骤...

怎么创建局域网(怎么创建局域网打游戏)

  1、购买路由器一台。进入路由器把dhcp功能打开  2、购买一台交换机。从路由器lan端口拉出一条网线查到交换机的任意一个端口上。  3、两台以上电脑。从交换机任意端口拉出网线插到电脑上(电脑设置...

精灵驱动器官方下载(精灵驱动手机版下载)

是的。驱动精灵是一款集驱动管理和硬件检测于一体的、专业级的驱动管理和维护工具。驱动精灵为用户提供驱动备份、恢复、安装、删除、在线更新等实用功能。1、全新驱动精灵2012引擎,大幅提升硬件和驱动辨识能力...

一键还原系统步骤(一键还原系统有哪些)

1、首先需要下载安装一下Windows一键还原程序,在安装程序窗口中,点击“下一步”,弹出“用户许可协议”窗口,选择“我同意该许可协议的条款”,并点击“下一步”。  2、在弹出的“准备安装”窗口中,可...

电脑加速器哪个好(电脑加速器哪款好)

我认为pp加速器最好用,飞速土豆太懒,急速酷六根本不工作。pp加速器什么网页都加速,太任劳任怨了!以上是个人观点,具体性能请自己试。ps:我家电脑性能很好。迅游加速盒子是可以加速电脑的。因为有过之...

任何u盘都可以做启动盘吗(u盘必须做成启动盘才能装系统吗)

是的,需要注意,U盘的大小要在4G以上,最好是8G以上,因为启动盘里面需要装系统,内存小的话,不能用来安装系统。内存卡或者U盘或者移动硬盘都可以用来做启动盘安装系统。普通的U盘就可以,不过最好U盘...

u盘怎么恢复文件(u盘文件恢复的方法)

开360安全卫士,点击上面的“功能大全”。点击文件恢复然后点击“数据”下的“文件恢复”功能。选择驱动接着选择需要恢复的驱动,选择接入的U盘。点击开始扫描选好就点击中间的“开始扫描”,开始扫描U盘数据。...

系统虚拟内存太低怎么办(系统虚拟内存占用过高什么原因)

1.检查系统虚拟内存使用情况,如果发现有大量的空闲内存,可以尝试释放一些不必要的进程,以释放内存空间。2.如果系统虚拟内存使用率较高,可以尝试增加系统虚拟内存的大小,以便更多的应用程序可以使用更多...

剪贴板权限设置方法(剪贴板访问权限)
剪贴板权限设置方法(剪贴板访问权限)

1、首先打开iphone手机,触碰并按住单词或图像直到显示选择选项。2、其次,然后选取“拷贝”或“剪贴板”。3、勾选需要的“权限”,最后选择开启,即可完成苹果剪贴板权限设置。仅参考1.打开苹果手机设置按钮,点击【通用】。2.点击【键盘】,再...

2026-01-29 21:37 liuian

平板系统重装大师(平板重装win系统)

如果你的平板开不了机,但可以连接上电脑,那就能好办,楼主下载安装个平板刷机王到你的个人电脑上,然后连接你的平板,平板刷机王会自动识别你的平板,平板刷机王上有你平板的我刷机包,楼主点击下载一个,下载完成...

联想官网售后服务网点(联想官网售后服务热线)

联想3c服务中心是联想旗下的官方售后,是基于互联网O2O模式开发的全新服务平台。可以为终端用户提供多品牌手机、电脑以及其他3C类产品的维修、保养和保险服务。根据客户需求层次,联想服务针对个人及家庭客户...