说明
(1)JDK版本:1.8
(2)Spring Boot 2.0.6
(3)Spring Security 5.0.9
(4)Spring Data JPA 2.0.11.RELEASE
(5)hibernate5.2.17.Final
(6)MySQLDriver 5.1.47
(7)MySQL 8.0.12
需求缘起
之前的版本的用户角色通过enum的方式硬编码了,实际场景中,我们会有一个角色表进行储存角色,如下图:
那么怎么动态加载角色呢?
一、动态加载角色
1.1 定义角色实体类
定义一个角色实体类Role:
package com.kfit.permission.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Role {
@Id @GeneratedValue
private long rid;//主键.
private String name;//角色名称.
private String description;//角色描述.
public Role() {
}
public Role(String name, String description) {
this.name = name;
this.description = description;
}
public long getRid() {
return rid;
}
public void setRid(long rid) {
this.rid = rid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
1.2 角色持久化
创建RoleRepository持久化操作:
package com.kfit.permission.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.kfit.permission.bean.Role;
public interface RoleRepository extends JpaRepository<Role,Long>{
}
1.3 修改UserInfo的用户角色
这里用户和角色的关系是多对多的(一个用户可以拥有多个角色,一个角色可以被多个用户拥有),所以使用@ManyToMany注解它们之间的关系:
package com.kfit.permission.bean;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
@Entity
public class UserInfo {
@Id @GeneratedValue
private long uid;//主键.
private String username;//用户名.
private String password;//密码.
//用户--角色:多对多的关系.
@ManyToMany(fetch=FetchType.EAGER)//立即从数据库中进行加载数据;
@JoinTable(name = "UserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "role_id") })
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public long getUid() {
return uid;
}
public void setUid(long uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
这样的话,就会自动创建一个关联表user_role:
1.4 修改CustomUserDetailService
CustomUserDetailService这里需要调整下角色的添加方式,如下代码:
package com.kfit.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.kfit.permission.bean.Role;
import com.kfit.permission.bean.UserInfo;
import com.kfit.permission.service.UserInfoService;
@Component
public class CustomUserDetailService implements UserDetailsService{
@Autowired
private UserInfoService userInfoService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("CustomUserDetailService.loadUserByUsername()");
//通过username获取用户信息
UserInfo userInfo = userInfoService.findByUsername(username);
if(userInfo == null) {
throw new UsernameNotFoundException("not found");
}
//定义权限列表.
List<GrantedAuthority> authorities = new ArrayList<>();
// 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头
for(Role role:userInfo.getRoles()) {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
}
User userDetails = new User(userInfo.getUsername(),userInfo.getPassword(),authorities);
return userDetails;
}
}
1.5 修改DataInit
最后我们需要修改下初始化数据的方法:
package com.kfit.permission.init;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.kfit.permission.bean.Role;
import com.kfit.permission.bean.UserInfo;
import com.kfit.permission.repository.RoleRepository;
import com.kfit.permission.repository.UserInfoRepository;
@Service
public class DataInit {
@Autowired
private UserInfoRepository userInfoRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private RoleRepository roleRepository;
@PostConstruct
public void dataInit() {
List<Role> roles = new ArrayList<>();
Role adminRole= new Role("admin","管理员");
Role normalRole = new Role("normal","普通用户");
roleRepository.save(adminRole);
roleRepository.save(normalRole);
roles.add(adminRole);
roles.add(normalRole);
UserInfo admin = new UserInfo();
admin.setUsername("admin");
admin.setPassword(passwordEncoder.encode("123"));
admin.setRoles(roles);
userInfoRepository.save(admin);
roles = new ArrayList<>();
roles.add(normalRole);
UserInfo user = new UserInfo();
user.setUsername("user");
user.setPassword(passwordEncoder.encode("123"));
user.setRoles(roles);
userInfoRepository.save(user);
}
}
1.6 运行测试
运行成功的话,可以看到自动构建表:
role的数据:
user_info的数据:
user_role的数据:
到这里已经支持角色动态从数据库中进行加载了,测试下admin和user吧。
历史文章
215.Spring Boot+Spring Security:初体验
216.Spring Boot+Spring Security:基于内存的认证信息
217.Spring Boot+Spring Security:基于内存的角色授权
218.Spring Boot+Spring Security:基于内存数据库的身份认证和角色授权
219.Spring Boot+Spring Security:基于MySQL数据库的身份认证和角色授权
220.Spring Boot+Spring Security:自定义登录页面和构建主页
221.Spring Boot+Spring Security:登出和403处理