20. 10 Minute Tutorial 十分钟教程

Introduction 前言

欢迎来到 Apache Shiro 十分钟教程!

希望通过这个简单、快速的示例,可以让你对应用程序中使用 Shiro 有个深入的了解。嗯,10分钟你应该可以搞定它。

Overview 概述

Apache Shiro 是什么?

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

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

那么 Apache Shiro 能用来做什么呢?

很多,很多,嘿嘿。但是不在快速指南中做介绍,如果你想知道,那怎么办呢?去这里找寻你的答案吧。当然如果你还想知道我们什么时候,以及为什么要“创造”Shiro,去看看Shrio的历史和使命吧。

现在让我们动手做点儿什么吧。

Shiro可以在任何环境下运行,小到最简单的命令行应用,大到大型的企业应用以及集群应用。但是我们准备在快速指南中使用最最简单的 main 方法的方式,让你对 Shiro 的API有个感官的认识。

Download 下载

  1. 确保已经安装了 JDK1.5+ 和 Maven2.2+
  2. 去这里下载最新已发布的源码。例子中我们使用 1.1.0 发布版本。
  3. 解压源代码

    unzip shiro-root-1.2.0-source-release.zip

  4. 进入快速指南文件夹

    cd shiro-root-1.1.0/samples/quickstart

  5. 运行快速指南

    mvn compile exec:java

过程中会输出日志信息,用来告诉你正在进行的是什么,最后退出执行。可以在这里 samples/quickstart/src/main/java/Quickstart.java 找到源码,也可以进行修改,记得修改后运行 mvn compile exec:java 即可。

Quickstart.java

Quickstart.java 中包含刚刚我们提到的所有内容(认证、授权等等),通过这个简单的示例可以让你轻松的熟悉Shiro的API。那么,让我们把Quickstart.java中的代码,一点一点剖析,这样便于理解它们的作用。 几乎所有的环境下,都可以通过这种方式获取当前用户:

  1. Subject currentUser = SecurityUtils.getSubject();

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

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

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

  1. Session session = currentUser.getSession();
  2. 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. if ( !currentUser.isAuthenticated() ) {
  2. //收集用户的主要信息和凭据,来自GUI中的特定的方式
  3. //如包含用户名/密码的HTML表格,X509证书,OpenID,等。
  4. //我们将使用用户名/密码的例子因为它是最常见的。.
  5. UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
  6. //支持'remember me' (无需配置,内建的!):
  7. token.setRememberMe(true);
  8. currentUser.login(token);
  9. }

就是这样,不能再简单了。

但如果登录失败了呢,你可以捕获所有异常然后按你期望的方式去处理:

  1. try {
  2. currentUser.login( token );
  3. //无异常,说明就是我们想要的!
  4. } catch ( UnknownAccountException uae ) {
  5. //username 不存在,给个错误提示?
  6. } catch ( IncorrectCredentialsException ice ) {
  7. //password 不匹配,再输入?
  8. } catch ( LockedAccountException lae ) {
  9. //账号锁住了,不能登入。给个提示?
  10. }
  11. ... 更多类型异常 ...
  12. } catch ( AuthenticationException ae ) {
  13. //未考虑到的问题 - 错误?
  14. }

这里有许多不同类别的异常你可以检测到,也可以抛出你自己异常。详见
AuthenticationException JavaDoc

小贴士:

最好的方式是将普通的失败信息反馈给用户,你总不会希望帮助黑客来攻击你的系统吧。

好,到现在为止,我们有了一个登录用户,接下来我们还可以做什么?

让我们显示他们是谁

  1. //打印主要信息 (本例子是 username):
  2. log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );

我们也可以判断他们是否拥有某个角色:

  1. if ( currentUser.hasRole( "schwartz" ) ) {
  2. log.info("May the Schwartz be with you!" );
  3. } else {
  4. log.info( "Hello, mere mortal." );
  5. }

我们也可以判断他们是否拥有某个特定动作或入口的权限:

  1. if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
  2. log.info("You may use a lightsaber ring. Use it wisely.");
  3. } else {
  4. log.info("Sorry, lightsaber rings are for schwartz masters only.");
  5. }

同样,我们还可以执行非常强大的 instance-level (实例级别)的权限检测,检测用户是否具备访问某个类型特定实例的权限:

  1. if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
  2. log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +
  3. "Here are the keys - have fun!");
  4. } else {
  5. log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
  6. }

轻而易举,是吧!

最后,当用记不再使用系统,可以退出登录:

  1. currentUser.logout(); //清楚识别信息,设置 session 失效.

这些就是使用 Apache Shiro 开发应用的核心了,当然,Apache Shiro已将将很多复杂的东西封装在内部了,但是现在它就是这么简单。

你会有疑问吧,用户登录时,谁负责把用户信息(用户名、密码、角色、权限等)取出来,还有运行时,谁负责安全认证呢?当然由你决定了啊。通过将一个实现了 Shiro 中的 Realm 的 Reaml 配置到 Shiro 中即可。

至于如何配置很大程度上取决于你的运行时环境,比如在单应用、web 应用、基于 Spring 或 JEE 容器的应用或者组合模式中使用 Shiro,配置都有所不同。如何配置已经超出 QuickStart 示例的范围,因为它的主要目的是帮助你熟悉 Shiro 的 API 和概念。

如果想进一步了解 Shiro,可以看看 Authentication Guide 和 Authorization Guide。也可以查看其他文档(特别是 用户指南),这里可以解决你的各种疑问。

感谢一路同行,希望你能喜欢使用 Apache Shiro。

译者注:本文参考:http://shiro.apache.org/10-minute-tutorial.html。如果对本中文翻译有疑议的或发现勘误欢迎指正,点此提问。