01-Shiro

一、简介
Apache Shiro 是 Java  的一个安全(权限)框架。
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。
Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存 等。
下载:http://shiro.apache.org/

功能简介
基本功能点如下图所示:


功能简介
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用 户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有 信息都在会话中;会话可以是普通 JavaSE 环境,也可以是 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web 支持,可以非常容易的集成到Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可 以提高效率; 
Concurrency:Shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能 把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

Shiro 架构(Shiro外部来看)
从外部来看Shiro ,即从应用程序角度的来观察如何使用 Shiro 完成工作:

Shiro 架构
Subject:应用代码直接交互的对象是 Subject,也就是说 Shiro 的对外 API 核心就是 Subject。Subject 代表了当前“用户”, 这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫, 机器人等;与 Subject 的所有交互都会委托给 SecurityManager; Subject 其实是一个门面,SecurityManager 才是实际的执行者; 
SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且其管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与 Shiro 的其他组件进行交互,它相当于 SpringMVC 中 DispatcherServlet 的角色
Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色/ 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource。

Shiro 架构(Shiro内部来看)


Shiro 架构
Subject:任何可以与应用交互的“用户”; 
SecurityManager :相当于SpringMVC 中的 DispatcherServlet;是 Shiro 的心脏; 所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进 行认证、授权、会话及缓存的管理。
Authenticator:负责 Subject 认证,是一个扩展点,可以自定义实现;可以使用认证 策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authorizer:授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控 制着用户能访问应用中的哪些功能; 
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体 的;可以是JDBC 实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要 实现自己的Realm。
SessionManager:管理 Session 生命周期的组件;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境 。
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据 基本上很少改变,放到缓存中后可以提高访问的性能。
Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密/解密。

二、集成Spring
与Web 集成 
Shiro 提供了与 Web 集成的支持,其通过一个 ShiroFilter 入口来拦截需要安全控制的URL,然后进行相应的控制。
ShiroFilter 类似于如 Strut2/SpringMVC 这种 web 框架的前端控制器,是安全控制的入口点,其负责读取配置(如 ini 配置文件),然后判断URL 是否需要登录/权限等工作

ShiroFilter 的工作原理

————————————————————————————————
在web.xml中配置了 shiroFilter 且filterMMapping 是/*,那么表示所有的 请求都会被拦截到。那么。如果访问的路径在 filterChainDefinitions 中没有定义的,或者是设置可以匿名访问的,那么就可以访问到访问路径中的页面。不然,会被重定向到 loginUrl 。

applicationContxt.xml

<property name="filterChainDefinitions">
    <value>
        /login.jsp = anon
        /shiro/login = anon
        /shiro/logout = logout
        
        /user.jsp = roles[user]
        /admin.jsp = roles[admin]
        
        # everything else requires authentication:
        /** = authc
    </value>
</property>

 

<!-- Shiro Filter is defined in the spring application context: -->
    <!-- 
    1. 配置  Shiro 的 shiroFilter.  
    2. DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容器中查找和 
    <filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter bean 的 id. 
    -->
    <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>

部分细节
[urls] 部分的配置,其格式是: “url=拦截器[参数],拦截 器[参数]”; 
如果当前请求的 url 匹配 [urls] 部分的某个 url 模式,将会 执行其配置的拦截器。 
anon(anonymous) 拦截器表示匿名访问(即不需要登 录即可访问)
authc(authentication)拦截器表示需要身份认证通过后 才能访问。

applicationContext.xml

<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"/> 
    
    <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
    
    <!--  
        配置哪些页面需要受保护. 
        以及访问这些页面需要的权限. 
        1). anon 可以被匿名访问
        2). authc 必须认证(即登录)后才可能访问的页面. 
        3). logout 登出.
        4). roles 角色过滤器
    -->
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
          
            # everything else requires authentication:
            /** = authc
        </value>
    </property>
    <!--  
    <property name="filterChainDefinitions">
        <value>
            /login.jsp = anon
            /shiro/login = anon
            /shiro/logout = logout
            
            /user.jsp = roles[user]
            /admin.jsp = roles[admin]
            
            # everything else requires authentication:
            /** = authc
        </value>
    </property>
    -->
</bean>

其中设置的 bean shiroFiilter  必须和 web.xml中的是一致的。
否则会抛出 NoSuchBeanDefinitionExceptioon 异常。在上下文中没有这样的一个bean 被定义。

<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

id 必须和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致。
若不一致, 则会抛出: NoSuchBeanDefinitionException. 因为 Shiro 会来 IOC 容器中查找和 <filter-name> 名字对应的 filter bean。

在org.springframework.web.filter.DelegatingFilterProxy 源码中
是Servlet 2.3 Filter 的一个代理对象。在IOC 容器中找实现了 Filter 接口的那个Bean。

DelegatingFilterProxy 实际上是 Filter 的一个代理对象. 默认情况下, Spring 会到 IOC 容器中查找和 
<filter-name> 对应的 filter bean. 也可以通过 targetBeanName 的初始化参数来配置 filter bean 的 id。
也可以自己进行指定:这时候 配置的bean shiroFilter  可以改成abc。

<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>
    <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>abc</param-value>
    </init-param>
</filter>

在DelegatingFilterProxy 源码中:

public void setTargetBeanName(String targetBeanName) {
   this.targetBeanName = targetBeanName;
}


shiro中默认的过滤器(拦截器)


部分细节
[urls] 部分的配置,其格式是: “url=拦截器[参数],拦截器[参数]”; 
如果当前请求的 url 匹配 [urls] 部分的某个 url 模式,将会 执行其配置的拦截器。 
anon(anonymous) 拦截器表示匿名访问(即不需要登 录即可访问)
authc (authentication)拦截器表示需要身份认证通过后 才能访问。

配置的参数:

public void setFilterChainDefinitions(String definitions) {
        Ini ini = new Ini();
        ini.load(definitions);
        Section section = ini.getSection("urls");
        if (CollectionUtils.isEmpty(section)) {
            section = ini.getSection("");
        }

        this.setFilterChainDefinitionMap(section);
    }

 

URL 匹配模式
url 模式使用 Ant 风格模式 
Ant 路径通配符支持 ?、*、**,注意通配符匹配不 包括目录分隔符“/”: 
– ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/; 
– *:匹配零个或多个字符串,如 /admin 将匹配 /admin、 /admin123,但不匹配 /admin/1; 
– **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b。

URL 匹配顺序 -->优先级问题。可以参见 Servlet 法拦截优先级。
URL 权限采取第一次匹配优先的方式,即从头开始使用第一个匹配的 url 模式对应的拦截器链。
如: 
– /bb/**=filter1 
– /bb/aa=filter2 
– /**=filter3 
– 如果请求的url是“/bb/aa”,因为按照声明顺序进行匹配,那么将使用 filter1 进行拦截。

第一次匹配优先的原则:那么此时的 list.jsp 就访问不了了。

<property name="filterChainDefinitions">
    <value>
        /login.jsp = anon
      
        # everything else requires authentication:
        /** = authc
        /list.jsp = anon
    </value>
</property>


三、认证
Shiro的架构,从外部来看

流程

#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#
# =============================================================================
# Quickstart INI Realm configuration
#
# For those that might not understand the references in this file, the
# definitions are all based on the classic Mel Brooks' film "Spaceballs". ;)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[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
shiro.ini

 

/**
 * Simple Quickstart application showing how to use Shiro's API.
 *
 * @since 0.9 RC2
 */
public class Quickstart {

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


    public static void main(String[] args) {

        // The easiest way to create a Shiro SecurityManager with configured
        // realms, users, roles and permissions is to use the simple INI config.
        // We'll do that by using a factory that can ingest a .ini file and
        // return a SecurityManager instance:

        // Use the shiro.ini file at the root of the classpath
        // (file: and url: prefixes load from files and urls respectively):
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        // for this simple example quickstart, make the SecurityManager
        // accessible as a JVM singleton.  Most applications wouldn't do this
        // and instead rely on their container configuration or web.xml for
        // webapps.  That is outside the scope of this simple quickstart, so
        // we'll just do the bare minimum so you can continue to get a feel
        // for things.
        SecurityUtils.setSecurityManager(securityManager);

        // Now that a simple Shiro environment is set up, let's see what you can do:

        // get the currently executing user:
        // 获取当前的 Subject. 调用 SecurityUtils.getSubject();
        Subject currentUser = SecurityUtils.getSubject();

        // Do some stuff with a Session (no need for a web or EJB container!!!)
        // 测试使用 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 + "]");
        }

        // let's login the current user so we can check against roles and permissions:
        // 测试当前的用户是否已经被认证. 即是否已经登录. 
        // 调动 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?
            }
        }

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

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

        //test a typed permission (not instance-level)
        // 测试用户是否具备某一个行为. 调用 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.");
        }

        //a (very powerful) Instance Level permission:
        // 测试用户是否具备某一个行为. 
        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!");
        }

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

        System.exit(0);
    }
}
Quickstart

1、获取当前的Subject,调用SecurityUtils.getSubject()
  SecurityUtils.setSecurityManager(securityManager);
  Subject currentUser = SecurityUtils.getSubject();
2、测试当前用户是否已经被认证,即是否已经登录。
  currentUser.isAuthenticated()
3、若没有认证,把用户名和密码封装为 UsernamePasswordToken 对象。
1)创建一个表单也难
2)点击提交,把请求提交到SpringMVC 的Handler
3)获取用户名和密码。
4、执行登录。调用Subject 的login(token)

void login(AuthenticationToken var1) throws AuthenticationException;

  currentUser.login(token);
5、自定义Realm 的方法。从数据库中获取对应的记录。,返回给Shiro。-->事实上这里传入的token,是传入到了这里。
1)如何创建Realm。
  实际上继承 org.apache.shiro.realm..AuthenticatingRealm 类。
2)实现doGetAuthenticationInfo(AuthenticationToken) 方法。
6、由 shiro完成对密码的比对。

身份验证 
身份验证:一般需要提供如身份 ID 等一些标识信息来表明登录者的身份,如提供 email,用户名/密码来证明。 
在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份: 
principals:身份,即主体的标识属性,可以是任何属性,如用户名、 邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名/邮箱/手机号。 
credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。 
最常见的 principals 和 credentials 组合就是用户名/密码了。

身份验证基本流程
1、收集用户身份/凭证,即如用户名/密码。
2、调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功。
3、创建自定义的 Realm 类,继承 org.apache.shiro.realm.AuthorizingRealm类,实现 doGetAuthenticationInfo()方法。

后退,然后在重新进来还是可以,因为shiro存在缓存。因此,这是在测试的时候,要用到shiro的logout 功能。
那么shiro 是如何进行如何进行密码不必对的。
1、先是前台一个表单提交,到了shiroHandler (也就是controller)中,获取到了用户名和密码。
2、调用 Subject currentUser = SecurityUtils.getSubject();
    ...currentUser.login(token);  将token 往后传。
3、传到了 ShiroRealm 的doGetAutehticationInfo 方法中。因为ShiroRealm 继承了 AutehticationInfoRealm(做认证)。 
  该方法中的token 就是上述的UsernamePassword token 。因此这里保存了用户名密码。
4、ShiroRealm 中的upToken 是前台获取的用户名,密码。还有一个
  info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
info 保存的是从数据库查询的密码。密码的比对是shiro 完成的。-->什么时候对比,怎么对比。

密码的比对:
通过 AuthenticationRealm 的 credentialsMather 属性来进行的密码的比对。-->现在这里是明文直接比较的。
那么数据表中英爱是加密了的。MD5 盐值加密。加密算法是不可逆的。
1、如何将一个字符串加密为MD5。  -->测试:可以在UsernamePasswordToken 类中的 getPassword() 方法处打一个断点,
  在往前找到 HashedcredentialsMatcher 类中的 doCredentialsMatch(AutenticationToken token,AuthenticationInfo info) 。
2、替换当前的Realm 的CredentialsMather 属性,直接使用HashedCredentialsMatcher 对象,并设置加密算法即可。

<!-- 
    3. 配置 Realm 
    3.1 直接配置实现了 org.apache.shiro.realm.Realm 接口的 bean
-->     
<bean id="jdbcRealm" class="com.atguigu.shiro.realms.ShiroRealm">
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="MD5"></property>
            <property name="hashIterations" value="1024"></property>  这里是指定MD5加密次数
        </bean>
    </property>
</bean>



加密存在的问题:
  如果密码一样的话,加密之后的值一样。现在需要让两个人的密码一样,加密后值还是不一样,这样更加安全。-->加盐
如何做:
1、因此返回值需要将盐值带上。-->使用复杂构造器


  //4). 盐值.
  ByteSource credentialsSalt = ByteSource.Util.bytes(username);//ByteSource 是一个接口,存在一个内部类及其方法。一般使用唯一字符串做盐。

SimpleAuthenticationInfo info = null; //new SimpleAuthenticationInfo(principal, credentials, realmName);
info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;

2、使用ByteSource.util.bytes()来计算盐值。
3、盐值需要唯一:一般使用随机字符串或userId
4、使用 new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); 来计算盐值加密后的值。


Shiro 多个Realm 和验证策略:
ModularRealmAuthenticator 类中。多个Realm 的验证。

身份验证示例


AuthenticationException
如果身份验证失败请捕获 AuthenticationException 或其子类 
最好使用如“用户名/密码错误”而不是“用户名错误”/“密码错误”, 防止一些恶意用户非法扫描帐号库;

认证流程


身份认证流程
1、首先调用 Subject.login(token) 进行登录,其会自动委托给 SecurityManager 。
2、SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;。
3、Authenticator 才是真正的身份验证者,Shiro API 中核心的身份 认证入口点,此处可以自定义插入自己的实现; 
4、Authenticator 可能会委托给相应的 AuthenticationStrategy 进 行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证; 
5、Authenticator 会把相应的 token 传入 Realm,从 Realm 获取 身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

Realm
Realm:Shiro 从 Realm 获取安全数据(如用户、角色、 权限),即 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证 用户是否能进行操作 。
Realm接口如下:


Realm 
一般继承 AuthorizingRealm(授权)即可;其继承了 AuthenticatingRealm(即身份验证),而且也间接继承了 CachingRealm(带有缓存实现)。
Realm 的继承关系:


Authenticator
Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验 证核心的入口点:如果验证成功,将返回AuthenticationInfo 验 证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的AuthenticationException 异常 
SecurityManager 接口继承了 Authenticator,另外还有一个 ModularRealmAuthenticator实现,其委托给多个Realm 进行验证,验证规则通过 AuthenticationStrategy 接口指定。

AuthenticationStrategy  :认证策略
AuthenticationStrategy 接口的默认实现: 
FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第 一个 Realm 身份验证成功的认证信息,其他的忽略; 
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和 FirstSuccessfulStrategy 不同,将返回所有Realm身份验证成功的认证信 息; 
AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有 Realm身份验证成功的认证信息,如果有一个失败就失败了。
ModularRealmAuthenticator 默认是 AtLeastOneSuccessfulStrategy 策略。

四、授权
授权,也叫访问控制,即在应用中控制谁访问哪些资源(如访问页面/编辑数据/页面操作 等)。
在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限 (Permission)、角色(Role)。 
——————————————
主体(Subject):访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。 
资源(Resource):在应用中用户可以访问的 URL,比如访问 JSP 页面、查看/编辑某些 数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。 
权限(Permission):安全策略中的原子授权单位,通过权限我们可以表示在应用中用户 有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用 户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控 制)等。权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许。 
Shiro 支持粗粒度权限(如用户模块的所有权限)和细粒度权限(操作某个用户的权限, 即实例级别的) 
角色(Role):权限的集合,一般情况下会赋予用户角色而不是权限,即这样用户可以拥有 一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等 都是角色,不同的角色拥有一组不同的权限。

授权方式
Shiro 支持三种方式的授权:
编程式:通过写if/else 授权代码块完成
注解式:通过在执行的Java方法上放置相应的注解完成,没有权限将抛出相应的异常 。
– JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成。


默认拦截器

Shiro 内置了很多默认的拦截器,比如身份验证授权等相关的。默认拦截器可以参考 org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举 拦截器:


身份验证相关的


授权相关的


其他


posted @ 2019-04-23 00:36  payn  阅读(208)  评论(0)    收藏  举报