在前一章中,我們實現了整合 SpringSecurity 框架,基於內存的最簡配置
目標
整合 SpringSecurity 及 MybatisPlus 實現使用讀取數據庫數據進行登陸鑑權
準備工作
創建用戶表 user
、角色表 role
、用戶角色關係表 user_role
CREATE TABLE `role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`rolename` varchar(32) NOT NULL COMMENT '角色名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COMMENT='角色';
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL COMMENT '用戶名',
`password` varchar(128) NOT NULL COMMENT '密碼',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4 COMMENT='用戶';
CREATE TABLE `user_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`,`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='用戶角色關係表';
操作步驟
添加依賴
引入 Spring Boot Starter 父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
添加 springSecurity
及 mybatisPlus
的依賴,添加後的整體依賴如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
配置
配置一下數據源
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&useSSL=false
username: app
password: 123456
編碼
實體類
角色實體類 Role,實現權限接口 GrantedAuthority
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("role")
public class Role implements GrantedAuthority {
@TableId(type = IdType.AUTO)
private Long id;
private String rolename;
@Override
public String getAuthority() {
return this.rolename;
}
}
用戶實體類 user,實現權限接口 UserDetails,主要方法是 getAuthorities,用於獲取用戶的角色列表
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user")
public class User implements UserDetails {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
@TableField(exist = false)
private List<Role> roleList;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roleList;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
用戶角色關係實體
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_role")
public class UserRole {
@TableId(type = IdType.AUTO)
private Long id;
private Long userId;
private Long roleId;
}
Repository 層
分別爲三個實體類添加 Mapper
@Mapper
public interface RoleRepository extends BaseMapper<Role> {
}
@Mapper
public interface UserRepository extends BaseMapper<User> {
}
@Mapper
public interface UserRoleRepository extends BaseMapper<UserRole> {
}
權限配置
實現 UserDetailsService 接口
UserDetailsService 是 SpringSecurity 提供的登陸時用於根據用戶名獲取用戶信息的接口
@AllArgsConstructor
@Service
public class UserService implements UserDetailsService {
private UserRepository userRepository;
private RoleRepository roleRepository;
private UserRoleRepository userRoleRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if (username == null || username.isEmpty()) {
throw new UsernameNotFoundException("用戶名不能爲空");
}
User user = userRepository.selectOne(
new QueryWrapper<User>().lambda().eq(User::getUsername, username));
if (user == null) {
throw new UsernameNotFoundException("用戶不存在");
}
List<UserRole> userRoles = userRoleRepository.selectList(
new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, user.getId()));
if (userRoles != null && !userRoles.isEmpty()) {
List<Long> roleIds = userRoles.stream()
.map(UserRole::getRoleId).collect(Collectors.toList());
List<Role> roles = roleRepository.selectList(
new QueryWrapper<Role>().lambda().in(Role::getId, roleIds));
user.setRoleList(roles);
}
return user;
}
}
權限配置
繼承 SpringSecurity 提供的 WebSecurityConfigurerAdapter 配置 userDetailsService 及加密方式。
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
}
啓動類
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
驗證結果
初始化數據
執行測試用例進行初始化數據
@Slf4j
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = Application.class)
public class SecurityTest {
@Autowired
private UserRepository userRepository;
@Autowired
private UserRoleRepository userRoleRepository;
@Autowired
private RoleRepository roleRepository;
@Test
public void initData() {
List<User> userList = new ArrayList<>();
userList.add(new User(1L, "admin", new BCryptPasswordEncoder().encode("123456"), null));
userList.add(new User(2L, "user", new BCryptPasswordEncoder().encode("123456"), null));
List<Role> roleList = new ArrayList<>();
roleList.add(new Role(1L, "ROLE_ADMIN"));
roleList.add(new Role(2L, "ROLE_USER"));
List<UserRole> urList = new ArrayList<>();
urList.add(new UserRole(1L, 1L, 1L));
urList.add(new UserRole(2L, 1L, 2L));
urList.add(new UserRole(3L, 2L, 2L));
userList.forEach(userRepository::insert);
roleList.forEach(roleRepository::insert);
urList.forEach(userRoleRepository::insert);
}
}
校驗
網頁訪問 http://localhost:8080,將自動跳轉登錄頁,輸入 user/123456
進行登錄,可以使用 debugger 進行調試。
源碼地址
本章源碼 : https://gitee.com/gongm_24/spring-boot-tutorial.git
結束語
使用數據庫進行鑑權,是實際應用中最基本的需求。