表设计
用户和角色多对多关系
角色和权限多对多关系
因为想要达到多对多的效果,所以需要单独维护两张关系表
几个查询操作
用户和角色
#
select c.name ,role_code
from cat c , role r , cat_role_relation cr
where c.id = cr.cat_id
and r.id = cr.role_id;
一个用户对应多个角色,反之每个角色也可以赋给不同的用户
角色和权限
#角色和权限多对多
select r.role_code ,p.api
from role r, permission p ,role_permission_relation rp
where r.id = rp.role_id
and p.id = rp.permission_id;
一个角色对应了多个权限,反之这些权限也可以赋给不同的角色
根据猫猫名找到角色
#根据猫猫名获取RoleCode
select r.role_code
from role r left join cat_role_relation cr on r.id = cr.role_id
left join cat c on c.id = cr.cat_id
where c.name='maomao';
返回一个角色列表
获取权限
#根据角色列表返回所有权限
select p.api
from permission p left join role_permission_relation rp on p.id = rp.permission_id
left join role r on rp.role_id = r.id
where r.role_code IN('admin','user')
Mybatis
之前是用mybatis plus写的,各种条件查询器wrapper然后嵌套这种,这次改用mybatis直接写
获取角色列表
@Select({"select r.role_code",
"from role r left join cat_role_relation cr on r.id = cr.role_id",
"left join cat c on c.id = cr.cat_id",
"where c.name=#{name};"
})
List<String> getRoleCode(String name);
获取权限列表
@Select({
"<script>",
"select p.api",
"from permission p " ,
"left join role_permission_relation rp on p.id = rp.permission_id",
"left join role r on rp.role_id = r.id",
"where r.role_code IN ",
"<foreach collection='roleCodeList' item= 'roleCode' open='(' separator =',' close=')'>",
"#{roleCode}",
"</foreach>",
"</script>"
})
List<String> getPermission(@Param("roleCodeList") List<String> roleCodeList);
动态加载角色权限数据
UserDetails:校验需要的用户信息都在这里,详情看文档UserDetails的解释 => DOC
实现自己的UserDetails
继承这个接口,里面的几个布尔值暂时全部设为true
@Data
public class CatDetails implements UserDetails {
private String name;
private String password;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
Collection<? extends GrantedAuthority> authorities;
//用户权限集合
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
//获取密码
@Override
public String getPassword() {
return password;
}
//获取用户名
@Override
public String getUsername() {
return name;
}
//账户是否没过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//账户是否没被锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
//密码是否没过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//账户是否可用 caotamade
@Override
public boolean isEnabled() {
return true;
}
}
真正校验需要的Service,
@Component
public class CatDetailsService implements UserDetailsService {
@Autowired
private CatService catService;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
//查询用户基本信息
CatDetails catDetails = catService.getCatDetailsByName(name);
//查询当前猫猫的角色列表
List<String> roleCodeList = catService.getRoleCodeList(catDetails.getName());
//根据当前猫猫的角色列表查询所拥有权限的api
List<String> authorities = catService.getPermissionApi(roleCodeList);
//添加标识前缀
roleCodeList = roleCodeList.stream().map(roleCode -> "ROLE_" + roleCode).collect(Collectors.toList());
authorities.addAll(roleCodeList); //角色也是权限,所以要添加到权限列表中
catDetails.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(
String.join(",", authorities)
));
log.info(catDetails.toString());
return catDetails;
}
}
测试下CatDetilasService的返回结果,应该是这样的
CatDetails(name=maomao,
password=$2a$10$6YCw0UapkFqwiioxteD/TeXauzTYpYA361772L5FsQTnR50N4yvKC,
isAccountNonExpired=true,
isAccountNonLocked=true,
isCredentialsNonExpired=true,
isEnabled=true,
authorities=
[/cat/add, /cat/delete, /cat/update,
/cat/search, /cat/search, ROLE_admin, ROLE_user])
\
指定Security的认证模式(FormLogin)然后编写规则
把CatDetailsService丢进去
动态鉴权
之前需要在config中写好访问URL时对应需要的权限,想想如果有100个路径难道要每个都手动指定权限吗
思路
动态鉴权的思路就是当我们访问时,拿到当前请求的uri 和 当前访问用户详情的权限列表,然后做对比,如果这个uri在认证主体的权限列表中,那么就返回true放行请求
- 注 uri不是URL,是地址和端口号后面的路径,例如
http://localhost:8099/cat/search
中的uri就是
/cat/search
实现自己的鉴权逻辑
- 新建一个类叫它
PermissionService
@Component("permissionService")
public class PermissionService {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
//拿到认证的主体
Object principal = authentication.getPrincipal();
//是否为UserDetails类型
if (principal instanceof UserDetails) {
//做一次强转
UserDetails userDetails = (UserDetails) principal;
//简单授权规则把uri填进去
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(request.getRequestURI());
//判断是否含有该规则
return userDetails.getAuthorities().contains(simpleGrantedAuthority);
}
return false;
}
}
文档中描述它是获取主体
关于这个SimpleCrantedAuthority类型的权限,名这么长不用怕他,这玩意我们见过,文档中是这么描述的
在实现自己的UserDetails时那个权限集合就是存这个的
最后