本文将介绍基于Spring Security实现安全框架中角色动态分配的功能。
作者:后端小肥肠
目录
6.4. 编写SysRoleAndPermissionVo实体类
1. 前言
欢迎来到【Spring Security系列】!在当今复杂多变的网络环境下,实现灵活而安全的权限管理是每个开发者不可回避的挑战。本文将以 Spring Security授权原理为基础背景,以成果界面展示、核心代码说明及表结构关系的讲解来告诉大家怎么实现用户-角色的动态分配以达到动态授权的目的。
2. Spring Security授权原理
Spring Security 是一个强大的身份验证和访问控制框架,它允许在Java应用程序中实现安全性。Spring Security 的角色分配是通过授权(Authorization)来实现的,授权主要通过配置权限和角色来完成。Spring Security的授权流程如下图所示:
由上图可看出Spring Security授权原理主要可以囊括为以下几点:
1. 用户认证(Authentication)
用户身份的验证是通过 Authentication
对象来表示的。Authentication
对象包含了用户的身份信息,如用户名和密码。Spring Security 提供了不同的认证方式,包括基于表单登录、HTTP 基本认证、LDAP 等。
2. 访问控制(Authorization)
一旦用户被认证,Spring Security 就通过授权来判断用户是否有访问资源的权限。授权是通过 GrantedAuthority
接口的实现类表示的,而其中的一种实现类就是角色(SimpleGrantedAuthority
)。
3. 角色和权限的关系
在 Spring Security 中,角色和权限是相互关联的。一个角色可以包含多个权限,而一个用户可以拥有多个角色。角色和权限的关系是通过配置来定义的。以下是一个简单的 Spring Security 配置类的示例,通过注解配置了两个角色(ROLE_USER
和 ROLE_ADMIN
)和相应的权限:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
.and()
.withUser("admin").password("{noop}password").roles("ADMIN");
}
}
在这个配置中,.hasRole("USER")
和 .hasRole("ADMIN")
表示访问对应路径的用户需要具备相应的角色。在 configureGlobal
方法中,使用了 inMemoryAuthentication
来配置了两个用户,一个拥有 USER
角色,另一个拥有 ADMIN
角色。采用这种硬编码的方式分配角色在实际项目中是不被允许的,因为系统管理员在需要变更用户权限时,可能需要修改代码并重新部署应用程序,这不仅繁琐,而且容易引入错误。
3. 成果界面展示
用户-角色动态分配涉及到三个大功能模块,分别为用户管理,角色管理,用户-角色管理,通过这三个功能模块能实现角色全部由界面交互动态分配,无需改动后端代码。
用户管理界面(真实项目数据厚码,着重关注字段就行):
上述界面为用户管理界面,主要实现用户列表的展示及搜索、用户禁用(禁用的用户无法登录系统)、 用户的新增、编辑和删除功能。
角色管理界面:
上述界面为角色管理界面,主要实现角色的列表展示及搜索、角色数据的新增、编辑和删除功能。
用户-角色分配界面(真实项目数据厚码)
在上述界面中点击 【保存当前页配置】按钮,即可实现用户与角色的绑定配置。
4. 开发环境搭建
4.1. 所需版本工具
依赖 | 版本 |
---|---|
Spring Boot | 2.6.14 |
java | 1.8 |
4.2. pom文件编写
1. 引入SpringBoot依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.14</version>
<relativePath/>
</parent>
2. 引入Spring Security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
5. 表结构关系
角色动态分配所涉及的表为用户表,角色表,用户和角色的桥接表,我们仅需要在桥接表中新增数据,即可实现用户和角色的双向绑定,从下图可以看出用户和角色之间的关系为多对多(一个用户可以拥有多个角色,一个角色对应多个用户)。
在上图中所涉及的表我已经上传到了我的资源中,需要的同学可以自行下载:
https://download.csdn.net/download/c18213590220/88670443?spm=1001.2014.3001.5503
6. 核心代码讲解
我在这里只写核心代码,不涉及增删改查基础代码,如用户列表和角色列表的CRUD。
6.1. 重写UserDetails
@Component
public class AuthUser implements UserDetails {
private String username;
private String password;
private Integer state;
private Collection<? extends GrantedAuthority> authorities;
public AuthUser() {
}
public AuthUser(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities) {
this.username = username;
this.password = password;
this.state = state;
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
// 账户是否未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
// 账户是否未被锁
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
@Override
public String toString() {
return "JwtUser{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", state=" + state +
", authorities=" + authorities +
'}';
}
6.2. 重写UserDetailsService
@Service
public class AuthUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private ISysUserService userService;
@Autowired
private ISysRoleService roleService;
/**
* 通过账号查找用户、角色的信息
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = userService.getUserByUserName(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("%s.这个用户不存在", username));
}else {
//查找角色
List<String> roles = roleService.getRolesByUserName(username);
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return new AuthUser(user.getUsername(), user.getPassword(), user.getIsEnabled(), authorities);
}
}
}
要实现角色的动态配置,首先应当重写UserDetailsService,从数据库中获取某用户所匹配的角色。
6.3. 编写角色-用户绑定方法
@Transactional
public boolean saveRoleUser(String roleId, SysRoleAndPermissionVo... sysRoleAndPermissionVos){
//先删除数据
this.delRoleId(roleId);
//
if(sysRoleAndPermissionVos !=null) {
Set<SysRoleUserTable> set = new HashSet<>();
SysRoleUserTable roleUser = null;
for (SysRoleAndPermissionVo roleVo : sysRoleAndPermissionVos) {
roleUser = new SysRoleUserTable();
//存储roleID和userID到多对对的中间表
roleUser.setRoleId(roleVo.getRoleId());
roleUser.setUserId(roleVo.getId());
set.add(roleUser);
}
System.out.println("set = " + set);
//再批量保存
return this.saveBatch(set);
}
return false;
}
上述方法就是将用户和角色id存储到sys_user_role桥接表中,实现用户和角色的绑定。
6.4. 编写SysRoleAndPermissionVo实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SysRoleAndPermissionVo {
String id;
String name;
String roleId;
String pid;
}
7. 结语
本文讲解了基于Spring Security实现角色动态分配的方法,下期将继续讲解怎么实现菜单的动态配置,针对本文的问题如您有更好的实现或解决方式,欢迎在评论区留言探讨~~