shiro系列3-Shiro简介

什么是shiro

Apache Shiro一个功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密及会话管理的解决方案。

功能

实际上,Shiro的主要功能是管理应用程序中与安全相关的全部,同时尽可能支持多种实现方法。Shiro是建立在完善的接口驱动设计和面向对象原则之上的,支持各种自定义行为。Shiro提供的默认实现,使其能完成与其他安全框架同样的功能。

功能点描述:

  1. 身份认证/登录,验证用户是不是拥有相应的身份;
  2. 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
  3. 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
  4. 加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
  5. Web支持,可以非常容易的集成到Web环境;
    Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
  6. shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
  7. 提供测试支持;
  8. 允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  9. 记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

官网使用手册

发展历程

Shiro最早的名字是JSecurity,后来更名为Shiro并成为Apache的孵化项目。现在的版本到了1.3.0

Spring Security

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI: Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

对比:

  1. Shiro is much easier to use , implement and most importantly understand than Spring
  2. The only reason Spring Security is much more well-known is because of the brand name “Spring” which is famous for simplicity , but ironically many find installing Spring Security little difficult
  3. Spring Security , however has a better community support
  4. Apache Shiro has an additional module over Spring Security of handling Cryptography

shiro的优势

  • Spring Security和spring依赖过于紧密,而且使用没有shiro来的简单。
  • shiro不依赖与spring
  • shiro不仅可以实现web应用的权限管理,还可以实现C/S系统、分布式系统权限管理
  • shiro属于比较轻量,越来越多的企业中也在使用shiro

shiro的架构

shiro架构

  • subject
    主体
  • securityManager
    安全管理器,主体访问和授权都是通过securityManager来进行,这是一个很大的容器?里面有很多的模块来实现相应的功能
  • authenticator
    认证器,主体进行认证最终通过authenticator来进行的
  • authorizer
    授权器,主体进行授权最终通过authorizer来进行
  • sessionManager
    web应用中,一般用中间件web容器对session进行管理。shiro自己也提供了一套session管理的方式。
  • sessionDAO
    通过sessionDAO来管理session数据,针对个性化的session数据存储,需要用到sessionDAO
  • catchManager
    缓存管理器,主要对session数据和授权数据进行缓存,比如将授权数据通过sessionManager进行缓存管理,和ehcache进行整合。管理的只是业务逻辑,真正的缓存操作还是要靠其他的框架来做。
  • Realm(领域)
    相当于数据源,通过realm来存取认证、授权的相关数据。
    在realm中存在授权和认证的逻辑。
  • cryptography
    密钥、密码,就是一个安全加密的方法,提供了一套加密和解密的组件以供开发。比如md5散列算法,没办法解密

开始使用shiro

下载

运行

  • 解压源代码
1
2
3
4
$ cd shiro-root-1.3.0\samples\quickstart
// 执行java中的main函数
$ mvn compile exec:java

Subject

Subject 表示某一项(如一个人)的一组相关信息。此类信息包括 Subject 的身份,以及与安全相关的属性(例如,密码和加密密钥)。

Subject 可以潜在地具有多重身份。每个身份被表示为 Subject 中的一个 Principal。Principal 只是把名称绑定到 Subject。例如,Subject 正好是一个人(Alice)时,它可以有两个主体:一个把她驾驶证上的名称 “Alice Bar” 绑定到 Subject,另一个把学生身份证上的号码 “999-99-9999” 绑定到 Subject。即使每个主体具有不同的名称,它们也都指的是同一个 Subject。

Subject 也可以拥有与安全相关的属性,它们被称为证书。敏感的证书需要特殊的保护,例如私有加密密钥存储在私有的证书 Set 中。将证书设计为共享的,例如公钥证书或 Kerberos 服务票据存储在一个公开证书 Set 中。访问和修改不同的证书 Set 需要不同的权限。

Quickstart.java

Quickstart.java中包含刚刚提到的所有内容(认证、授权等等),通过这个简单的示例可以熟悉Shiro的API。

首先,几乎所有的环境下,都可以通过这种方式获取当前用户:

1
2
// get the currently executing user:
Subject currentUser = SecurityUtils.getSubject();

通过SecurityUtils.getSubject(),就可以获取当前Subject。Subject是应用中用户的一个特定安全的缩影,虽然感觉上直接使用User会更贴切,但是实际上它的意义远远超过了User。而且每个应用程序都会有自己的用户以及框架,我们可不想和它们混淆在一起,况且Subject就是安全领域公认的名词。

在单应用系统中,调用getSubject()会返回一个Subject,它是位于应用程序中特定位置的用户信息;在服务器中运行的情况下(比如web应用),getSubject会返回一个位于当前线程或请求中的用户信息。 现在你已经得到了Subject对象,那么用它可以做什么呢?

session

如果你想得到应用中用户当前Session的其他参数,可以这样获取Session对象:

1
2
3
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );

这个Session对象是Shiro中特有的对象,它和我们经常使用的HttpSession非常相似,但还提供了额外的东西,其中与HttpSession最大的不同就是Shiro中的Session不依赖HTTP环境(换句话说,可以在非HTTP 容器下运行)。

如果将Shiro部署在web应用程序中,那么这个Session就是基于HttpSession的。但是像QuickStart示例那样,在非web环境下使用,Shiro则默认使用EnterpriseSessionManagment。也就是说,不论在应用中的任何一层使用同样的API,却不需要考虑部署环境,这一优点为应用打开一个全新的世界,因为应用中要获取Session对象再也不用依赖于HttpSession或者EJB的会话Bean。而且任何客户端技术都可以共享session 数据。

现在你可以得到当前Subject和它的Session对象。那么我们如何验证比如角色和权限这些东西呢?

很简单,可以通过已得到的user对象进行验证。Subject对象代表当前用户,但是,谁才是当前用户呢?他们可是匿名用户啊。也就是说,必须登录才能获取到当前用户。没问题,这样就可以搞定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// let's login the current user so we can check against roles and permissions:
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}

登陆过程中的异常会被捕获,不同的异常有对应的处理方式,也可以直接反馈给用户,说登陆错误请重新登陆,不用处理异常,直接让他重新登陆。