shiro安全框架
原文:http://blog.csdn.net/boonya/article/details/8233303
可能大家早先会见过 J-security,这个是 Shiro 的前身。在 2009 年 3 月初之前,这个安全框架叫做 J-security,由于某些原因,更名为 Shiro(或者 Ki,意为 Fortress),是 Apache 的孵化项目,鉴于本文编写时 Shiro 的还没有正式发布的版本,本文使用的是 Jsecurity 的稳定版本 0.9,本文中 Shiro 等同于 Jsecurity。
本文代码的开发环境:
- Jsecurity 0.9
- Grails 1.2.0
- Grails Shiro Plugin 1.0.1
- SpringSource Tool Suite 2.3
- 易于理解的 Java Security API;
- 简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
- 对角色的简单的签权(访问控制),支持细粒度的签权;
- 支持一级缓存,以提升应用程序的性能;
- 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
- 异构客户端会话访问;
- 非常简单的加密 API;
- 不跟任何的框架或者容器捆绑,可以独立运行。
JAAS —面世的时间最早,但是鉴于其在使用上有很大的限制,很少有人真正的使用它。可以说它不是一个好的应用程序级别的安全框架;
下面就开始我们的 Shiro 之旅吧!
图
1. 整体架构
- SecurityManager
典型的 Facade,Shiro 通过它对外提供安全管理的各种服务。
- Authenticator
对“Who are you ?”进行核实。通常涉及用户名和密码。
这个组件负责收集 principals 和 credentials,并将它们提交给应用系统。如果提交的 credentials
跟应用系统中提供的 credentials 吻合,就能够继续访问,否则需要重新提交 principals 和
credentials,或者直接终止访问。 - Authorizer
身份份验证通过后,由这个组件对登录人员进行访问控制的筛查,比如“who can do what”, 或者“who can do which
actions”。Shiro 采用“基于 Realm”的方法,即用户(又称 Subject)、用户组、角色和 permission 的聚合体。 - Session Manager
这个组件保证了异构客户端的访问,配置简单。它是基于 POJO/J2SE 的,不跟任何的客户端或者协议绑定。
从 Shiro 的框架图,已经能够体会到这个工具的简单了。下面让我们来看看 Shiro 是如何工作的。先了解一下它的安全模型吧!见下图:
图
2. 安全模型
- Subject 是安全领域术语,除了代表人,它还可以是应用。在单应用中,可将其视为 User 的同义词。
- Principal 是 Subject 的标识,一般情况下是唯一标识,比如用户名。
- Role 和 Permission 分别代表了不同粒度的权限,从上图中可以看出 Role 的粒度更大些,Permission 代表了系统的原子权限,比如数据的修改、删除权限。对于简单的权限应用,可以不需要 Permission。
- Realm 是一个执行者,负责真正的认证和鉴权。
- role 没有实质内容,只是代表一组 permission,目的是为了管理的方便,一般都是动态定义;
- permission 一般都是预先定义好的,不允许动态改变,除非源代码改动,它才会变化,它是整个安全模块的基础;
- 要使 permission 也能动态定义,并非不可能,但是这将使鉴权非常复杂,甚至可能导致鉴权语句遍布整个程序,得不偿失;
- 当然有一个例外:如果知道 permission 动态定义的规则和鉴权规则,如 Grails 的 fileter 中“${controllerName}:${actionName}:${params.id}”也可实现 permission 的动态定义
图
3. 关键类
AuthenticationToken 和 AuthenticationInfo
RememberMe
Credentials 和 CredentialsMatcher
图
4. CredentialsMatcher 的作用
PAM= Pluggable Authentication Modules
AuthorizationInfo
PermissionResolver 和 Permission
图
5. PermissionResolver 和 Permission 的关系
内置的权限有 2 个:
- AllPermission,总是返回 true
- WildcardPermission,权限字符串的表示方式。
如果要比较权限字符串,可以使用 permission1.implies(permission2),它分别比较对应位置的字符串,在如下情况中,结果会返回 true:
- permission1 中的子串有 * 或 permission1 子串 ==permission2 子串;
- permission1 无子串,permission2 有;
- permission1 有子串,permission2 无,permission1 的所有子串都是 *。
- 权限的比较实际是字符串的比较,只不过是考虑到了字符串的分级
- 字符串的分级划分完全由使用者自己决定,Shiro 的惯例是 3 级:资源 : 操作 : 实例。
- 字符串的使用必须一致,分隔符之间不要有空格,避免无意间引入的不一致。如:定义使用“file : create, update :
1”,而验证使用“file : update”,那么分解之后一个是“ update ”,一个是“ update”,因空格而引起不等。
Realm
图
6. JdbcRealm 的继承链
Session
SecurityManager
Filter
图
7. Filter 的作用
- web.xml 中只需配置 JSecurityFilter。对于 Spring 应用,则使用 SpringJSecurityFilter;
- 子 filter 作为主 filter 的配置参数值出现,特点是:顺序相关
- 对于多个 URL,验证顺序是由上至下,类似 Exception 的匹配。因此,使用顺序应该是由细到粗。
- 对于同一 URL,子 filter 的验证顺序是从左至右的 AND 操作。
- 如果配置值中含有分隔符,如 Permission,就需要使用引号来转义。
Subject
Configuration
图
8. 配置关系
- JSecurityFilter 创建 Configuration 实例,并将 ini 参数值传给 Configuation。在 Spring
环境中,分别使用 SpringJSecurityFilter 和 SpringIniWebConfiguration。 - Configuration 实际就是 SecurityManager 的 Factroy,对 SpringIniWebConfiguration
而言,它需要知道 SecurityManager 的 BeanName,该值由 SpringJSecurityFilter
的初始化参数“securityManagerBeanName”值决定。即 SpringJSecurityFilter,实际有两个初始化参数:
- config,是 ini 配置文件内容
- securityManagerBeanName,是 SecurityManager 的 BeanName
SecurityUtils
杂项
- AOP,提供 AOP 方面的支持,实现对某个类某个方法的拦截,从而使权限控制延伸至类的方法。
- Cache,提供缓存支持
- Codec,提供编码方面的支持
- Crypto,提供加密支持
- IO,从多个资源位置读写原始数据
- JNDI,提供 jndi 支持
- util,工具类支持
- 标签类,用于 Web 页面
在开始案例的学习之前,先作好准备工作 -- 获得 Shiro 相关的 jar 包,获取途径有两种:
- 直接到 J-security 的网站上
- ,本文用到的就是这个;
- 由于 Shiro 目前是 Apache 的孵化项目,还没有发布正式的版本,但是我们可以到 Subversion 上下载代码,之后使用 Maven 构建
mkdir shiro
cd shiro
svn co https://svn.apache.org/repos/asf/incubator/shiro/trunk/
mvn install
示例一:让
Shiro 为你的应用服务
- 用户登录后方可进入系统;
- 假定一个 message 的安全内容,用户可以创建 message 的内容,但是如果需要修改 / 删除 message 的内容就必须具有相应的权限;
- Admin 具有所有的权限;
- message 的权限跟角色关联。
图
9 程序执行的流程
好了,开始程序的编写啦!
创建安全领域类
清单
1. User/Role/Permission 的 Domain class
class User { |
这里使用了最简单的情形,即权限传递结构为:Permission -> Role -> User。通常情况下,Permission 也可以分配给单个 User。
创建一个安全实体
清单
2. message 的 Domain class
class Message { |
配置 web.xml
清单 3. 在 web.xml 加入 SecurityFilter 的内容:
<filter> |
- 这个 Filter 应该在 Grails 的 web.xml 中所有缺省的 Filter 最后;
- url-pattern 不要使用“/**”,因为这样会造成登录页的 css 和图片无法访问。解决办法,可以通过遵循“只能通过 Controller/Action 访问”这个规则,并使用 Grails 的 Filter 机制,可以保证所有安全 URL 不被非法访问。
创建 realm
清单 4. conf/spring/resources.groovy
beans = { |
接分配给 User,即 Permission 和 User 之间是多对多关系,那么 permissionsQuery 应该使用 union,即“role 相关 permission union user 相关 permission”。对于 User 有多个 Role 的情况,JdbcRealm 会循环得出总的结果。
安全守护神:SecurityFilters
清单
5. SecurityFilters
import org.jsecurity.SecurityUtils class SecurityFilters { |
- authc 表示的是所有用户对应用系统的任何访问都要经过 auth 认证,对于没有认证的用户的访问会重定向到登录界面;
- admin 表示的是属于 admin 角色的用户对 user/role/permission 具有所有权限,对于非 admin 角色的用户会对其提示没有 user/role/permission 的访问权限;
- editmessage 表示当用户对 message 进行修改或者删除的时候对其进行鉴权,只有具有 message 的 update/delete 权限的用户才能对 message 进行修改。
认证的代码
清单 6. 认证代码
def signIn = { |
- 如果对所有 message 具有修改权限,权限字符串可以为:message:delete,update:*;
- 如果针对某一个 message 具有修改权限,权限字符串可以为:message:update,delete:messageid。
示例二:使用 Shiro Plugin 快速构建安全模块
使用步骤如下:
- 安装 Shiro Plugin,在你的 grails 项目中运行:
grails install-plugin shiro
- , 会创建 grails-app/realms 目录,并提供如下新的 Grails 命令:
grails create-auth-controller
,创建 AuthController 以及登录窗口,Controller 提供了登录、登出和权限验证失败处理等 Action。grails create-db-realm
,创建一个访问数据库的 Realmgrails create-ldap-realm
,创建一个访问 ldap 的 Realmgrails create-wildcard-realm
,创建一个访问数据库的 Realm,但使用的是 Shiro 的 WildcardPermission。grails quick-start
,它是 create-auth-controller 和 create-db-realm 的集合体,是 Shiro 的快速入门,在接下来的内容中将详细介绍它的使用。- 下面进行 Shiro 快速入门。在 grails 项目的目录下执行:
grails quick-start
,这个命令会创 建如下内容:- 在 grails-app/realms 下创建 ShiroDbRealm,这个 realm 是访问控制的信息库,用来决定一个用户有权访问哪些内容;
- 在 grails-app/domain 创建 ShiroRole.groovy 和 ShiroUser.groovy;
- 在 grails-app/controllers 下创建 AuthController,这里提供了登录和退出的 action;
- 在 grails-app/views 下创建 auth 文件夹以及 login.gsp;
- 在 grails-app/conf/ 下创建 SecurityFilters.groovy,这里管理着对所有 controller 和 action 的访问控制。
- 启动程序,当访问 controller 的时候,页面就会重定向到登录页面。但是这个时候是无法登录,因为我们没有添加用户。
- 进入到 grails-app/conf/ 修改 BootStrap.groovy,
清单
7. 修改后的 BootStrap.groovydef init = { servletContext ->
def user = new User(username: "user",
passwordHash: new Sha1Hash("user").toHex())
user.save()
def role= new Role(name:"admin").addToUsers(user).save()
}重新启动程序,就能使用 user/user 登陆了。
- 在 plugin 缺省创建的 SecurityFilters 中使用了对所有 URL 进行 before 拦截,但是我们根据实际情况进行修改,例如我们要示例一的 filter 内容,就可以做如下更改:
清单
8. 更改后的 SecurityFiltersauth(controller: "*", action: "*"){
before={
accessControl{true}
}
}
management(controller: "user|role|permission", action: "*"){
before={
accessControl{role("admin")}
}
}
message(controller: "message", action: "delete|update"){
before={
accessControl{
permission("message:${actionName}:${params.id}")
}
}
}看到这里,读者可能已经注意到了,这里的代码比示例一中的 Filter 的代码简单的许多。对,Shiro 插件已经将示例一中的类似代码封装到 ShiroGrailsPlugin.groovy 中了,我们使用的时候只需要:
调用 accessControl 方法,该方法的参数可以是 Map、Closure 或者 Map+Closure;
使用 role( …… ),验证访问对象是否具有相应的角色;
使用 permission( …… ),验证访问对象是否具有相应的 Permission。
- 授权部分内容参见上一示例,这里不做冗述。
- Shiro Plugin 中除了为我们提供了上述方便之外,还提供了一些常用的 taglib 来增强用户界面 , 这些 taglib 都在
pluginPath/grails-app/taglib /ShiroTagLib.groovy 中定义,我们可以直接在 gsp 中使用它们。比如,只有具有修改权限的用户才能够看到一些修改类的按钮显示,可以这样写:
清单
9. Taglib 的使用<shiro:hasPermission permission="message:update,delete">
<span class="button">
<g:actionSubmit class="edit" value="Edit" />
</span>
<span class="button">
<g:actionSubmit class="delete"
onclick="return confirm('Are you sure?');"
value="Delete" />
</span>
</shiro:hasPermission>如下是经常使用到的 Tag:
- principal,
输出当前用户的标识 - hasRole,判断当前用户是否属于给定的角色,参数:name
- hasPermission, 判断当前用户是否具有指定的权限,参数:type,action 或者 permission
- isLoggedIn,
判断当前用户是否已经登录 - hasAnyRole,判断当前用户是否属于给定的某个角色,参数:in
- principal,
如果您已经非常了解了 Shiro,可以采用示例一的方式自己写所有的代码,但是对于初学者,作者还建议使用 Shiro plugin,这个插件帮助我们生成了安全相关的基本内容,以及提供了方便的 Tag。
这里给大家举的示例只是抛砖引玉。Shiro 真正的精髓还需要在项目中慢慢的体会。本文是引领我们走向 Shiro 的第一步。在这里要感谢胡键对本文编写的大力支持。
shiro安全框架的更多相关文章
- thymeleaf模板引擎shiro集成框架
shiro权限框架.前端验证jsp设计.间tag它只能用于jsp系列模板引擎. 使用最近项目thymeleaf作为前端模板引擎,采用HTML档,未出台shiro的tag lib,假设你想利用这段时间s ...
- shiro权限框架(一)
不知不觉接触shiro安全框架都快三个月了,这中间配合项目开发踩过无数的坑.现在回想总结下,也算是一种积累,一种分享.中间有不够完美的地方或者不好的地方,希望大家指出来能一起交流.在这里谢谢开涛老师的 ...
- Shiro安全框架【快速入门】就这一篇!
Shiro 简介 照例又去官网扒了扒介绍: Apache Shiro™ is a powerful and easy-to-use Java security framework that perfo ...
- SpringBoot集成Shiro安全框架
跟着我的步骤:先运行起来再说 Spring集成Shiro的GitHub:https://github.com/yueshutong/shiro-imooc 一:导包 <!-- Shiro安全框架 ...
- Shiro权限框架简介
http://blog.csdn.net/xiaoxian8023/article/details/17892041 Shiro权限框架简介 2014-01-05 23:51 3111人阅读 评论 ...
- Springboot整合Shiro安全框架
最近在学习Springboot,在这个过程中遇到了很多之前都没有技术知识,学习了一阵子,稍微总结一些. ---- Shiro框架 shiro框架,是一个相对比较简便的安全框架,它可以干净利落地处理身份 ...
- 在前后端分离的SpringBoot项目中集成Shiro权限框架
参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制 以及跨域的问题也有涉及
- (转) shiro权限框架详解06-shiro与web项目整合(上)
http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springM ...
- Shiro安全框架入门使用方法
详见:https://blog.csdn.net/qq_32651225/article/details/77199464 框架介绍Apache Shiro是一个强大且易用的Java安全框架,执行身份 ...
随机推荐
- 通过AJAX与ASP.NET结合实现的仿GridView增删改查功能
jQurey代码部分: 1. <script type="text/javascript"> 2. var flag = 0; 3. 4. ...
- 【栈思想、DP】NYOJ-15 括号匹配(二)
括号匹配(二) 描述 给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能 ...
- HTTP协议中的长连接和短连接(keep-alive状态)
什么是长连接 HTTP1.1规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包.不四次握手),等待在同 ...
- lightoj 1023
题意:让你输出前N个大写字母的前K个排列,按字典序,很水,直接dfs. #include<cstdio> #include<string> #include<cstrin ...
- SQL 的一些概念问答
1.触发器的作用? 答:触发器是一中特殊的存储过程,主要是通过事件来触发而被执行的.它可以强化约束,来维护数据的完整性和一致性,可以跟踪数据库内的操作从而不允许未经许可的更新和变化.可以联级运算.如, ...
- SQL合并时间段的问题
假设表结构如下所示: Tsheets 字段名 字段类型 约束 id CHAR(10) PRIMARY KEY start_date DATE CHECK(start_date<= end_dat ...
- bzoj 2393 Cirno的完美算数教室(容斥原理+搜索)
[题意] 定义C数为只包含数字2和9的数,求[L,R]内能被C数整除的个数. [思路] Dfs预处理出C数,并去除其中倍数的情况. Dfs搜索出现情况,奇数加,偶数减,当数值大于R时剪枝. [代码] ...
- bzoj 3207 花神的嘲讽计划Ⅰ(哈希法+主席树)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3207 [题意] 给定一个文本串,多次询问K长的模式串是否在文本[l,r]区间内出现. ...
- js笔记--json
1.什么事json: json是指javascript对象表示法(javascript Object Notation), json是轻量级的文本数据交换格式: json独立于语言: json具有自我 ...
- mybatis系列-04-mybatis开发dao的方法
4.1 SqlSession使用范围 4.1.1 SqlSessionFactoryBuilder 通过SqlSessionFactoryBuilder创建会话工厂SqlSession ...