Fork me on GitHub

Shrio的基本入门-01

Shrio权限管理框架的使用

1、基本介绍

Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。
Apache Shiro 的首要目标是易于使用和理解。安全有时候是很复杂的,甚至是痛苦的,但它没有必要这样。框架应
该尽可能掩盖复杂的地方,露出一个干净而直观的 API,来简化开发人员在使他们的应用程序安全上的努力。
以下是你可以用 Apache Shiro 所做的事情:

  • 验证用户来核实他们的身份
  • 对用户执行访问控制,如:
    • 判断用户是否被分配了一个确定的安全角色
  • 判断用户是否被允许做某事
  • 在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。
  • 在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。
  • 聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。
  • 启用单点登录(SSO)功能。
  • 为没有关联到登录的用户启用"Remember Me"服务

    以及更多——全部集成到紧密结合的易于使用的 API 中。
    Shiro 视图在所有应用程序环境下实现这些目标——从最简单的命令行应用程序到最大的企业应用,不强制依赖其
    他第三方框架,容器,或应用服务器。当然,该项目的目标是尽可能地融入到这些环境,但它能够在任何环境下立
    即可用。

2、基本入门程序

i、控制台【Java】

a) 导入依赖

<!--导入依赖如下-->
 <!--导入主要依赖文件-->
    <dependencies>
        <!--1、shiro的主要依赖包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--2、日志处理-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>
    </dependencies>

b) 新建 .ini 配置文件

[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345'
# ("That's the same combination on my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz

# -----------------------------------------------------------------------------
# Roles with assigned permissions
# 
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'delete' (action) the user (type) with
# license plate 'zhangsan' (instance specific id)
goodguy = user:delete:zhangsan

c) 新建入门的相关程序

package com.dream;

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;

/**
 * 一个简单的快速入门应用程序,显示了如何使用Shiro的API。
 */
public class Quickstart {

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

    public static void main(String[] args) {

        // 创建具有配置的领域,用户,角色和权限的Shiro,SecurityManager的最简单方法是使用简单的INI配置。
        // 我们将通过使用可以提取.ini文件并返回,SecurityManager实例的工厂来做到这一点:
        // Use the shiro.ini file at the root of the classpath
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        //对于这个简单的示例快速入门,使SecurityManage作为JVM单例访问。大多数应用程序不会
        //这样,而是依靠其容器配置或web.xml来配置webapps。这超出了此简单快速入门的范围,因此
        //我们将只做最少的工作,以便您可以继续对事物有所了解。
        SecurityUtils.setSecurityManager(securityManager);

        // 获取当前的 Subject. 调用 SecurityUtils.getSubject();
        Subject currentUser = SecurityUtils.getSubject();

        // 使用Session做一些事情(不需要Web或EJB容器!!!)
        // 测试使用 Session
        // 获取 Session: Subject中的getSession()函数
        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 + "]");
        }

        //让我们登录当前用户,以便我们可以检查角色和权限: 测试当前的用户是否已经被认证. 即是否已经登录.
        //调动 Subject 的 isAuthenticated()
        if (!currentUser.isAuthenticated()) {
            // 把用户名和密码封装为 UsernamePasswordToken 对象
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            // rememberme
            token.setRememberMe(true);
            try {
                // 执行登录.
                currentUser.login(token);
            }
            // 若没有指定的账户, 则 shiro 将会抛出 UnknownAccountException 异常.
            catch (UnknownAccountException uae) {
                log.info("----> There is no user with username of " + token.getPrincipal());
                return;
            }
            // 若账户存在, 但密码不匹配, 则 shiro 会抛出 IncorrectCredentialsException 异常。
            catch (IncorrectCredentialsException ice) {
                log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");
                return;
            }
            // 用户被锁定的异常 LockedAccountException
            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?
            }
        }

        //打印其标识主体(在这种情况下,为用户名):
        log.info("----> User [" + currentUser.getPrincipal() + "] logged in successfully.");

        // 测试是否有某一个角色. 调用 Subject 的 hasRole 方法.
        if (currentUser.hasRole("schwartz")) {
            log.info("----> May the Schwartz be with you!");
        } else {
            log.info("----> Hello, mere mortal.");
            return;
        }

        //测试类型化的权限(不是实例级别)
        // 测试用户是否具备某一个行为. 调用 Subject 的 isPermitted() 方法。
        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("user:delete:zhangsan")) {
            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!");
        }

        // 执行登出. 调用 Subject 的 Logout() 方法.
        System.out.println("---->" + currentUser.isAuthenticated());
        currentUser.logout();
        System.out.println("---->" + currentUser.isAuthenticated());
        System.exit(0);
    }
}

简洁过程:


// 创建具有配置的领域,用户,角色和权限的Shiro,SecurityManager的最简单方法是使用简单的INI配置。
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
// 使用Session做一些事情(不需要Web或EJB容器!!!
// 获取 Session: Subject中的getSession()函数
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 + "]");
}
//让我们登录当前用户,以便我们可以检查角色和权限: 测试当前的用户是否已经被认证. 即是否已经登录.
//调动 Subject 的 isAuthenticated()
if (!currentUser.isAuthenticated()) {
    // 把用户名和密码封装为 UsernamePasswordToken 对象
    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
    // rememberme
    token.setRememberMe(true);
    try {
        // 执行登录.
        currentUser.login(token);
    }
    // 若没有指定的账户, 则 shiro 将会抛出 UnknownAccountException 异常.
    catch (UnknownAccountException uae) {
        log.info("----> There is no user with username of " + token.getPrincipal());
        return;
    }
    // 若账户存在, 但密码不匹配, 则 shiro 会抛出 IncorrectCredentialsException 异常。
    catch (IncorrectCredentialsException ice) {
        log.info("----> Password for account " + token.getPrincipal() + " was incorrect!");
        return;
    }
    // 用户被锁定的异常 LockedAccountException
    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?
    }
}

//打印其标识主体(在这种情况下,为用户名):
log.info("----> User [" + currentUser.getPrincipal() + "] logged in successfully.");

// 测试是否有某一个角色. 调用 Subject 的 hasRole 方法.
if (currentUser.hasRole("schwartz")) {
    log.info("----> May the Schwartz be with you!");
} else {
    log.info("----> Hello, mere mortal.");
    return;
}

//测试类型化的权限(不是实例级别)
// 测试用户是否具备某一个行为. 调用 Subject 的 isPermitted() 方法。
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("user:delete:zhangsan")) {
    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!");
}

// 执行登出. 调用 Subject 的 Logout() 方法.
System.out.println("---->" + currentUser.isAuthenticated());
currentUser.logout();
System.out.println("---->" + currentUser.isAuthenticated());
System.exit(0);

ii、JavaWeb

a) 导入相关依赖

 <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <spring.version>5.0.2.RELEASE</spring.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- spring 相关依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--encache的核心jar包-->
    <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
      <version>2.4.3</version>
    </dependency>
  </dependencies>

b) Spring整合SpringMVC

1) 配置Web
<!--=============================Spring整合SpringMVC框架======================================-->
<!--1、配置Spring的监听器:默认只会在WEB-INF目录下的applicationContext.xml文件-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--=======================================================================================-->
<!--2、设置配置文件的路径:加载类路径的文件-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--3、配置前端控制器-->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<!--4、配置初始化页面-->
 <welcome-file-list>
     <welcome-file>login.jsp</welcome-file>
</welcome-file-list>
2)配置SpringMVC的核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.dream"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀:设置在根目录中-->
        <property name="prefix" value="/"/>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
3) 配置Spring核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd ">
    
</beans>
4) 配置Log4j的配置文件【添加Shiro的级别】
log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

# General Apache libraries
log4j.logger.org.apache=WARN

# Spring
log4j.logger.org.springframework=WARN

# Default Shiro logging
log4j.logger.org.apache.shiro=TRACE

# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
5)配置Shiro

首先在web.xml文件中添加shiro的过滤器

<!--配置shiro过滤器-->
<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>targetFilterLifecycle</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

添加Shiro的核心配置文件

  • 1、配置SecurityManager
  • 2、配置CacheManager
  • 3、配置 Realm
  • 4、配置 LifecycleBeanPostProcessor
  • 5、启用 IOC 容器中使用 shiro 的注解【必须在配置了 LifecycleBeanPostProcessor 之后才可以使用 】
  • 6、 配置 ShiroFilter
<!-- =========================================================
         Shiro 核心组件 -不特定于Spring
         ========================================================= -->
<!-- Shiro用于启用Web的应用程序的主要业务层对象(在没有网络环境的情况下,请使用DefaultSecurityManager代替)-->
<!--  1. 配置 SecurityManager!  -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="cacheManager" ref="cacheManager"/>
    <property name="authenticator" ref="authenticator"/>
    <property name="realms" ref="jdbcRealm"/>
</bean>
<!-- 使用一些企业级缓存支持来获得更好的性能
        2. 配置 CacheManager.
        2.1 需要加入 ehcache 的 jar 包及配置文件.
    -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <!--如果您已经有一个net.sf.ehcache.CacheManager实例,请在此处设置。如果没有,将使用默认配置创建一个新的:
             <property name="cacheManager" ref="ehCacheManager"/> -->
    <!-- 如果您没有要插入的预构建net.sf.ehcache.CacheManager实例,但是您希望使用特定的Ehcache配置,请在此处指定。否则,将使用默认值。-->
    <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
    <property name="authenticationStrategy">
        <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"/>
    </property>
</bean>
<!--
     3. 配置 Realm
     3.1 直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
    -->
<bean id="jdbcRealm" class="com.dream.realms.ShrioRealm"/>
<!-- =========================================================
         Shiro Spring特定的集成
     ========================================================= -->
<!-- 后处理器自动为Spring配置的Shiro对象调用init()和destroy()方法,因此您不必
            1)为每个bean定义指定init-method和destroy-method属性
            2)甚至知道哪些Shiro对象要求调用这些方法。 -->
<!-- 4. 配置 LifecycleBeanPostProcessor. 可以自定的来调用配置在 Spring IOC 容器中 shiro bean 的生命周期方法-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- 为Spring配置的bean启用Shiro注释。仅在lifecycleBeanProcessor运行之后运行:-->
<!--5. 启用 IOC 容器中使用 shiro 的注解. 但必须在配置了 LifecycleBeanPostProcessor 之后才可以使用 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
      depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager"/>
</bean>

<!-- 在此处(作为FactoryBean)而不是直接在web.xml中定义Shiro筛选器-web.xml使用DelegatingFilterProxy访问此bean。
         这使我们可以通过更好的控制来连接事物,并利用Spring的出色事物,例如PropertiesPlaceholderConfigurer和抽象Bean或我们可能需要的其他任何事物:-->
<!--
    6. 配置 ShiroFilter.
    6.1 id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
                      若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean.
    -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <property name="loginUrl" value="/login.jsp"/>
    <property name="successUrl" value="/list.jsp"/>
    <!--没有权限的页面-->
    <property name="unauthorizedUrl" value="/unauthorized.jsp"/>
    <!--
         配置哪些页面需要受保护. 以及访问这些页面需要的权限.
         1). anon 可以被匿名访问【此设置是默认所有用户都可以进行访问的】
         2). authc 必须认证(即登录)后才可能访问的页面.
         3). logout 登出.
         4). roles 角色过滤器
        -->
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
            /user.jsp = roles[user]
            /admin.jsp = roles[admin]
            <!--# 其他所有内容都需要身份验证:-->
            /** = authc
        </value>
    </property>
</bean>

注:默认的Eacache.xml配置文件

<!-- EhCache XML configuration file used for Shiro spring sample application -->
<ehcache updateCheck="false" name="shiroCache">
    <diskStore path="java.io.tmpdir/shiro-spring-sample"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            />
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"/>

    <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization"
           maxElementsInMemory="100"
           eternal="false"
           timeToLiveSeconds="600"
           overflowToDisk="false"/>
</ehcache>

c) shiro的大致运行流程

posted @ 2020-07-08 21:02  CodeZhangJ  阅读(166)  评论(0)    收藏  举报