之前已经熟悉了认证登陆,接下来尝试一下权限控制。我接触的权限控制的方式主要有2种,一种是粗粒度URL级别的权限控制,还有一种就是细粒度方法级别权限控制。具体的这2种方式的定义和说明请自行查找资料。这里使用的是细粒度的URL级别的权限控制。
1 配置
这个配置在认证登陆那里已经写过,将主要的内容在贴过来:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/index" />
<property name="unauthorizedUrl" value="/unauthorized" />
<property name="filterChainDefinitions">
<value>
/index = authc
/login = anon
/logout = logout
/common/**= anon
/** = authc
</value>
</property>
</bean>
PS:anon—是可以匿名访问的,比如登陆页面,还有部分资源文件,authc—是需要权限认证的,比如登陆后的首页等。
2 实现
2.1 建类
1.用户类
package com.yzpt.entity.system;
import lombok.Getter;
import lombok.Setter;
/**
* Description: 用户类
*
* @author yzp
*
* @date 2019年9月25日
*
*/
@Setter
@Getter
public class User {
private Integer id; // 用户id
private String userName; // 用户账号
private String nickName; // 用户昵称
private String password; // 用户密码
private String salt; // 加密密码的盐
private Integer status; // 用户状态,0:创建未认证(比如没有激活,没有输入验证码等) --等待验证的用户,1:正常状态,2:用户被锁定
private String age; // 用户年龄
private String sex; // 用户性别
private String mail; // 用户邮箱
private String photo; // 用户头像
}
2.角色类
package com.yzpt.entity.system;
import lombok.Getter;
import lombok.Setter;
/**
* Description: 角色类
*
* @author yzp
*
* @date 2019年9月25日
*
*/
@Setter
@Getter
public class Role {
private Integer id; // 角色id
private String roleName; // 角色名称
private String description; // 角色描述
private Boolean avaliable = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户
private String beiyong; // 备用字段
}
3.权限类
package com.yzpt.entity.system;
import lombok.Getter;
import lombok.Setter;
/**
* Description: 权限类
*
* @author yzp
*
* @date 2019年9月25日
*
*/
@Setter
@Getter
public class Permission {
private Integer id; // 权限id
private String permissionName; // 权限名称
private String reseourceType; // 资源类型 [menu|button]
private String url; // 资源路径
private String permissionCode; // 权限代码 menu例子:role:* button例子:role:create,role:update,role:delete,role:view
private Long parentId; // 父编号
private String parentIds; // 父编号列表
private Boolean available = Boolean.FALSE; // 是否有效
private String beiyong; // 备用字段
}
PS:每个用户可以拥有多个角色,每个角色可以拥有多个权限,上述类中有的属性不一定能用到。
2.2 建表
1.用户表
CREATE TABLE `t_user` (
`id` int(100) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户账号',
`nick_name` varchar(255) DEFAULT NULL COMMENT '用户昵称',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
`salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '加密密码的盐',
`status` int(1) NOT NULL COMMENT '用户状态,0:创建未认证(比如没有激活,没有输入验证码等) --等待验证的用户,1:正常状态,2:用户被锁定',
`age` varchar(20) DEFAULT NULL COMMENT '用户年龄',
`sex` varchar(20) DEFAULT NULL COMMENT '用户性别',
`mail` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户邮箱',
`photo` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '头像地址',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modified_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
2.角色表
CREATE TABLE `t_role` (
`id` int(100) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`role_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称',
`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色描述',
`status` int(1) DEFAULT NULL COMMENT '状态: 1.有效 2.删除',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modified_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
3.权限表
CREATE TABLE `t_permission` (
`id` int(100) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`permission_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '权限名称',
`reseource_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '资源类型',
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '资源路径',
`permission_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '权限代码 menu例子:role:* button例子:role:create,role:update,role:delete,role:view',
`parent_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '父编号',
`parent_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '父编号列表',
`status` int(1) DEFAULT NULL COMMENT '状态:1.有效 2.删除',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modified_time` datetime DEFAULT NULL COMMENT '修改时间',
`sort` int(100) DEFAULT NULL COMMENT '排序',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
4.用户_角色表
CREATE TABLE `t_user_role` (
`id` int(100) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_id` int(100) NOT NULL COMMENT '用户id',
`role_id` int(100) NOT NULL COMMENT '角色id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modified_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
5.角色_权限表
CREATE TABLE `t_role_permission` (
`id` int(100) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`role_id` int(100) NOT NULL COMMENT '角色id',
`permission_id` int(100) NOT NULL COMMENT '权限id',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`modified_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
3 代码实现
1.代码配置
// 权限认证
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User user = (User)SecurityUtils.getSubject().getPrincipal();
if(user != null) {
// 获取角色
Integer userId = user.getId();
List<Role> roles = userMapper.getRolesByUserId(userId);
List<Permission> permissions = new ArrayList<Permission>();
// 获取权限
if(roles != null && roles.size() > 0) {
for(int i = 0 ; i < roles.size(); i++) {
List<Permission> ps = userMapper.getPermissionsByRoleId(roles.get(i).getId());
if(ps != null) {
permissions.addAll(ps);
}
// 角色
authorizationInfo.addRole(roles.get(i).getRoleName());
}
}
// 权限
if(permissions != null && permissions.size() > 0) {
for(int i = 0 ; i < permissions.size() ; i++) {
authorizationInfo.addStringPermission(permissions.get(i).getPermissionCode());
}
}
}
return authorizationInfo;
}
2.代码调用
/**
* Description: 用户列表查询
*
* @param request
* @param response
* @return
*
*/
@RequestMapping(value = "/userList")
@RequiresPermissions("user:view")
public String userInfo(HttpServletRequest request, HttpServletResponse response) {
String result = "";
try {
// 分页查询
int pageNo = request.getParameter("pageNo") != null ? Integer.valueOf(request.getParameter("pageNo")) : 1;
int pageSize = 10;
// 查询所有的用户
Page<User> page = userService.findByPage(pageNo, pageSize);
User currentUser = (User)SecurityUtils.getSubject().getPrincipal();
request.setAttribute("page", page);
request.setAttribute("currentUser", currentUser);
result = "userInfoList";
} catch (Exception e) {
result = "error";
logger.info("用户列表查询失败 ----- "+e);
}
return result;
}
PS:网上查找的资料很多都是在业务层添加权限验证注解的,如果需要在控制层可以通过注解的方式来进行权限验证,需要将以下配置在SpringMVC的配置中:
<!-- shiro生命周期处理器 -->
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- AOP式方法级权限检查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- 无权访问跳转页面 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">
../../unauthorized
</prop>
<prop key="org.apache.shiro.authz.UnauthenticatedException">
../../unauthorized
</prop>
</props>
</property>
</bean>