基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户能够访问并且只能访问本身被受权的资源。java
权限管理包括用户身份认证和受权两部分,简称认证受权。对于须要访问控制的资源用户首先通过身份认证,认证经过后用户具备该资源的访问权限方可访问。mysql
身份认证,就是判断一个用户是否为合法用户的处理过程。最经常使用的简单身份认证方式是系统经过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则须要刷卡。jquery
上边的流程图中须要理解如下关键对象:web
Subject:主体spring
访问系统的用户,主体能够是用户、程序等,进行认证的都称为主体;sql
Principal:身份信息数据库
是主体(subject)进行身份认证的标识,标识必须具备惟一性,如用户名、手机号、邮箱地址等,一个主体能够有多个身份,可是必须有一个主身份(PrimaryPrincipal)。tomcat
credential:凭证信息安全
是只有主体本身知道的安全信息,如密码、证书等。bash
受权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后须要分配权限方可访问系统的资源,对于某些资源没有权限是没法访问的。
下图中橙色为受权流程。
受权可简单理解为who对what(which)进行How操做:
Who,即主体(Subject),主体须要访问系统中的资源。
What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型和资源实例,好比商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。
How,权限/许可(Permission),规定了主体对资源的操做许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,经过权限可知主体对哪些资源都有哪些操做许可。
权限分为粗颗粒和细颗粒,粗颗粒权限是指对资源类型的权限,细颗粒权限是对资源实例的权限。
主体、资源、权限关系以下图:
对上节中的主体、资源、权限经过数据模型表示。
主体(帐号、密码)
资源(资源名称、访问地址)
权限(权限名称、资源id)
角色(角色名称)
角色和权限关系(角色id、权限id)
主体和角色关系(主体id、角色id)
以下图:
以下图:
一般企业开发中将资源和权限表合并为一张权限表,以下:
资源(资源名称、访问地址)
权限(权限名称、资源id)
合并为:
权限(权限名称、资源名称、资源访问地址)
上图常被称为权限管理的通用模型,不过企业在开发中根据系统自身的特色还会对上图进行修改,可是用户、角色、权限、用户角色关系、角色权限关系是须要去理解的。
对主体分配权限,主体只容许在权限范围内对资源进行操做,好比:对u01用户分配商品修改权限,u01用户只能对商品进行修改。
权限分配的数据一般须要持久化,根据上边的数据模型建立表并将用户的权限信息存储在数据库中。
用户拥有了权限便可操做权限范围内的资源,系统不知道主体是否具备访问权限须要对用户的访问进行控制。
RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制,好比:主体的角色为总经理能够查询企业运营报表,查询员工工资信息等,访问控制流程以下:
上图中的判断逻辑代码能够理解为:
if(主体.hasRole("总经理角色id")){
查询工资
}
缺点:以角色进行访问控制粒度较粗,若是上图中查询工资所须要的角色变化为总经理和部门经理,此时就须要修改判断逻辑为“判断主体的角色是不是总经理或部门经理”,系统可扩展性差。
修改代码以下:
if(主体.hasRole("总经理角色id") || 主体.hasRole("部门经理角色id")){
查询工资
}
RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制,好比:主体必须具备查询工资权限才能够查询员工工资信息等,访问控制流程以下:
上图中的判断逻辑代码能够理解为:
if(主体.hasPermission("查询工资权限标识")){
查询工资
}
优势:系统设计时定义好查询工资的权限标识,即便查询工资所须要的角色变化为总经理和部门经理也只须要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。
对资源类型的管理称为粗颗粒度权限管理,即只控制到菜单、按钮、方法,粗粒度的例子好比:用户具备用户管理的权限,具备导出订单明细的权限。对资源实例的控制称为细颗粒度权限管理,即控制到数据级别的权限,好比:用户只容许修改本部门的员工信息,用户只容许导出本身建立的订单明细。
对于粗颗粒度的权限管理能够很容易作系统架构级别的功能,即系统功能操做使用统一的粗颗粒度的权限管理。
对于细颗粒度的权限管理不建议作成系统架构级别的功能,由于对数据级别的控制是系统的业务需求,随着业务需求的变动业务功能变化的可能性很大,建议对数据级别的权限控制在业务层个性化开发,好比:用户只容许修改本身建立的商品信息能够在service接口添加校验实现,service接口须要传入当前操做人的标识,与商品信息建立人标识对比,不一致则不容许修改商品信息。
基于url拦截是企业中经常使用的权限管理方法,实现思路是:将系统操做的每一个url配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能经过Filter进行过虑,过虑器获取到用户访问的url,只要访问的url是用户分配角色中的url则放行继续访问。
以下图:
对于权限管理基本上每一个系统都有,使用权限管理框架完成权限管理功能的开发能够节省系统开发时间,而且权限管理框架提供了完善的认证和受权功能有利于系统扩展维护,可是学习权限管理框架是须要成本的,因此选择一款简单高效的权限管理框架显得很是重要。
jdk:1.7.0_72
web容器:tomcat7
系统框架:springmvc3.2.0+mybatis3.2.7(详细参考springmvc教案)
前台UI:jquery easyUI1.2.2
建立mysql5.1数据库
建立用户表、角色表、权限表、角色权限关系表、用户角色关系表。
导入脚本,先导入shiro_sql_talbe.sql再导入shiro-sql_table_data.sql
用户登录成功记录activeUser信息并将activeUser存入session。
public class ActiveUser implements java.io.Serializable {
private String userid;//用户id
private String usercode;// 用户帐号
private String username;// 用户名称
private List<SysPermission> menus;// 菜单
private List<SysPermission> permissions;// 权限复制代码
anonymousURL.properties公开访问地址,无需身份认证便可访问。
commonURL.properties公共访问地址,身份认证经过无需分配权限便可访问。
使用springmvc拦截器对用户身份认证进行拦截,若是用户没有登录则跳转到登录页面,本功能也可使用filter实现 。
public class LoginInterceptor implements HandlerInterceptor {
// 在进入controller方法以前执行
// 使用场景:好比身份认证校验拦截,用户权限拦截,若是拦截不放行,controller方法再也不执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// 校验用户访问是不是公开资源地址(无需认证便可访问)
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
// 用户访问的url
String url = request.getRequestURI();
for (String open_url : open_urls) {
if (url.indexOf(open_url) >= 0) {
// 若是访问的是公开 地址则放行
return true;
}
}
// 校验用户身份是否定证经过
HttpSession session = request.getSession();
ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
if (activeUser != null) {
// 用户已经登录认证,放行
return true;
}
// 跳转到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,
response);
return false;
}复制代码
使用springmvc拦截器对用户访问url进行拦截,若是用户访问的url没有分配权限则跳转到无权操做提示页面(refuse.jsp),本功能也可使用filter实现。
public class PermissionInterceptor implements HandlerInterceptor {
// 在进入controller方法以前执行
// 使用场景:好比身份认证校验拦截,用户权限拦截,若是拦截不放行,controller方法再也不执行
// 进入action方法前要执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
// 用户访问地址:
String url = request.getRequestURI();
// 校验用户访问是不是公开资源地址(无需认证便可访问)
List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
// 用户访问的url
for (String open_url : open_urls) {
if (url.indexOf(open_url) >= 0) {
// 若是访问的是公开 地址则放行
return true;
}
}
//从 session获取用户公共访问地址(认证经过无需分配权限便可访问)
List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
// 用户访问的url
for (String common_url : common_urls) {
if (url.indexOf(common_url) >= 0) {
// 若是访问的是公共地址则放行
return true;
}
}
// 从session获取用户权限信息
HttpSession session = request.getSession();
ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
// 取出session中权限url
// 获取用户操做权限
List<SysPermission> permission_list = activeUser.getPermissions();
// 校验用户访问地址是否在用户权限范围内
for (SysPermission sysPermission : permission_list) {
String permission_url = sysPermission.getUrl();
if (url.contains(permission_url)) {
return true;
}
}
// 跳转到页面
request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(
request, response);
return false;
}
复制代码
用户输入用户帐号和密码登录,登录成功将用户的身份信息(用户帐号、密码、权限菜单、权限url等)记入activeUser类,并写入session。
//用户登录提交
@RequestMapping("/loginsubmit")
public String loginsubmit(HttpSession session,String usercode,String password,String randomcode) throws Exception{
//校验验证码
//从session获取正确的验证码
String validateCode = (String)session.getAttribute("validateCode");
if(!randomcode.equals(validateCode)){
//抛出异常:验证码错误
throw new CustomException("验证码 错误 !");
}
//用户身份认证
ActiveUser activeUser = sysService.authenticat(usercode, password);
//记录session
session.setAttribute("activeUser", activeUser);
return "redirect:first.action";
}复制代码
/**
*
* <p>
* Title: authenticat
* </p>
* <p>
* Description:用户认证
* </p>
*
* @param usercode
* 用户帐号
* @param password
* 用户密码
* @return ActiveUser 用户身份信息
* @throws Exception
*/
public ActiveUser authenticat(String usercode, String password)
throws Exception;
// 根据帐号查询用户
public SysUser findSysuserByUsercode(String usercode) throws Exception;
// 根据用户id获取权限
public List<SysPermission> findSysPermissionList(String userid)
throws Exception;
// 根据用户id获取菜单
public List<SysPermission> findMenuList(String userid) throws Exception;
复制代码
文章有不当之处,欢迎指正,你也能够关注个人微信公众号:
好好学java
,获取优质资源。