美文网首页
shiro1.2_入门

shiro1.2_入门

作者: o______o | 来源:发表于2020-03-15 12:43 被阅读0次
1

Apache Shiro 示例

你的第一个Apache Shiro 应用

如果你是第一次访问Apache Shiro,本简易教程将为你展示如何使用Apache
Shiro设置一个基本且简单的安全应用.我们将通过讨论Shiro的核心概念的方式来帮助你熟悉Shiro的设计和API.
如果你不想在学习的时候编辑代码,你可以获得一个基本相同的样例程序并在学习时参阅,下面是示例程序的不同下载方式:

设置

在本简单样例中,我们将会创建一个非常简单的命令行程序,你可以非常快的运行和退出,以此来感受Shiro的API.

任何应用

Apache Shiro从第一天的设计开始就支持任意应用,从最简单的命令行程序到大型集群web应用,即使如此我们依旧通过一个简单的app来作为开始的教程,以此来让你了解一件事,你的程序无论被创建或被部署到什么地方,这些应用都采用相同的模式来使用Shiro提高安全性.

3
本教程需要Java1.6或更加高级的版本,同时我们也会使用Apache Maven来作为我们的项目构建工具.但这对应使用Apache Shiro来说并不是必须的,你可以获取Apache shiro的Jar包文件并可以用任意方式集成到你的程序中.比如还有Apache的 AntIvy.
对于本次演示,请保证你的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"
4
现在,创建一个目录,在你的文件系统中,比如名字叫做shiro-tutorial并保存下面的Maven的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>3.8.0</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>

        <!-- 此插件只在测试运行我们的小应用,它在大多数需要Shiro的应用中是永不到的 -->
            <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.4.1</version>
        </dependency>
        <!-- Shiro使用SLF4J来记录日志,我们将在此应用中使用简单绑定,具体详见: http://www.slf4j.org  -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.21</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
5
我们将会运行一个简单的命令行程序,因此我们需要创建一个带有
public static void main(String[] args)主方法的Java类.先找到你的pom.xml文件,在同一目录下创建一个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);
    }
}
6

先不要担心引入的声明,我们马上就会用到他们.但现在我们可以通过命令行来运行此程序,(说明:Linux下shell,在windows下是cmd),文件中所有的代码程序只会做一件事,那就是输出文本"My First Apache Shiro Application"并退出.


7

运行测试

为了尝试运行我们的示例程序,需要在你的示例项目的根目录下(比如 shiro-tutorial目录下)执行下面的命令:
mvn compile exec:java
随后你会看到我们的小示例应用的运行和结束.你应该会看到一些类似下面的内容(红色加粗代表我们的输出):
运行程序

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

... 一系列的 Maven 输出 ...

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

我们确定此程序运行可以成功,现在让我们开始使用Apache Shiro.在我们继续此示例程序的同时,当我们添加了代码后,你可以在在任意时刻运行
mvn compile exec:java
命令来查看不同的运行结果.

8
启用Shiro
在一个应用中启动Shiro,你首先需要理解的是,几乎在Shiro中所有的内容都依赖于被我们称为SecurityManager的核心组件.对于熟悉Java安全的人,你们要注意这个组件和java.lang.SecurityManager不是一个东西.
当然,我们也会在体系结构那一章回顾Shiro的设计细节,早点了解是有好处的,Shiro的Security Manager是一个应用中Shiro环境的核心,而且一个SecurityManager必须放入一个应用,因此,我们在示例应用中要做的第一件事就是设置SecurityManager的实例.
9

配置

在我们可以直接创建一个SecurityManager类的时候,Shiro的SecurityManager实现类拥有足够多的配置选项和内部组件以至于会让你在使用Java代码编程的时候感到些许痛苦.不要担心,只要将SecurityManager的配置代码换成灵活的,基于文本的配置,此过程将变得异常简单。
为了实现那个目标,Shiro提供了一种通用的解决方法,一种基于 INI 格式的文档文件的配置方案。近些时间,人们开始不喜欢笨重的XML配置文件,反观INI文件,读起来容易,用着也简单,而且也只需要很少的依赖。一会儿你就能看到一个简单的易于理解的对象引导,通过INI文件可以被用来高效的配置简单的对象,就像使用SecurityManager一样。


10

多种配置选项

Shiro的实现类和所有支持的组件全部兼容JavaBean,这允许Shiro可以兼容几乎任何类型的配置文件比如XML(Spring,JBoss,Guice,等等),YAML,JSON,Groovy Builder markup,还有其他,INI文件只是Shiro的常用配置文件,且可以保证在任意环境下都可以使用.

11
shiro.ini
于是我们可以使用INI文件来为这个小程序配置SecurityManager,首先在与pom.xml所在目录相同的目录下创建目录src/main/resources,然后在新文件夹下创建shiro.ini文件,文件的内容如下:
src/main/resources/shiro.ini
# =============================================================================
# 示例INI配置
#
# 用户名和密码基于经典的 Mel Brooks' film "Spaceballs" :)
# =============================================================================

# -----------------------------------------------------------------------------
# 用户和他们的(可选的)角色
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# 角色和他们关联的权限
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
12

正如你所看到的,这个配置文件基本上设置了一个很小的静态的用户账户集合,已经足够我们的第一个小程序来使用了.在接下来的章节中,你将会看到我们如何使用更加灵活的数据源,比如关系数据库,LDAP和ActiveDirectory等.


13

引用配置文件

现在我们有了定义好的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);
}
14
现在好了,Shiro可以使用了,仅仅添加了3行代码,是不是很简单?
现在运行mvn compile exec:java然后查看一切运行正常(因为Shiro的默认为调试日志输出或更低级别的日志模式,你将不会看到任何Shiro日志信息,假如在运行后没有任何错误,就代表一切正常).
下面讲解上述添加内容的功能介绍:
  1. 我们使用Shiro的IniSecurityManagerFactory实现来获取我们的shiro.ini文件(位于classpath的根目录下).此实现反映了Shiro对于工厂方法设计模式的支持.classpath:前缀是一个资源标识符表名shiro从何处加载ini文件内容(其他前缀,比如url:file:也同样支持)
  2. 调用factory.getInstance()方法来解析INI文件并返回一个根据文件配置完成的SecurityManager实例.
  3. 在此简单案例中,我们设置SecurityManager为一个静态单例方法,通过JVM可自动获取.要注意到如果有多个Shiro配置在一个应用中此时就无法获取到实例.在此简单案例中,他是可以获取的,但更加复杂的应用环境通常将替换SecurityManager在特定应用内存(比如网页应用的ServletContext或者Spring,Guice或者JBoss DI容器实例).
    15

使用Shiro

现在我们的SecurityManager配置完成并可以使用了.我们可以开始做一些我们真正关心的执行安全选项.
当要保护我们的应用时,获取最需要问的问题就是谁是当前用户?或者当前用户被允许做X操作了吗?这些问题经常在我们写代码或者设计用户接口时被提到,应用程序通常是基于用户信息构建的,你希望基于每个用户来表示(和保护)调用的功能.因此,我们考虑应用程序安全性的最自然的方法是基于当前用户.Shiro的API使用Subject从根本上代表了"当前用户"的概念.
在几乎所有环境中,你可以通过如下调用获取当前正在访问的用户变量:

Subject currentUser = SecurityUtils.getSubject();
16
使用 SecurityUtils.getSubject()方法,我们可以得到当前访问的Subject对象.Subject是一个安全术语,通常意味着"当前执行用户的特定安全视图".它并不特指用户,因为用户这个词通常代表一个人.而在安全领域,Subject可以代表人同时也可以代表任何第三方进程,定时任务,守护进程或任何类似的内容,它仅仅代表和当前软件交互的东西,大多数情况下可以认为Subject就是当前用户.
getSubject()方法是独立程序调用会返回一个Subject对象基于用户数据,在一个特定的应用环境下,或者在一个服务器环境下(比如web app),它需要基于用户数据的Subject关联当前进程或访问的请求.
现在你拥有了一个Subject对象,你可以用它做什么呢?
如果你想在应用的当前session下设置或获取某些变量,你可以按照如下方式获取session:
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
17
这个Session(一次会话)是一个Shiro特定环境下的实例,它可以提供大多数你需要的在HttpSessions中的功能和数据,此外还有一个不同点就是它不需要HTTP环境!
如果Shiro部署在一个web应用中,默认Session会基于HttpSession.但是在非web环境,比如当前的简单教程应用,Shiro将自动使用它的企业级Session管理来自动生成Session.这代表以你可以使用同样的API在你的应用中,在任何层面,且与部署环境无关,它将打开应用的新世界,任何需要Session的应用都不再被强制使用HttpSessionEJB Stateful Session对象,而且现在任何客户端技术都可以共享session数据.
所以现在你可以获得一个Subject和他们的会话.那些真正有用的东西呢?比如检查是否允许他们做一些事情,比如检查角色和权限?
当然,我们可以只检查它们是否是已知用户,上述的Subject实例代表了当前用户,但谁是当前用户?现在我们还是匿名状态,直到登录至少一次,于是,我们这样做:
if ( !currentUser.isAuthenticated() ) {
    // 以特定GUI的方式收集用户 主体 和 凭据
    // 比如html格式的用户名和密码, X509认证, OpenID, 等.
    // 我们在此使用最常用的用户名/密码
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    // 你只需要如下操作就可以实现记住我的功能(无需配置,内建功能!):
    token.setRememberMe(true);
    // 登录
    currentUser.login(token);
}
18

完成了,不能再简单了!
但如果他们登录失败要如何处理?你可以捕捉所有有序的特定异常,这些异常将告诉你实际发生了什么,并允许你做出对应的处理和反应.

try {
    currentUser.login( token );
    // 如果没有异常,就到此结束
} catch ( UnknownAccountException uae ) {
    // 用户名不存在
} catch ( IncorrectCredentialsException ice ) {
    // 密码不匹配
} catch ( LockedAccountException lae ) {
    // 账户被锁定,无法登陆
}
    ... 更多类型异常检查...
} catch ( AuthenticationException ae ) {
    // 无法处理的异常
}
19
有很多你可以检查的异常类型,还可以自定义客户端异常类型假如Shiro里面的都不符合要求,可以在授权异常章节看到更多内容.
处理技巧

安全性最好的处理方法是给一个登陆失败提示,而不是具体的细节提示,因为你也不想帮助黑客黑进你的系统里面吧.
好的,到现在为止,我们已经有一个已经登陆的用户,之后要怎么做?
记录我是谁:

// 打印他们的用户主体,这里代表用户名
log.info( "用户 [" + currentUser.getPrincipal() + "] 登陆成功." );
20

我们可以测试看他们是否拥有某个角色:

if ( currentUser.hasRole( "schwartz" ) ) {
    log.info("希望schwartz和你同在!" );
} else {
    log.info( "你好,普通人." );
}
21

我们同样可以检查他们是否拥有某个特定实体或类型的一个权限.

if ( currentUser.isPermitted( "lightsaber:wield" ) ) {
    log.info("你可以用光剑戒指。明智地使用它.");
} else {
    log.info("对不起,光剑戒指只为schwartz主人服务.");
}
22

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

if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
    log.info("你被允许驾驶winnebago车型eagle5.这是钥匙,玩的开心!");
} else {
    log.info("对不起,你没有权限开 'eagle5' 房车!");
}
23

如此简单,不是吗?
最后我们的用户操作完成后他们可以如下退出:

currentUser.logout(); // 移除所有用户信息和已验证的用户关联的session等.
24
最终示例类

在上面的代码示例添加结束后,下面是我们最终示例代码.自由的编辑和玩吧,修改安全检查或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("我的第一个Apache Shiro应用");

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

        // 获取当前访问的用户对象
        Subject currentUser = SecurityUtils.getSubject();

        // 使用Session做些事,不需要web或JEB环境
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("恢复为正确的值! [" + value + "]");
        }

        // 让我们登录看看当前用户有哪些角色和权限
        if (!currentUser.isAuthenticated()) {
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            token.setRememberMe(true);
            try {
                currentUser.login(token);
            } catch (UnknownAccountException uae) {
                log.info("根本就没有用户名: " + token.getPrincipal());
            } catch (IncorrectCredentialsException ice) {
                log.info("账户密码 " + token.getPrincipal() + " 不正确!");
            } catch (LockedAccountException lae) {
                log.info("当前账户 " + token.getPrincipal() + "被锁定.  " +
                        "请联系管理员解锁账户.");
            }
            // ... 抓取更多异常 (也许这里可以自定义你的应用)
            catch (AuthenticationException ae) {
                // 无法处理的情况,错误?
            }
        }

        // 告诉我是谁:
        // 打印他们的认证主体,默认是用户名
        log.info("用户 [" + currentUser.getPrincipal() + "] 登录成功.");

        // 测试角色:
        if (currentUser.hasRole("schwartz")) {
            log.info("Schwartz一直与你同在!");
        } else {
            log.info("你好,普通人");
        }

        // 测试权限类型,非实例类型
        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("你可以使用光明戒指. 明智的使用.");
        } else {
            log.info("对不起,光明戒指只为schwartz主人服务.");
        }

        // 一个强大的实例级权限鉴定
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("你被允许驾驶winnebago车型eagle5.这是钥匙,玩的开心!");
        } else {
            log.info("对不起,你没有权限开 'eagle5' 房车!");
        }

        // 当所有操作都结束时,记得退出!
        currentUser.logout();

        System.exit(0);
    }
}
25

总结

希望这个介绍示例帮助你理解如何设置Shiro于一个基本的应用中,就像Shiro的主要设计概念,SubjectSecurityManager..
但这只是一个极其简单的应用.你或许会问自己,如果我不想使用INI来配置用户使用,取而代之的是连接一个更加灵活的数据源要怎么做?
为了回答此问题需要更深的理解Shiro的结构和配置机制,我们将会在下一节讲解Shiro的配置.

相关文章

网友评论

      本文标题:shiro1.2_入门

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