lee 1030 0 2017-03-21 22:49:39

文章位置:左搜> 编程技术> 正文

您的第一个Apache Shiro应用程序

如果你是Apache Shiro的新手,这个简短的教程将告诉你如何设置一个由Apache Shiro保护的初始和非常简单的应用程序。我们将讨论Shiro的核心概念,以帮助您熟悉Shiro的设计和API。

如果您不想按照本教程实际编辑文件,则可以获取几乎相同的示例应用程序,并在您参考时进行参考。选择地点:

  • 在Apache Shiro的Git存储库中:https://github.com/apache/shiro/tree/master/samples/quickstart
  • 在Apache Shiro的源代码发布的samples/quickstart目录中。源分发可从下载页面获取。

建立

在这个简单的例子中,我们将创建一个非常简单的命令行应用程序,它将运行并快速退出,这样您可以感受到Shiro的API。

 任何应用程序

Apache Shiro是从第一天开始设计的,以支持任何应用程序 - 从最小的命令行应用程序到最大的集群Web应用程序。即使我们为本教程创建了一个简单的应用程序,也知道无论应用程序如何创建或部署在其中,都会使用相同的使用模式。

本教程需要Java 1.5或更高版本。我们还将使用Apache Maven作为我们的构建工具,但是当然这不需要使用Apache Shiro。您可以购买Shiro的.jars并以任何你喜欢的方式将它们合并到应用程序中,例如使用Apache Ant和Ivy。

对于本教程,请确保您使用的是Maven 2.2.1或更高版本。您应该能够mvn --version在命令提示符中键入,并看到类似于以下内容:

测试Maven安装

hazlewood:~/shiro-tutorial$ mvn --version
Apache Maven 2.2.1 (r801777; 2009-08-06 12:16:01-0700)
Java version: 1.6.0_24
Java home: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
Default locale: en_US, platform encoding: MacRoman
OS name: "mac os x" version: "10.6.7" arch: "x86_64" Family: "mac"

现在,在文件系统上创建一个新目录,例如,shiro-tutorial并将以下Maven pom.xml文件保存在该目录中:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.shiro.tutorials</groupId>
    <artifactId>shiro-tutorial</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <name>First Apache Shiro Application</name>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>

        <!-- This plugin is only to test run our little application.  It is not
             needed in most Shiro-enabled applications: -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <classpathScope>test</classpathScope>
                    <mainClass>Tutorial</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.1.0</version>
        </dependency>
        <!-- Shiro uses SLF4J for logging.  We'll use the 'simple' binding
             in this example app.  See http://www.slf4j.org for more info. -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Tutorial类

我们将运行一个简单的命令行应用程序,因此我们需要使用一个public static void main(String[] args)方法创建一个Java类。

在包含您的pom.xml文件的同一目录中,创建一个* src/main/java子目录。在src/main/java创建一个Tutorial.java包含以下内容的文件:

src / main / java / Tutorial.java

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Tutorial {

    private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);

    public static void main(String[] args) {
        log.info("My First Apache Shiro Application");
        System.exit(0);
    }
}

不要担心import语句现在 - 我们会很快得到他们。但是现在,我们有一个典型的命令行程序“shell”。所有这个程序都会打印出文字“我的第一个Apache Shiro应用程序”并退出。

测试运行

要尝试我们的Tutorial应用程序,请在教程项目的根目录(例如shiro-tutorial)中的命令提示符中执行以下命令,然后键入以下内容:

mvn compile exec:java

你会看到我们的小教程'应用程序'运行和退出。您应该看到类似以下内容(注意粗体文本,表示我们的输出):

运行应用程序

lhazlewood:~/projects/shiro-tutorial$ mvn compile exec:java

... a bunch of Maven output ...

1 [Tutorial.main()] INFO Tutorial - My First Apache Shiro Application
lhazlewood:~/projects/shiro-tutorial\$

我们已经验证应用程序运行成功 - 现在让我们启用Apache Shiro。当我们继续教程,你可以运行mvn compile exec:java后,每次我们添加一些更多的代码,以查看我们的更改的结果。

启用S​​hiro

在应用程序中启用Shiro时,首先要了解的是,Shiro中的几乎所有内容都与称为的中央/核心组件相关SecurityManager。对于那些熟悉Java安全性的人,这是Shiro的一个SecurityManager的概念 - 它不是一样的东西java.lang.SecurityManager

虽然我们将在“ 架构”章节中详细介绍Shiro的设计,但现在已经足够了解Shiro SecurityManager是应用程序的Shiro环境的核心,而且每个应用程序SecurityManager都必须存在。因此,我们在Tutorial应用程序中必须做的第一件事是设置SecurityManager实例。

组态

虽然我们可以SecurityManager直接实例化一个类,但是Shiro的SecurityManager实现有足够的配置选项和内部组件,这使得在Java源代码中做这件事变得很困难 - SecurityManager使用灵活的基于文本的配置格式配置更容易。

为此,Shiro通过基于文本的INI配置提供了默认的“公分母”解决方案。人们对这些天使用庞大的XML文件感到厌烦,INI很容易阅读,使用简单,而且只需要非常少的依赖。稍后还会看到,通过对对象图形导航的简单理解,INI可以有效地用于配置简单对象图形,如SecurityManager。

 许多配置选项

Shiro的SecurityManager实现和所有支持组件都是JavaBeans兼容的。这允许Shiro几乎配置任何配置格式,如XML(Spring,JBoss,Guice等),YAML,JSON,Groovy Builder标记等。INI只是Shiro的“公分母”格式,允许在任何环境中进行配置,以防其他选项不可用。

 
shiro.ini

因此,我们将使用一个INI文件来SecurityManager为这个简单的应用程序配置Shiro 。首先,创建一个src/main/resources目录,从同一目录开始,其中pom.xml。然后shiro.ini在该新目录中创建具有以下内容的文件:

src / main / resources / shiro.ini

# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

正如你所看到的,这个配置基本上设置了一组小的静态用户帐户,足够用于我们的第一个应用程序。在后面的章节中,您将了解如何使用更复杂的用户数据源,如关系数据库,LDAP和ActiveDirectory等。

引用配置

现在我们已经定义了一个INI文件,我们可以SecurityManager在我们的Tutorial应用程序类中创建该实例。更改main方法以反映以下更新:

public static void main(String[] args) {

    log.info("My First Apache Shiro Application");

    //1.
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

    //2.
    SecurityManager securityManager = factory.getInstance();

    //3.
    SecurityUtils.setSecurityManager(securityManager);

    System.exit(0);
}

我们去 - Shiro在我们的示例应用程序中启用,只添加了3行代码!这是多么容易?

随意运行mvn compile exec:java,看到一切仍然运行成功(由于Shiro的默认日志记录调试或更低,你不会看到任何Shiro日志消息 - 如果它启动和运行没有错误,那么你知道一切仍然确定)。

这里是上面添加的是做什么:

  1. 我们使用Shiro的IniSecurityManagerFactory实现来获取shiro.ini位于类路径根目录的文件。这个实现反映了Shiro对工厂方法设计模式的支持。该classpath:前缀是一个资源的指标,告诉四郎在哪里加载从ini文件(其它前缀,如url:file:以及支持)。

  2. factory.getInstance()方法被调用,它解析INI文件并返回一个SecurityManager反映配置的实例。

  3. 在这个简单的示例中,我们将其设置SecurityManager静态(内存)单例,可通过JVM访问。但请注意,如果您在单个JVM中将有多个启用Shiro的应用程序,这是不可取的。对于这个简单的例子,它是确定,但更复杂的应用程序环境通常会放置SecurityManager在应用程序特定的内存(如在Web应用程序ServletContext或Spring,Guice或JBoss DI容器实例)。

使用Shiro

现在我们的SecurityManager已经设置好了,现在我们可以开始做我们真正关心的事情 - 执行安全操作。

当保护我们的应用程序时,我们可能最相关的问题是“当前用户是谁”或“当前用户是否允许做X”?在我们编写代码或设计用户界面时,常常会提出这些问题:应用程序通常基于用户故事构建,并且您希望基于每个用户表示(和安全)的功能。因此,我们在应用程序中考虑安全性的最自然的方式是基于当前用户。Shiro的API从根本上代表了“当前用户”的Subject概念。

在几乎所有环境中,您可以通过以下调用获取当前正在执行的用户:

Subject currentUser = SecurityUtils.getSubject();

使用SecurityUtils。getSubject(),我们可以获取当前正在执行的Subject主题是一个安全术语,基本上意味着“当前正在执行的用户的安全特定视图”。它不被称为“用户”,因为“用户”这个词通常与一个人相关联。在安全世界中,术语“主题”可以指人,也可以是第三方进程,cron作业,守护进程帐户或任何类似的。它只是指“当前与软件交互的东西”。对于大多数意图和目的,你可以认为Subject是Shiro的'用户'概念。

独立应用程序中的getSubject()调用可以Subject基于应用程序特定位置中的用户数据返回,并且在服务器环境(例如web应用程序)中,它Subject基于与当前线程或传入请求相关联的用户数据来获取。

现在你有了Subject,你能用它做什么?

如果您想在用户在应用程序的当前会话期间使用户可用,您可以获取其会话:

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

Session是一个Shiro特定的实例,提供了大多数你习惯了与常规HttpSessions,但有一些额外的好处和一个巨大的区别:它不需要HTTP环境!

如果在Web应用程序中部署,默认情况下SessionHttpSession基于。但是,在非Web环境中,像这个简单的教程应用程序,Shiro将默认自动使用其企业会话管理。这意味着您可以在任何层次的应用程序中使用相同的API,而不考虑部署环境!这打开了一个全新的应用程序世界,因为任何需要会话的应用程序不需要被强制使用HttpSession或EJB有状态会话Bean。而且,任何客户端技术现在都可以共享会话数据。

所以现在你可以获得一个Subject和他们Session。怎么样真正像检查,如果他们被允许做的事情,比如对角色和权限检查有用的东西?

好吧,我们只能为已知用户执行这些检查。我们的Subject上面的实例代表当前用户,但是当前用户?好吧,他们是匿名的 - 也就是说,直到他们至少登录一次。所以,让我们这样做:

if ( !currentUser.isAuthenticated() ) {
    //collect user principals and credentials in a gui specific manner
    //such as username/password html form, X509 certificate, OpenID, etc.
    //We'll use the username/password example here since it is the most common.
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");

    //this is all you have to do to support 'remember me' (no config - built in!):
    token.setRememberMe(true);

    currentUser.login(token);
}

而已!这不容易。

但是,如果他们的登录尝试失败怎么办?您可以捕获各种特定的异常,告诉您发生了什么,并允许您处理并做出相应的反应:

try {
    currentUser.login( token );
    //if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
    //username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
    //password didn't match, try again?
} catch ( LockedAccountException lae ) {
    //account for that username is locked - can't login.  Show them a message?
}
    ... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
    //unexpected condition - error?
}

有很多不同类型的异常,你可以检查,或抛出自己的自定义条件Shiro可能不考虑。有关更多信息,请参阅AuthenticationException JavaDoc。

 方便的提示

安全最佳做法是向用户提供通用登录失败消息,因为您不想帮助攻击者试图进入您的系统。

好的,所以到现在为止,我们有一个登录用户。我们还能做什么?

让我们说他们是谁:

//print their identifying principal (in this case, a username): 
log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );

我们还可以测试他们是否有特定的作用:

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

我们还可以看到他们是否有权对某种类型的实体采取行动:

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

此外,我们可以执行非常强大的实例级权限检查 - 查看用户是否能够访问类型的特定实例:

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

蛋糕,对吧?

最后,当用户完成使用应用程序时,他们可以注销:

currentUser.logout(); //removes all identifying information and invalidates their session too.

最终教程类

在添加上面的代码示例之后,这里是我们最终的Tutorial类文件。随意编辑和玩它,并更改安全检查(和INI配置),你喜欢:

最终src / main / java / Tutorial.java

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Tutorial {

    private static final transient Logger log = LoggerFactory.getLogger(Tutorial.class);

    public static void main(String[] args) {
        log.info("My First Apache Shiro Application");

        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

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

        // Do some stuff with a Session (no need for a web or EJB container!!!)
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("Retrieved the correct value! [" + value + "]");
        }

        // 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?
            }
        }

        //say who they are:
        //print their identifying principal (in this case, a username):
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        //test a role:
        if (currentUser.hasRole("schwartz")) {
            log.info("May the Schwartz be with you!");
        } else {
            log.info("Hello, mere mortal.");
        }

        //test a typed permission (not instance-level)
        if (currentUser.isPermitted("lightsaber:weild")) {
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        //a (very powerful) Instance Level permission:
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        //all done - log out!
        currentUser.logout();

        System.exit(0);
    }
}

概要

希望这个介绍教程帮助你了解如何设置Shiro在一个基本的应用程序,以及Shiro的主要设计概念,SubjectSecurityManager

但这是一个相当简单的应用程序。你可能会问自己,“如果我不想使用INI用户帐户而是想连接到更复杂的用户数据源怎么办?

要回答这个问题,需要更深入地了解Shiro的架构和支持配置机制。接下来我们将介绍Shiro的建筑。

原文地址:http://shiro.apache.org/tutorial.html