美文网首页Shiro我爱编程
Apache Shiro入门指南

Apache Shiro入门指南

作者: 倔强的小亮 | 来源:发表于2017-04-15 12:03 被阅读0次

    你的第一个Apache Shiro应用

    如果你是第一次接触Apache Shiro,这个简短的教程将向你展示如何创建并初始化一个非常简单的Apache Shiro应用。在这个过程中我们将讨论Shiro的核心概念,以此来帮助你熟悉Shiro的设计和API。

    如果你并不想编写这个教程接下来的代码,你可以通过下面两种方式得到一个简单例子:

    Setup

    在这个简单的例子中,我们将创建一个非常简单的命令行应用,它将运行并很快的退出,让你领略下Shiro的API。

    任何应用
    Apache Shiro从设计的第一天起就支持任何应用,从最小的命令行应用到最大的web集群应用。尽管在这个教程中我们创建了一个简单的app,但这些方式也在其他地方也同样适用。

    这个教程需要Java 1.5以上,我们也将会使用Apache Maven作为我们的构建工具,当然这并不是使用Apache Shiro所必须的。你也可以使用任何你喜欢的方式获得Shiro的jar包并将它们合并到你的应用中,例如使用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>
    

    class

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

    在包含pom.xml文件的目录中创建一个src/main/java的子目录,在src/main/java目录中创建Tutorial.java文件并输入下面内容:

    src/main/java/Tutoral.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部分,后面我们会讲到。现在,我们得到了一个典型的命令行应用,这个应用将会在控制台输出“My First Apache Shiro Application”然后退出。

    Test Run

    打开一个命令行窗口,切换到你的tutorial项目的根目录下(例如:shiro-tutorial)并执行下面代码:
    mvn compile exec:java
    你将看到我们的小应用运行起来并退出。你应该看到类似下面的内容:
    Run The Application
    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这个命令来查看我们每次添加代码后的结果。

    Enable Shiro

    在应用中使用Shiro,我们首先要明白的是在Shiro中的所有组件都和一个核心组件相关,这个组件就是SecurityManager。对于那些熟悉Java安全的人来说,这个是Shiro概念里的SecurityManager。它和java.lang.SecurityManager不是同一回事

    我们将会在Shiro架构章节详细讲述Shiro的设计细节,现在我们只要知道Shiro SecurityManager是所有使用Shiro的应用的核心,并且每个应用都需要一个SecurityManager就已经足够了。因此,第一件事就是我们必须在我们的应用中获取一个SecurityManager实例。

    配置

    虽然我们可以直接实例化一个SecurityManager类,但是Shiro的SecurityManager有很多的配置选项和内部组件,使得用Java源代码来配置非常痛苦。更容易和灵活的方式是通过基于文本的配置。

    为实现这个目标,Shiro提供了一个默认“common denominator”来实现基于文本 INI 的配置。人们已经厌倦了使用笨重的XML文件,在加上INI文件容易阅读,易于使用,并且依赖较少。你将看到一个简单的对象图,可以有效的使用INI配置简单对象图,就像SecurityManager

    配置选项
    Shiro的SecurityManager实现和所有支持的组件都和JavaBeans兼容。事实上任何格式的配置文件都可以配置Shiro的SecurityManager,例如XML(Spring,JBoss,Guice等)、YAML、JSON、Groovy Builder markup等其他格式。INI是Shiro“common denominator”的格式,允许在任何环境中使用以防止其他的格式不可用。

    shiro.ini
    因此,我们将使用一个INI文件来配置我们这个简单应用的Shiro SecurityManager。首先,在pom.xml所在目录创建src/main/resources子目录。然后,在刚才创建的目录里面创建一个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实例,按照下面的代码修改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集成到我们的例子应用中,是不是很简单?

    现在在命令行执行mvn compile exec:java,你将会看到所有代码都运行成功(由于Shiro的日志级别为debug或更低,因此你不会看到任何Shiro的日志信息。如果运行没有出现任何错误信息,那么就说明一起都正常)。

    上面添加的代码做了下面几件事情:

    • 我们使用Shiro的IniSecurityManagerFactory来实现对classpath根目录下的shiro.ini文件的提取。这是通过工厂模式来实现的。classpath:前缀是一个资源指示器,它告诉Shiro到什么地方去找ini文件(还有一些其他前缀,比如url:file:都支持的很好)
    • 调用factory.getInstance()方法,将解析INI文件并返回一个``SecuriManager`实例。
    • 在这个例子中,我们将SecurityManager设置为单例,在JVM范围内访问。

    使用Shiro

    现在我们的SecurityManager已经设置好并且可以运行了,现在我们可以添加一些安全相关的操作了。
    在思考应用安全性的时候,我们可能最关心的事情是“当前用户是谁?”或者“当前用户是否允许做X?”,我们在写代码或者设计用户接口的时候通常会问这些问题。通常构建应用程序是基于用户故事,和你基于用户需求想要的功能。因此,思考应用安全问题最自然的方式便是基于当前用户。在Shiro API里面使用Subject这个概念来代表当前用户。

    几乎在所有环境下,你可以使用通过下面的代码获取当前用户:
    Subject currentUser = SecurityUtils.getSubject();
    使用SecurityUtils.getSubject()我们可以得到当前执行的得SubjectSubject是一个安全术语,意思是当前执行用户的一个特定安全视图(原文使用security-specific view)。我们不称之为“用户”是因为“用户”这个词通常代表一个人类。在安全的世界里,“Subject”可以代表一个真实的人,也可以代表一个第三方处理、cron任务、守护进程或其他类似的东西,简单来说就是和当前软件系统交互的主体。在大多数情况下你可以认为Subject就是Shiro的“用户”概念。

    在独立应用中调用getSubject()可以从指定位置的用户数据返回一个Subject,在服务器环境下(例如 web app),将基于当前线程或收到的请求返回一个Subject

    现在你已经有了一个Subject,通过它可以做什么呢?

    如果你想让用户在当前会话中使用该应用程序,你可以得到他们的session:

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

    Session是一个特定的Shiro实例,它不仅代表最常用的普通HttpSesstion,而且还带有一些额外的东西,其中最大的区别是:Shiro Session并不需要HTTP环境!

    如果在web应用中使用,默认情况下Session就是HttpSession。但是在非web环境下,例如在我们教程中创建的这个应用里,Shiro将自动默认使用企业级会话管理。也就是说在你的应用中你可以使用同一套API而不用关心你应用的发布环境。这就为那些需要使用会话却不想强制使用HttpSession或则EJB会话的应用打开了一个新的世界。而且,客户端还可以分享会话数据。

    现在你可以获得一个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?
    }
    

    你还可以检查其他不同的异常类型,或则抛出你自定义的异常类型。更多异常类型请查看文档

    现在我们已经有了一个登陆用户,我们还可以做些什么呢?
    我们来打印出他们是谁:

    //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类

    Final src/main/java/Turorial

    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);
        }
    }
    

    相关文章

      网友评论

        本文标题:Apache Shiro入门指南

        本文链接:https://www.haomeiwen.com/subject/yondattx.html