一.基于角色的权限管理
访问控制策略一般有以下几种方式:
- 自主型访问控制(DAC):用户/对象来决定访问权限。信息的所有者来设定谁有权限来访问信息以及操作类型(读、写、执行。。。)。是一种基于身份的访问控制。例如UNIX权限管理。
- 强制性访问控制(MAC):系统来决定访问权限。安全属性是强制型的规定,它由安全管理员或操作系统根据限定的规则确定的,是一种规则的访问控制。
- 基于角色的访问控制(RBAC):角色决定访问权限。用组织角色来同意或拒绝访问。比MAC、DAC更灵活,适合作为大多数公司的安全策略,但对一些机密性高的政府系统不适用。
Role-Based Access Control - 规则驱动的基于角色的访问控制:提供了一种基于约束的访问控制,用一种灵活的规则描述语言和一种ixn的信任规则执行机制来实现。
- 基于属性证书的访问控制:访问权限信息存放在用户属性证书的权限属性中,每个权限属性描述了一个或多个用户的访问权限。但用户对某一资源提出访问请求时,系统根据用户的属性证书中的权限来判断是否允许或拒绝;
RBAC(Role-Based Access Control)
基于角色的访问控制,模型是20世纪90年代研究出来的一种新模型,但从本质上讲,这种模型是对前面描述的访问矩阵模型的扩展。这种模型的基本概念是把许可权(Permission)与角色(Role)联系在一起,用户通过充当合适角色的成员而获得该角色的许可权。
在实际的组织中,为了完成组织的业务工作,需要在组织内部设置不同的职位,职位既表示一种业务分工,又表示一种责任与权利。根据业务分工的需要,职位被划分给不同群体,各个群体的人根据其工作任务的需要被赋予不同的职责和权利,每个人有权了解与使用与自己任务相关的信息与资源,对于那些不应该被知道的信息则应该限制他们访问。这就产生了访问控制的需求。
例如,在一个大学中,有校长、副校长、训练部长、组织处长、科研处长、教保处长等不同的职位,在通常情况下,职位所赋予的权利是不变的,但在某个职位上工作的人可以根据需要调整。RBAC模型对组织内部的这些关系与访问控制要求给出了非常恰当的描述。
二.权限和角色的操作
权限注解
RequiredPermission
//该注解贴在哪个方法上就表示该方法 需要权限才能访问
@Retention(RetentionPolicy.RUNTIME)//注解可以加载到JVM中
@Target(ElementType.METHOD)//注解可以贴在方法上
public @interface RequiredPermission {
String value();//描述权限的名称
}
在Controller中的方法,贴上注解,表示该方法需要权限访问
@RequiredPermission("编辑员工")
@RequestMapping("input")
public String input(Long id,Model model){
if (id != null) {
Employee employee = employeeService.get(id);
System.out.println("------>>>>>>: "+employee);
model.addAttribute(employee);
}
model.addAttribute("depts",departmentService.list());
model.addAttribute("roles", roleService.list());
return "employee/input";
}
加载权限的方法,保存数据
DpartementServiceImple
public void reload() {
//先查询数据库中已有的权限表达式
List<String> exprs = permissionMapper.selectAllExpressions();
//1. 从容器获取所有的Controller对象
Collection<?> beans = ctx.getBeansWithAnnotation(Controller.class).values();
for (Object ctrl : beans) {
//2. 获取Controller中所有的方法对象
Method[] methods = ctrl.getClass().getDeclaredMethods();
for (Method method : methods) {
//3. 判断每一个方法是否有权限注解
RequiredPermission anno = method.getAnnotation(RequiredPermission.class);
if (anno != null) {
//拿到方法的权限表达式
String exp = PermissionUtil.buildExpression(method);
if (!exprs.contains(exp)){//不包含该权限才保存
//4. 如果有注释把该方法做成一个权限对象
Permission permission = new Permission();
permission.setName(anno.value());
permission.setExpression(exp);
//5. 保存到数据库
permissionMapper.insert(permission);
}
}
}
}
}
RoleMapper
<resultMap id="baseMap" type="Role">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="sn" column="sn"/>
<!--查询关系的多方对象-->
<collection property="permissions" column="id"
select="cn.dusk.rbac.mapper.PermissionMapper.selectPermissionsByRoleId"/>
</resultMap>
PermissionMapper
<!--用于关联查询-->
<select id="selectPermissionsByRoleId" resultType="Permission">
select
p.id,p.name
from permission p join role_permission rp
on p.id = rp.permission_id
where rp.role_id = #{roleId}
</select>
IRoleService
public interface IRoleService {
void saveOrUpdate(Role entity, Long[] permissionIds);
void delete(Long id);
Role get(Long id);
List<Role> list();
ResultPage query(QueryObject qo);
}
RoleServiceImpl
@Service
public class RoleServiceImpl implements IRoleService{
@Autowired
private RoleMapper roleMapper;
public void saveOrUpdate(Role entity, Long[] permissionIds) {
if (entity.getId() == null) {
roleMapper.insert(entity);
} else {
//删除旧的关系
roleMapper.deleteRelation(entity.getId());
roleMapper.updateByPrimaryKey(entity);
}
//保存角色和权限的关系
if (permissionIds != null) {
for (Long pId : permissionIds) {
roleMapper.insertRelation(entity.getId(),pId);
}
}
}
public void save(Role role) {
roleMapper.insert(role);
}
public void delete(Long id) {
//先删除关系
roleMapper.deleteRelation(id);
roleMapper.deleteByPrimaryKey(id);
}
public Role get(Long id) {
Role role = roleMapper.selectByPrimaryKey(id);
return role;
}
public List<Role> list() {
List<Role> list = roleMapper.selectAll();
return list;
}
public ResultPage query(QueryObject qo) {
int rows = roleMapper.queryForCount(qo);
List<?> data = roleMapper.queryForList(qo);
return new ResultPage(qo.getCurrentPage(), qo.getPageSize(), rows, data);
}
}
三.登录和拦截
登录Controller
LoginController
@Controller
public class LoginController {
@Autowired
private IEmployeeService employeeService;
@RequestMapping("login")
public String login(String username, String password, Model model){
try {
employeeService.login(username, password);
} catch (Exception e) {
e.printStackTrace();
model.addAttribute("msg",e.getMessage());
return "forward:/login.jsp";
}
return "redirect:/main.do";
}
@RequestMapping("main")
public String main(){
return "main";
}
}
EmployeeServiceImple
public void login(String name,String password){
Employee employee = employeeMapper.selectEmployeeByInfo(name, password);
if (employee == null) {
throw new RuntimeException("账号密码不匹配");
}
HttpSession session = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest().getSession();
//把当前登录成功的用户存入session
session.setAttribute("EMP_IN_SESSION",employee);
//把当前用户的拥有权限表达式查询出来存入session,目的用于权限检验
List<String> exps = permissionMapper.selectExpressionsByEmployeeId(employee.getId());
session.setAttribute("EXPS_IN_SESSION",exps);
}
}
拦截器
CheckLoginInterceptor
//检查登录拦截器
public class CheckLoginInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从session中取出当前登录的用户
Object emp = request.getSession().getAttribute("EMP_IN_SESSION");
if (emp == null) {//没有登录
response.sendRedirect("/login.jsp");
return false;//不放行
}
return true;//放行
}
}
mvc.xml
<!--配置拦截器-->
<mvc:interceptors>
<!--登录拦截-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login.do"/>
<bean class="cn.dusk.rbac.interceptor.CheckLoginInterceptor"/>
</mvc:interceptor>
<!--权限检查拦截器-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login.do"/>
<bean class="cn.dusk.rbac.interceptor.SecurityInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
四.安全控制拦截
安全控制拦截
SecurityInterceptor
public class SecurityInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//获取当前登录的用户
Employee employee = (Employee)session.getAttribute("EMP_IN_SESSION");
System.out.println("拦截器: --->" +employee);
//判断如果是超级管理员
if (employee.isAdmin()){
System.out.println("超级管理员");
return true;
}
//拿到当前访问的方法对象
HandlerMethod hd = (HandlerMethod) handler;
Method method = hd.getMethod();
//判断当前方法是否需要权限
if (!method.isAnnotationPresent(RequiredPermission.class)){
System.out.println("不需要权限");
return true;//该方法不需要权限,放行
}
//判断当前用户是否拥有执行的权限
String exp = PermissionUtil.buildExpression(method);
List<String> exps = (List<String>)session.getAttribute("EXPS_IN_SESSION");
if(exps.contains(exp)){
System.out.println("拥有执行权限");
return true;//当前用户有权限放行
}
//没有权限跳转的页面
System.out.println("没有权限");
request.getRequestDispatcher("/WEB-INF/views/common/nopermission.jsp").forward(request,response);
return false;
}
}