Apache Shiro系列五:快速开始

如果你对Apache Shiro还不太熟悉,这篇文章将向你展示如何开发一个由Apache Shiro保护的非常简单的应用程序。在此过程中,我们将讨论Shiro的核心概念,以帮助你熟悉Shiro的设计和API。
在阅读该文前,建议先了解Shiro的架构和安全相关术语,以便更好的理解代码。
本文代码参考Apache Shiro源码(版本2.0.0)中的 samples/quickstart/src/main/java/Quickstart.java 文件,可以根据需要下载

环境要求

  • JDK 11+(查看版本:java -version
  • Maven 3.6.3+(查看版本:mvn --version

实战

项目结构

在这里插入图片描述

基础配置

pom文件

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 https://maven.apache.org/maven-v4_0_0.xsd">

    <groupId>org.apache.shiro.samples</groupId>
    <version>4.0.0</version>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>quickstart</artifactId>
    <name>Apache Shiro :: Samples :: Quick Start</name>
    <packaging>jar</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.11</source>
                    <target>1.11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!-- configure logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>2.0.12</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j2-impl</artifactId>
            <version>2.22.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.22.1</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

日志配置

log4j2.xml

<Configuration name="ConfigTest" status="ERROR" monitorInterval="5">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="org.springframework" level="warn" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="org.apache" level="warn" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="net.sf.ehcache" level="warn" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Logger name="org.apache.shiro.util.ThreadContext" level="warn" additivity="false">
            <AppenderRef ref="Console"/>
        </Logger>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

使用shiro

在应用程序中使用Shiro,首先要理解的是,Shiro中几乎所有的内容都与核心组件SecurityManager相关。需要注意,它与java.lang.SecurityManager不是同一个东西。
正如我们在架构中所说,Shiro的SecurityManager是应用程序Shiro环境的核心,并且每个应用程序必须要有一个SecurityManager。因此,我们首先要做的就是设置SecurityManager实例。

初始化SecurityManager

尽管我们可以直接在Java源代码中实例化SecurityManager类,但Shiro中SecurityManager的实现包含很多配置选项和内部组件,这使得这样做变得繁琐。而使用基于文本的格式来配置SecurityManager会更加容易,也更加灵活。
为此,Shiro通过基于文本的INI配置文件提供了一种默认的“通用解决方案”。如今,人们已经厌倦了使用庞大的XML文件,而INI格式易读、易用,并且几乎不需要依赖。
由于Shiro中SecurityManager 的实现和所有支持组件都是与 JavaBeans 兼容的,因此Shiro几乎可以使用任何配置文件格式进行配置,如XMLYAMLJSONGroovy Builder标记语言等。INI只是 Shiro 的“通用解决方案”格式,任何环境下,其他选项均不可用的情况下,可使用该格式进行配置。
在本项目中,我们使用shiro.ini文件来对SecurityManager进行配置。

shiro.ini
# -----------------------------------------------------------------------------
# 用户及其分配的角色
#
# 每行都符合 org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions 的 JavaDoc 中定义的格式。
#
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
#  用户 = 'root' 密码 = 'secret' 角色 = {'admin'}
root = secret, admin
# 用户 = 'guest' 密码 = 'guest' 角色 = {'guest'}
guest = guest, guest
# 用户 = 'presidentskroob' 密码 = '12345' 角色 = {'president'}
presidentskroob = 12345, president
# 用户 = 'darkhelmet' 密码 = 'ludicrousspeed' 角色 = {'darklord', 'schwartz'}
darkhelmet = ludicrousspeed, darklord, schwartz
# 用户 = 'lonestarr' 密码 = 'vespa' 角色 = {'goodguy', 'schwartz'}
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# 角色及其分配的权限
# 
# 每行都符合 org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions 的 JavaDoc 中定义的格式。
#
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]
# 角色 'admin' 拥有所有权限,用通配符 '*' 表示
admin = *
# 角色 'schwartz' 拥有名称前缀为 'lightsaber: '的所有权限
schwartz = lightsaber:*
# “goodguy”角色可以“drive”(操作)车牌为“eagle5”(实例特定id)的winnebago(类型)
goodguy = winnebago:drive:eagle5

编写安全代码

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

public class Quickstart {

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

    public static void main(String[] args) {

        /**
         * 1、实例化SecurityManager
         * classpath: 资源指示符的前缀,它告诉Shiro从哪里加载ini文件(也支持其他前缀,如‘url:’和 ‘file:’)。
         * */
        SecurityManager securityManager = new BasicIniEnvironment("classpath:shiro.ini").getSecurityManager();

        /**
         * 2、全局设置SecurityManager(非必须)
         * 这个简单的快速入门示例,将SecurityManager作为JVM单例进行访问。
         * 大多数应用程序不会这样做,而是依赖于它们的容器配置或web.xml(Web应用程序)。
         * */
        SecurityUtils.setSecurityManager(securityManager);

        /**
         * 3、获取当前用户
         * 在保护我们的应用程序时,可能我们最常问自己的问题是“当前用户是谁?”或“当前用户是否允许执行该操作?”,因此,对我们来说,在应用程序中考虑安全的最自然的方式就是基于当前用户。
         * Shiro的API基本上使用其Subject来表示“当前用户”。
         * */
        Subject currentUser = SecurityUtils.getSubject();

        /**
         * 4、获取Session
         * Session是Shiro特有的实例,它提供了您所熟悉的常规HttpSession的大部分功能,但有一些额外的优点和一个很大的不同:它不需要HTTP环境!
         * */
        Session session = currentUser.getSession();
        session.setAttribute("someKey", "aValue");
        String value = (String) session.getAttribute("someKey");
        if (value.equals("aValue")) {
            log.info("获取到正确的值! [" + value + "]");
        }

        /**
         * 5、登录
         * 上面的Subject实例代表了当前用户,但谁是当前用户呢?他们是匿名的,直到他们至少登录一次为止。
         * */
        if (!currentUser.isAuthenticated()) {
            //可以以GUI的方式收集用户身份和凭证,如HTML的用户名/密码表单、X509证书、OpenID等。这里使用用户名/密码的示例,因为这是最常见的方式。
            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) {
                //意外情况?  error?
            }
        }

        /**
         * 6、登录后,用户角色、权限等操作
         * */
        log.info("用户 [" + currentUser.getPrincipal() + "] 成功登录.");

        if (currentUser.hasRole("schwartz")) {
            log.info("你的角色是 Schwartz!");
        } else {
            log.info("你不是 schwartz 角色.");
        }

        if (currentUser.isPermitted("lightsaber:wield")) {
            log.info("你有 lightsaber:wield 的权限");
        } else {
            log.info("你没有 lightsaber:wield 的权限");
        }

        //实例级权限:
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            log.info("你允许‘drive’车牌号为(id)'eagle5'的winnebago。这是钥匙——玩得开心点!");
        } else {
            log.info("你未被允许驾驶车牌号为(id)'eagle5'的房车!");
        }

        /**
         * 7、退出
         * */
        currentUser.logout();

       System.exit(0);
    }
}

输出

20:18:08.105 [main] INFO  Quickstart - 获取到正确的值! [aValue]
20:18:08.111 [main] INFO  Quickstart - 用户 [lonestarr] 成功登录.
20:18:08.111 [main] INFO  Quickstart - 你的角色是 Schwartz!
20:18:08.112 [main] INFO  Quickstart - 你有 lightsaber:wield 的权限
20:18:08.112 [main] INFO  Quickstart - 你允许‘drive’车牌号为(id)'eagle5'的winnebago。这是钥匙——玩得开心点!

总结

希望这篇文章能帮助你理解如何在基本应用程序中使用Shiro,以及Shiro的主要设计概念,即SubjectSecurityManager

  • 23
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值