首先,需要在MySQL数据库中创建五个表,分别为user、user_role、role、permission、role_permission表;
user表需要的基本字段:id, username, password, createtime;
user_role表需要的基本字段:user_id, role_id;
role表需要的基本字段:role_id, role_name, role_key(角色权限字符串,为admin或者common), remark;
permission表需要的基本字段:id, perms, url;
role_permission表需要的基本字段:id, role_id, permission_id
在user实体类中添加list类型的role使多表查询时候进行连接
在role实体类中添加list类型的permission使多表查询时候进行连接
查询角色授权和权限授权的sql语句
<!--多表联查查询用户角色授权-->
<select id="queryUserRoleById" parameterType="integer" resultMap="RoleMap">
SELECT DISTINCT *
FROM role r
LEFT JOIN user_role ur ON ur.role_id = r.role_id
LEFT JOIN user us ON us.id = ur.user_id
WHERE us.id = #{userid}
</select>
<!--多表联查查询用户权限授权-->
<select id="queryPermsByUserId" resultType="string" parameterType="integer">
SELECT distinct pr.perms
FROM permission pr
LEFT JOIN role_permission rp ON pr.id = rp.permission_id
LEFT JOIN role r ON rp.role_id = r.role_id
LEFT JOIN user_role ur ON r.role_id = ur.role_id
LEFT JOIN user us ON us.id = ur.user_id
WHERE us.id = #{id}
</select>
Service层的写法
/**
* 根据用户ID查询角色
* 这个方法写在role的service层
* @param userId 用户ID
* @return 权限列表
*/
Set<String> selectRoleKeys(Integer userId);
/**
* 根据用户ID查询权限
* 这个方法写在permission的service层
* @param userId 用户ID
* @return 权限列表
*/
Set<String> selectPermsByUserId(Integer userId);
impl的写法
/**
* 根据用户ID查询角色
*这个方法写在role的impl层
* @param userId 用户ID
* @return 权限列表
*/
@Override
public Set<String> selectRoleKeys(Integer userId) {
List<Role> perms = roleDao.queryUserRoleById(userId);
Set<String> permsSet = new HashSet<>();
for (Role perm : perms) {
if (perm!=null) {
permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(",")));
}
}
return permsSet;
}
/**
* 根据用户ID查询权限
* 这个方法写在permission的impl层
* @param userId 用户ID
* @return 权限列表
*/
@Override
public Set<String> selectPermsByUserId(Integer userId) {
List<String> perms = permissionDao.queryPermsByUserId(userId);
Set<String> permsSet = new HashSet<>();
for (String perm : perms) {
if (StringUtils.isNotEmpty(perm)) {
permsSet.addAll(Arrays.asList(perm.trim().split(",")));
}
}
return permsSet;
}
在自定义的realm中的doGetAuthorizationInfo方法中进行角色和权限的授权
/**
* 执行授权逻辑
*
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
LOGGER.info("执行授权逻辑");
// 角色列表
Set<String> roles;
// 权限列表
Set<String> perms;
// 给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
// 获取SimpleAuthenticationInfo方法的第一个参数的内容
Object obj =subject.getPrincipal();
User user;
if(obj instanceof User) {
user = (User) obj;
LOGGER.info("登录成功后类型正确获取的用户信息:right-- {}",JSON.toJSONString(user));
} else {
user = JSON.parseObject(JSON.toJSON(obj).toString(), User.class);
LOGGER.info("登录成功后类型错误获取的用户信息:worry-- {}",JSON.toJSONString(user));
}
subject.getSession().setAttribute("user",user);
// 管理员拥有所有权限
if (user.isAdmin()) {
info.addRole("admin");
info.addStringPermission("*:*:*");
} else {
roles = roleService.selectRoleKeys(user.getId());
perms = permissionService.selectPermsByUserId(user.getId());
LOGGER.info("用户角色信息:{}",roles);
LOGGER.info("用户权限信息:{}",perms);
// 角色加入AuthorizationInfo认证对象
info.setRoles(roles);
// 权限加入AuthorizationInfo认证对象
info.setStringPermissions(perms);
}
return info;
}
因为将缓存存储到Redis中,取出的时候devtools热部署会提示类型转换错误,所有需要判断取出的类型是否正确,如果不正确需要将它强制类型转换成需要的数据格式。
判断是否是管理员的isAdmin()方法的写法,写在user的实体类中
public static boolean isAdmin(Integer userId) {
return userId != null && 1L == userId;
}
public boolean isAdmin() {
return isAdmin(this.id);
}
使用方法就是在controller层的方法上添加@RequiresPermissions(value = {“user:add”})注解或者 @RequiresRoles(value = “{admin}”)注解,让用户拥有value属性中的值得时候才能拥有权限访问该方法,否则没法访问。
为了让注解生效,需要在ShiroConfig的配置类中进行配置
/**
* 开启shiro 注解模式
* 可以在controller中的方法前加上注解
* 如 @RequiresPermissions("userInfo:add")
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
本文参考若依后台网站的例子完成
网站地址