Mybatis Plus 是一个非常流行的 ORM 框架,可以帮助我们在 Spring 中更方便地访问数据库。在配合 Spring Security 使用时,我们可以使用 Mybatis Plus 来定义数据访问层(DAO),使用 Spring Boot 的自动配置功能来配置 Mybatis Plus 和 Spring Security,从而实现用户认证和授权的功能。
下面是一个使用 Mybatis Plus 和 Spring Security 的示例:
1.创建用户表
首先,我们需要在数据库中创建用户表,作为存储用户信息和凭证的表。在本例中,我们创建一个名为 sys_user
的表,包含了用户 ID、用户名、密码和是否启用等字段。
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码',
`enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用:0-禁用,1-启用',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息表';
2.添加 Mybatis Plus 和 Spring Security 的依赖
在 Maven 中添加 Mybatis Plus 和 Spring Security 的依赖:
com.baomidou
mybatis-plus-boot-starter
3.4.2
org.springframework.boot
spring-boot-starter-security
2.5.2
3.配置 Mybatis Plus 和 Spring Security
在 Spring Boot 的配置文件中,添加如下配置:
# application.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useSSL=false
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
security:
user:
name: admin
password: password
mvc:
# 去掉默认的 favicon.ico
favicon:
enabled: false
mybatis-plus:
configuration:
# 开启驼峰命名规则,即支持Java的驼峰命名法。
map-underscore-to-camel-case: true
# 扫描 Mapper 接口,注意:这里需要换成自己 DTO 的包。
mapper-locations: classpath*:/mapper/**Mapper.xml
在上述代码中,我们配置了数据源、Spring Security 的用户信息(用于登录验证)、Mybatis Plus 的配置和 Mapper 接口的扫描位置。需要注意的是,这里的 mapper-locations
是 Mapper XML 文件的位置,需要根据实际情况进行配置。
4.定义 Mapper 接口
在 Mybatis Plus 中,可以使用 Mapper 接口来访问数据库。首先,需要定义一个用于表示用户信息的 DTO 类,例如:
@Data
public class UserDTO implements UserDetails {
private Long id;
private String username;
private String password;
private Boolean enabled;
@Override
public Collection extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.NO_AUTHORITIES;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled == null || enabled;
}
}
然后,定义一个 Mapper 接口来访问 sys_user
表,例如:
@Mapper
public interface UserMapper extends BaseMapper {
/**
* 根据用户名获取用户信息
*
* @param username 用户名
* @return 用户信息
*/
UserDTO findByUsername(String username);
}
这里使用了 Mybatis Plus 的 BaseMapper
泛型接口来继承了基本的增、删、改、查操作,并且定义了一个新的方法来根据用户名获取用户信息。其中,@Mapper
注解表示该接口是一个 Mybatis Mapper 接口,由 Mybatis 解析并生成相应的实现类。
5.定义 Service
通过定义 DAO,我们现在已经能够使用 Mybatis Plus 来访问数据库,接下来需要定义 Service 层来对 DAO 层进行封装,提供给 Controller 层调用。例如,我们定义一个名为 UserServiceImpl
的 Service 实现类:
@Service
public class UserServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDTO user = userMapper.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("No matching user found for " + username);
}
return user;
}
}
在代码中,我们通过 @Autowired
注解将
UserMapper注入到
UserServiceImpl中,并实现了
UserDetailsService接口的
loadUserByUsername()` 方法,根据用户名查询用户信息并返回给 Spring Security 使用。
6.配置 Spring Security
最后,需要配置 Spring Security,使用上述 Service 实现类来进行用户认证和授权。在 Spring Boot 的配置文件中添加如下配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
在上述代码中,我们定义了一个 WebSecurityConfig
配置类,并继承自 WebSecurityConfigurerAdapter
,用于配置 Spring Security。在 configure(HttpSecurity)
方法中,我们定义了登录页面、授权规则和登录方式。在 configureGlobal(AuthenticationManagerBuilder)
方法中,我们将上述 Service 实现类注入到 AuthenticationManagerBuilder
中,使其能够在登录时调用该 Service 实现类进行认证操作。
现在,当用户在登录页面中输入用户名和密码时,Spring Security 就会自动调用上述 Service 实现类来进行用户认证。如果认证成功,用户就能够访问被授权的资源。如果认证失败,则会抛出相应的异常,例如 UsernameNotFoundException
、BadCredentialsException
等。
以下是一个使用 Mybatis Plus、Spring Security 和 Controller 的实例:
1.定义 Controller
定义一个 UserController 类来作为 Spring Boot 的 Web 接口,例如:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/")
public List findAll() {
return userService.list();
}
@GetMapping("/{id}")
public UserDTO findById(@PathVariable Long id) {
return userService.getById(id);
}
@PostMapping("/")
public UserDTO create(@RequestBody UserDTO user) {
userService.save(user);
return user;
}
@PutMapping("/{id}")
public UserDTO update(@PathVariable Long id, @RequestBody UserDTO user) {
user.setId(id);
userService.updateById(user);
return user;
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.removeById(id);
}
}
在上述代码中,我们使用 @RestController
注解来定义一个 RESTful 风格的控制器,使用 @RequestMapping
注解来指定请求的路径。在控制器类中,我们通过 @Autowired
注解将 Service 接口注入到 Controller 中,使用 @GetMapping
、@PostMapping
、@PutMapping
和 @DeleteMapping
注解来分别定义 GET、POST、PUT 和 DELETE 请求的处理方法。
2.添加 Swagger 文档支持
为了方便地查看和测试接口,我们可以使用 Swagger UI 来生成文档和测试接口。
首先,在 Maven 中添加 Swagger2 和 Swagger UI 的依赖:
io.springfox
springfox-swagger2
2.10.5
io.springfox
springfox-swagger-ui
2.10.5
然后,在 Spring Boot 的配置文件中添加如下配置:
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
.paths(PathSelectors.any())
.build();
}
}
最后,启动 Spring Boot 应用,访问 http://localhost:8080/swagger-ui.html
即可看到 Swagger 文档和测试接口的界面。
现在,在 Swagger UI 中可以方便地测试 CRUD 操作接口,验证 Mybatis Plus 和 Spring Security 的整合效果。
2023/4/10 20:33:00
之前你不是在controller层加注解@PreAuthorize("hasAuthority('ROLE_ADMIN')")实现过滤的吗,为何这里不需要?
重试
复制
你提到的 @PreAuthorize("hasAuthority('ROLE_ADMIN')")
注解是用来控制方法的访问权限的,可以在 Controller 层的方法上添加该注解来进行权限控制,保证只有具有相应权限的用户才能访问该接口。
在我的之前的范例中,之所以需要添加该注解是因为我们控制了访问 /admin
路径的权限,只有具有 ROLE_ADMIN
角色的用户才能访问到该路径下的接口。而在这个示例中,我没有控制任何路径的权限,只是使用了 Spring Security 来实现用户的身份认证和访问授权。
在实际开发中,根据业务需求和安全性要求,可以在 Controller 层的每个方法上添加不同的权限控制注解,以实现更加细粒度的权限控制。
2023/4/10 20:39:29
现在在Controller层加上@PreAuthorize,我想看看如何控制
重试
复制
好的,我来给你举个例子:
假设我们的应用需要实现通过用户角色来控制访问权限,例如只有拥有 ADMIN 角色的用户才能访问某些接口,我们可以在 Controller 层的方法上使用 @PreAuthorize
注解来实现权限控制。
首先,我们需要在 pom.xml
文件中添加 Spring Security 的依赖:
org.springframework.boot
spring-boot-starter-security
然后,我们可以在 WebSecurityConfig
配置类中设置拦截规则,例如:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") // 只有 ADMIN 角色可以访问 /admin 路径下的接口
.anyRequest().authenticated()
.and().formLogin()
.and().httpBasic();
}
}
在上述代码中,我们设置了只有拥有 ADMIN 角色的用户可以访问 /admin/**
路径下的接口,并且其他的请求都需要进行身份认证才能访问。
接着,我们可以在 Controller 层中使用 @PreAuthorize
注解来控制方法的访问权限,例如:
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PreAuthorize("hasRole('ADMIN')") // 只有 ADMIN 角色可以访问该接口
@GetMapping("/")
public List findAll() {
return userService.list();
}
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") // 只有 USER 或 ADMIN 角色可以访问该接口
@GetMapping("/{id}")
public UserDTO findById(@PathVariable Long id) {
return userService.getById(id);
}
@PreAuthorize("hasRole('ADMIN')") // 只有 ADMIN 角色可以访问该接口
@PostMapping("/")
public UserDTO create(@RequestBody UserDTO user) {
userService.save(user);
return user;
}
@PreAuthorize("hasRole('ADMIN')") // 只有 ADMIN 角色可以访问该接口
@PutMapping("/{id}")
public UserDTO update(@PathVariable Long id, @RequestBody UserDTO user) {
user.setId(id);
userService.updateById(user);
return user;
}
@PreAuthorize("hasRole('ADMIN')") // 只有 ADMIN 角色可以访问该接口
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.removeById(id);
}
}
在上述代码中,我们给每个需要控制访问权限的方法上添加了 @PreAuthorize
注解,并给该注解传递了相应的角色信息,例如 @PreAuthorize("hasRole('ADMIN')")
表示只有 ADMIN 角色可以访问该接口,@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
表示只有 USER 或 ADMIN 角色可以访问该接口等。
现在,我们就可以根据业务需求和安全性要求,使用 @PreAuthorize
注解来实现细粒度的方法级别权限控制。
2023/4/10 20:41:39
那么这个USER和'ADMIN'的权限我去哪里给每个用户定义呢?是在java程序里还是在mysql表里?或者说是其他方式
重试
复制
可以说在 Java 程序和 MySQL 中都可以定义用户的角色和权限。
一般来说,在 MySQL 中创建用户和角色并给予相应的权限是很常见的做法。可以在 MySQL 中创建一个用户表和角色表,然后再创建一个关联表来记录用户和角色之间的对应关系。例如:
CREATE TABLE user (
id BIGINT NOT NULL AUTO_INCREMENT,
username VARCHAR(64) NOT NULL,
password VARCHAR(256) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE role (
id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES user (id),
FOREIGN KEY (role_id) REFERENCES role (id)
);
然后,在 Java 程序中,可以通过 UserRepository 和 RoleRepository 来操作用户和角色等相关信息,例如:
public interface UserRepository extends JpaRepository {
User findByUsername(String username);
}
public interface RoleRepository extends JpaRepository {
Role findByName(String name);
}
只要有了这些信息,就可以在 Spring Security 的配置类中将用户和角色关联起来,并授权给相应的角色。
例如,在上面提到的 WebSecurityConfig
配置类中,可以使用 UserDetailsService
来获取用户相关信息,并将用户和角色在内存中进行对应和授权:
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User " + username + " not found");
}
Set authorities = new HashSet<>();
for (Role role : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(), user.getPassword(), authorities);
});
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and().formLogin()
.and().httpBasic();
}
在上述代码中,我们使用 UserDetailsService
对象来获取用户相关信息,并将用户和角色在内存中进行对应和授权,这样用户就可以根据自己的角色来访问相应的接口了。
当然,这只是一个比较简单的示例,真实的应用场景可能会更加复杂,需要根据实际情况来灵活地设计用户和角色的关系,并进行相应的授权。