权限管理及shiro框架
权限管理简介
做系统时肯定遇到最常见的就是不同的用户需求是不一样的,就拿登陆来说,一个办公管理系统,不同部门的人肯定要求的功能和权限是不一样的,那你不可能对每一个部门都写一个登陆页面,给不同的url吧!亦或者在下边选择你是什么部门的人?那每个部门还有等级。再继续选,然后个每个人写一个界面?那明显是不可能的,那我们到底要怎么实现?
最常用的方法就是划分不同的角色,赋予角色权限,然后再让用户去申请角色就好了,具体的实现慢慢说。
RBAC
RBAC基于角色的权限管理。简单理解为谁扮演什么角色,被允许做什么操作。
-
用户对象user:当前操作用户
-
角色对象role:表示权限操作许可权的集合
-
权限对象permission:资源操作许可权
例子:张三(user)下载(permission)一个高清无码的种子(资源),需要VIP权限(role)
张三--->普通用户--->授权---->VIP用户----->下载种子
根据角色授权的思想,我们需要涉及五张表)
三张主表
-
用户表
-
角色表
-
权限表
两张中间表
-
用户角色表(user_role)
-
角色资源表(permission)
上图称为权限管理的通用模型,不过企业在开发中根据系统自身的特点还会对上图进行修改,但是用户、角色、权限、用户角色关系、角色权限关系需要去理解。
shiro权限管理
shiro基本概念
shiro权限管理框架提供了用户身份认证和授权两部分,简称认证授权。
用户身份认证流程
身份认证就是判断一个用户是否为合法用户的处理过程.醉常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确.对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。
用户授权流程
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
Apcache Shiro是Java的一个安全框架。帮助我们完成:认证、授权、加密、会话管理、与Web集成、缓存等。
从功能角度看:shiro
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
Shiro架构有三个主要概念—Subject,SecurityManager、Realms
Subject
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;Subject一词是一个安全术语,其基本意思是"当前的操作用户"。它是一个抽象的概念,可以是人,也可以是第三方进程或其他类似事物,如爬虫,机器人等。在程序任意位置:Subject currentUser = SecurityUtils.getSubject(); 获取shiro一旦获得Subject,你就可以立即获得你希望用Shiro为当前用户做的90%的事情,如登录、登出、访问会话、执行授权检查等
SecurityManager
安全管理器,它是shiro功能实现的核心,负责与后边介绍的其他组件(认证器/授权器/缓存控制器)进行交互,实现subject委托的各种功能。有点类似于spirngmvc中的DispatcherServlet前端控制器。
Realms
Realm充当了Shiro与应用安全数据间的"桥梁"或者"连接器"。;可以把Realm看成DataSource,即安全数据源。执行认证(登录)和授权(访问控制)时,Shiro会从应用配置的Realm中查找相关的比对数据。以确认用户是否合法,操作是否合理
从系统结构角度看:shiro
Subject:主体,可以看到主体可以是任何可以与应用交互的"用户";
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;可以实现分布式的会话管理;
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到redis中,可以实现自己的redis SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
shiro认证
使用ini完成认证
认证步骤:
1:拷贝依赖
2:配置shiro.ini配置文件,模拟数据库用户列表
3.执行shiro登录登出操作
认证操作常见的异常:
shiro登录登出流程分析
1、调用subject.login方法进行登录,其会自动委托给securityManager.login方法进行登录;
2、securityManager通过Authenticator(认证器)进行认证;
3、Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带,相当于数据源);
4、IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
5、最后调用Subject.logout进行退出操作。
自定义realm
步骤:
1:自定义reaml,继承 AuthorizingRealm 重写3方法:getName doGetAuthorizationInfo doGetAuthenticationInfo
2:配置ini文件,指定使用自定义realm
授权方式
权限表达式定义
在ini文件中用户、角色、权限的配置规则是:"用户名=密码,角色1,角色2..." "角色=权限1,权限2...",首先根据用户名找角色,再根据角色找权限,角色是权限集合。
权限字符串的规则是:"资源标识符:操作:资源实例标识符",意思是对哪个资源的哪个实例具有什么操作,":"是资源/操作/实例的分割符,权限字符串也可以使用*通配符。
例子:
用户创建权限:user:create,或user:create:*
用户修改实例001的权限:user:update:001
用户实例001的所有权限:user:*:001
一般而已,我们操作只需要关注前面两节:
资源:操作 :
*:* : 所有资源的所有操作权限--->admin
使用ini方式判断是否有角色/权限
步骤:
1:配置ini文件
2:加载配置文件,测试用户是否拥有角色
自定义realm完成授权
步骤
1:自定义PermissionRealm继承AuthorizingRealm重写3个方法:getName doGetAuthorizationInfo doGetAuthenticationInfo
2
授权源码分析
1、首先调用Subject.isPermitted*/hasRole*接口,其会委托给SecurityManager,而SecurityManager接着会委托给Authorizer
2、Authorizer是真正的授权者,如果我们调用如isPermitted("user:view"),其首先会通过PermissionResolver把字符串转换成相应的Permission实例;
3、在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;
4、Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted*/hasRole*会返回true,否则返回false表示授权失败。
Web集成
Shiro与Web集成,主要是通过配置一个ShiroFilter拦截所有URL,其中ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,是所有请求入口点,负责根据配置(如ini配置文件),判断请求进入URL是否需要登录/权限等工作。
web项目集成shiro
步骤
1:导入相关依赖jar包,多出一个shiro-web.jar包
2:在web.xml文件中配置shiro的过滤器shiroFilter
3:配置shiro.ini配置文件
shiro默认过滤器
过滤器简称 对应的java类
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
user org.apache.shiro.web.filter.authc.UserFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
anon:匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例"/static/**=anon"
authc:表示需要认证(登录)才能使用;示例"/**=authc"
主要属性:usernameParam:表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名(rememberMe);loginUrl:登录页面地址(/login.jsp);successUrl:登录成功后的默认重定向地址; failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure);
authcBasic:Basic HTTP身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application);
roles:角色授权拦截器,验证用户是否拥有资源角色;示例"/admin/**=roles[admin]"
perms:权限授权拦截器,验证用户是否拥有资源权限;示例"/user/create=perms["user:create"]"
user:用户拦截器,用户已经身份验证/记住我登录的都可;示例"/index=user"
logout:退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例"/logout=logout"
port:端口拦截器,主要属性:port(80):可以通过的端口;示例"/test= port[80]",如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样
rest:rest风格拦截器,自动根据请求方法构建权限字符串(GET=read, POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串;
示例"/users=rest[user]",会自动拼出"user:read,user:create,user:update,user:delete"权限字符串进行权限匹配(所有都得匹配,isPermittedAll);
ssl:SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;
注:
anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器
shiro登录拦截器解析
authc登录拦截器工作原理
authc拦截器有2个作用:
1>登录认证
请求进来时,拦截并判断当前用户是否登录了,如果已经登录了放行, 如果没有登录,跳转到authc.loginUrl属性配置的路径,注意:默认是/login.jsp
2>执行登录认证
请求进来时,如果请求的路径为authc.loginUrl属性配置的路径(没配置,默认是/login.jsp)时,如果当前用户没有登录,authc这个拦截器会尝试获取请求中的账号跟密码值,然后比对ini配置文件或者realm中的用户列表,如果比对正确,直接执行登录操作,反之,抛异常,跳转到authc.loginUrl指定的路径。
注意:请求中账号与密码必须固定为username 跟password, 如果需要改动必须额外指定,authc.usernameParam=xxx authc.passwordParam=xxxx
authc登录成功之后处理逻辑:
authc登录失败之后处理逻辑:
Shiro的jsp标签
步骤
1:在jsp中引入shiro标签库
2:标签是否拥有权限进而显示操作按钮
spring集成-项目准备
步骤:
1:解压:shiro-spring.zip 文件
2:将shiro-spring文件导入idea中
3:创建一个shiro数据库, utf-8编码
4:导入shiro.sql数据
5:修改resources/jdbc.properties中username跟password 值
6:配置tomcat服务器
7:启动tomcat服务器访问:http://localhost:8080/main
spring集成-shiro配置
步骤:
1:添加shiro与spring集成依赖jar包
2:在web.xml中配置shiro与spring框架集成需要shiroFilter代理类
3:配置shiro配置文件
1>拷贝spring.xml文件, 删除调用除了根元素之外的所有内容,然后配置shiro相关操作
2>在mvc.xml文件中配置引入spring-shiro.xml文件
4:测试,启动服务器,访问:http://localhost:8080/main
shiro登录操作
步骤:
1:重写LoginController类,实现登录操作
2:重写UserRealm中的doGetAuthenticationInfo, 注意需要注入IUserDAO对象操作数据库
在spring-shiro.xml文件中,修改自定义UserRealm,注入IUserDAO属性值
3:先请求/main, 再登录,登录成功直接跳转到main请求路径
shiro静态授权
步骤:
1:分析权限控制选择
1>编程式,缺点:必须进入请求方法中才能判断是否有权限,放弃
2>jsp标签方式, 缺点:虽然在页面上没有显式请求按钮,但是可以通过浏览器地址栏中输入请求访问, 放弃
3>注解方式:优点,可以在请求进入方法之前进行权限控制。 推荐
2:在需要权限控制的方法上面贴上权限标签:(此处仅仅讨论居于权限的表达式空:permission)
3:在spring-shiro.xml文件中配置权限注解支持, 让权限注解生效@RequiresPermissions
4:添加用户权限(静态操作方式:模拟查询数据库)
5:在spring-shiro.xml配置没有权限异常信息统一处理方式:原先在shiroFilter配置是无效的,因为springmvc重新定制异常处理方式
6:测试, 分别使用zhangsan 与admin账号登录, 点击不同请求观察效果
shiro权限-角色-用户关系分析
用户-角色-权限数据初始化(权限的分配)
步骤:
1:在role表中添加2个角色 部门经理(deptMgr) 人事经理(empMgr)
2:给人事经理分配权限:员工的crud权限
在role_permission表中添加4条数据
如上图操作, 此时人事经理这个角色被允许执行员工列表 员工删除 员工保存 员工编辑的操作(权限)
3:给用户指派某个角色:给zhangsan指定人事经理这个角色
在user_role表中添加1条数据
如上图操作, 此时张三这个用户扮演人事经理这个角色,被允许执行执行员工列表 员工删除 员工保存 员工编辑的操作
数据库方式授权
步骤:
1: 需要执行上述的数据权限分配
2:在自定义的UserRealm添加2个属性:IRoleDAO IPermissionDAO
注意:同时修改spring-shiro.xml文件中UserRealm定义,注入dao实现类
3:重写改动原先授权操作,改为使用数据库的方式授权
4:测试
1>先使用zhangsan账号登录查看是否有部门操作权限, 如果没有表示授权成功
2>再使用admin账号登录,查看是否有部门操作权 如果没有表示授权失败
根据权限显示列表