1 SpringSecurity系列一

01.场景:Passcode模式
    默认用户名:user
    默认密码:3ac08a32-4155-4155-926f-e94385f5232d

1.1 HttpSecurity

1.2 多个HttpSecurity

1.3 密码加密

1.4 方法安全

1.5 基于数据库的认证

1.6 角色继承与动态配置权限

2 SpringSecurity系列二

2.1 过滤器链

00.spring security内置拦截器顺序及用途
    1.ChannelProcessingFilter,使用它因为我们可能会指向不同的协议(如:Http,Https)
    2.SecurityContextPersistenceFilter,负责从SecurityContextRepository 获取或存储 SecurityContext。SecurityContext 代表了用户安全和认证过的session
    3.ConcurrentSessionFilter,使用SecurityContextHolder的功能,更新来自“安全对象”不间断的请求,进而更新SessionRegistry
    4.认证进行机制,UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter等等--SecurityContextHolder可能会修改含有Authentication这样认证信息的token值
    5.SecurityContextHolderAwareRequestFilter,如果你想用它的话,需要初始化spring security中的HttpServletRequestWrapper到你的servlet容器中。
    6.JaasApiIntegrationFilter,如果JaasAuthenticationToken在SecurityContextHolder的上下文中,在过滤器链中JaasAuthenticationToken将作为一个对象。
    7.RememberMeAuthenticationFilter,如果还没有新的认证程序机制更新SecurityContextHolder,并且请求已经被一个“记住我”的服务替代,那么将会有一个Authentication对象将存放到这(就是 已经作为cookie请求的内容)。
    8.AnonymousAuthenticationFilter,如果没有任何认证程序机制更新SecurityContextHolder,一个匿名的对象将存放到这。
    9.ExceptionTranslationFilter,为了捕获spring security的错误,所以一个http响应将返回一个Exception或是触发AuthenticationEntryPoint。
    10.FilterSecurityInterceptor,当连接被拒绝时,保护web URLS并且抛出异常。

2.2 使用JSON方式登录

01.场景:使用JSON方式验证SpringSecurity

02.解决:通过重写UsernamePasswordAuthenticationFilter父类中的attemptAuthentication方法

2.3 整合JWT

3 OAuth2.0系列

3.1 认证、授权、鉴权、权限控制

01.常见方案
    a.HTTP Auth Authentication
        a.场景
            一般多被用在内部安全性要求不高的的系统上,如路由器网页管理接口
        b.优点
            通用HTTP身份验证框架有多个验证方案使用。不同的验证方案会在安全强度上有所不同
        c.缺点
            ①请求上携带验证信息,容易被嗅探到
            ②无法注销
    b.Cookie + Session
        a.场景
            传统系统独立鉴权
        b.优点
            服务端存储session,客户端存储cookie,其中cookie保存的为sessionID
            可以灵活revoke权限,更新信息后可以方便的同步session中相应内容
            分布式session,一般使用redis存储
        c.缺点
            依赖于浏览器
    c.JWT
        a.场景
            ①适合做简单的RESTful API认证
            ②适合一次性验证,例如注册激活链接
        b.优点
            服务器不再需要存储session,服务器认证鉴权业务可以方便扩展
            JWT并不依赖cookie,可以使用header传递
            为减少盗用,要使用HTTPS协议传输
        c.缺点
            ①使用过程中无法废弃某个token,有效期内token一直有效
            ②payload信息更新时,已下发的token无法同步
    d.OAuth
        a.场景
            简化模式:不安全,适用于纯静态页面应用
            密码模式:一般在内部系统中使用,调用者是以用户为单位
            客户端模式:一般在内部系统之间的API调用,两个平台之间调用,以平台为单位
            授权码模式:功能最完整、流程最严密的授权模式,通常使用在公网的开放平台中
        b.优点
            OAuth是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,
            而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。
        d.缺点
            相对于场景

02.认证、授权、鉴权、权限控制
    a.图示
        a.流程
            场景1:使用门禁卡开门,认证、授权、鉴权、权限控制四个环节一气呵成,在瞬间同时发生
            场景2:用户的网站登录,认证和授权(前提,用户登录),鉴权和权限控制(后续,支付商品)
            误区:很多时候认证、授权、鉴权和权限控制一同发生,以至于被误解为,认证就是鉴权,鉴权就是认证
                    身份证                 授信媒介            门禁卡识别器                控制门的开关
                  用户名和密码              session           session的合法性             接口允许或拒绝请求
            认证 ---------------> 授权 ---------------> 鉴权 ---------------> 权限+控制 ----------------> 操作
                    用户手机                cookie            cookie的合法性              接口允许或拒绝请求
                    电子邮箱                token              token的合法性              接口允许或拒绝请求
        b.认证(identification)
            定义:确认声明者的身份
            实现:根据声明者独特的识别信息
        c.授权(authorization)
            定义:获取用户的委派权限
            实现:颁发一个授信媒介,不可被篡改,不可伪造,受保护
        d.鉴权(authentication)
            定义:鉴别(抽象的逻辑概念,定义和配置可执行的操作),控制(具体的实现方式,通过一定方式控制操作允许和禁止)
            实现:鉴权和授权是一一对应关系,解析授信媒介,确认其合法性、有效性
        e.权限控制(access/permission control)
            定义:权限(抽象的逻辑概念,定义和配置可执行的操作),控制(具体的实现方式,通过一定方式控制操作允许和禁止)
            实现:实现方式多样,根据具体情况来实现
    b.认证(identification)
        a.定义
            认证:根据声明者所特有的识别信息,确认声明者的身份
            防伪:为了确认用户的身份,防止伪造,在安全要求高的场合,经常会使用多个认证方式对用户的身份进行校验
        b.实现
            身份证
            用户名和密码
            用户手机:手机短信、手机二维码扫描、手势密码
            用户的电子邮箱
            基于时间序列和用户相关的一次性口令
            用户的生物学特征:指纹、语音、眼睛虹膜
            用户的大数据识别
    c.授权(authorization)
        a.定义
            授权:资源所有者委派执行者,赋予执行者指定范围的资源操作权限,以便执行者代理执行对资源的相关操作
            资源所有者:拥有资源的所有权利,一般就是资源的拥有者
            资源执行者:被委派去执行资源的相关操作
            操作权限:可以对资源进行的某种操作
            资源:有价值的信息或数据等,受到安全保护
        b.实现
            浏览器的session机制:一个访问会话保持着用户的授权信息
            浏览器的cookie机制:一个网站的cookie保持着用户的授权信息
            颁发授权token令牌:一个合法有效的令牌中保持着用户的授权信息
    d.鉴权(authentication)
        a.定义
            鉴权:对于一个声明者所声明的身份权利,对其所声明的真实性进行鉴别确认的过程
            鉴权主要是对声明者所声明的真实性进行校验。若从授权出发,则会更加容易理解鉴权
            授权和鉴权是两个上下游相匹配的关系,先授权,后鉴权
            授权和鉴权两个词中的“权”,是同一个概念,就是所委派的权利,在实现上即为授信媒介的表达形式
        b.实现
            门禁卡:通过门禁卡识别器
            钥匙:通过相匹配的锁
            银行卡:通过银行卡识别器
            session/cookie/token:校验session/cookie/token的合法性和有效性
        c.理解
            鉴权是一个承上启下的一个环节,上游它接受授权的输出,校验其真实性后,然后获取权限,为下一步的权限控制做准备
    e.权限控制(access/permission control)
        a.定义
            权限控制:对可执行的各种操作组合配置为权限列表,然后根据执行者权限,若其操作在权限范围内,则允许执行
        b.实现
            门禁:控制门的开关
            自行车锁:控制车轮
            互联网web后端服务:控制接口访问,允许或拒绝访问请求
        c.理解1
            权限控制分为两部分进行理解,一个是权限,另一个是控制。权限是抽象的逻辑概念,而控制是具体的实现方式
            权限:鉴权的输出是权限(Permission),一旦有了权限,便知道了可执行的操作,接下来就是控制的事情了
            控制:根据执行者的权限,对其所执行的操作进行判断,决定允许或禁止当前操作的执行
        d.理解2
            权限作为一个抽象的概念,将执行者和可具体执行操作相分离。若以门禁卡的权限实现为例,则可以各自表达为如下内容:
            这是一个门禁卡,拥有开公司所有的门的权限
            这是一个门禁卡,拥有管理员角色的权限,因而可以开公司所有的门
    f.提问
        授权Authorization和权限控制Permission Control不是同样的东西吗?如果授权的时候已经有资源执行方和持有方参与,那这一步就应该要做权限控制了吧?比如我作为游客想要在你的博客上添加一篇博文,我的浏览器向你的博客系统发送认证信息,你的博客系统进行鉴权,确认我是个游客,接下来判断我要做的操作是添加一篇博文,但是由于权限控制,我不能添加,因此拒绝。按我的理解,你说的这个流程应该应用于类似OAuth的第三方登录场景,比如你的博客要获取我的Github数据,因此要先跳转至Github让我点同意,这一步是我(资源持有者)对你的博客系统的授权。
    g.解答
        从过程上,授权和权限控制是可以分开的两个阶段,获得了授权并不一定就能够成功执行所获得权限允许的操作,比如说授权超时,虽然权限已颁发,但是若超时了就会变得无效,这个判断是由权限控制来决定。【权限控制是比较灵活的阶段】,除了根据客户端发过来的授权信息,还可以根据其它条件判断是否可以执行相应的操作,比如登录的IP地址是否安全、是否长时间未登录、是否在执行大风险的操作,若有风险的话,可以进行追加鉴权操作,确定该操作确实是由用户所委派等。是一个通常的授权和权限控制流程,不仅仅在OAuth,而且普通的用户登录、移动支付等场景也是如此。
        你给的博客登录例子中,当认证你为游客后,浏览器中会保存你作为游客的授权记录,在浏览器中执行的所有后续操作都会按照游客的权限进行控制,这个控制其实发生在认证授权之后,并且在博客后端服务器所实现。你说的“权限控制”(即在授权时游客角色的确定),其实只是【逻辑权限的颁发,还是属于授权】,真正的控制发生在后续操作时是否被拦截或被允许。

3.2 分布式权限校验

01.导入依赖
    现在我们需要为每个服务都添加验证机制,然后我们依然使用SpringSecurity框架作为权限校验框架
    <!--  SpringSession Redis支持  -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <!--  添加Redis的Starter  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--  添加SpirngSecurity -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

02.配置文件
    接着我们在每个服务都编写一下对应的配置文件:
    spring:
      session:
        # 存储类型修改为redis
        store-type: redis
      redis:
        # Redis服务器的信息
        host: localhost
        port: 6379
        password: myslayers
        database: 0

03.开启@EnableRedisHttpSession装配
    @ServletComponentScan
    @SpringBootApplication
    @EnableRedisHttpSession
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }

04.使用Spring Session
    @RestController
    public class SessionController {
    
        @Autowired
        private SessionRepository sessionRepository;
    
        @GetMapping("/set-session")
        public String setSessionData(HttpServletRequest request) {
            Session session = sessionRepository.createSession();
            session.setAttribute("username", "user123");
            sessionRepository.save(session);

            // 获取新创建会话的ID并返回给客户端
            String sessionId = session.getId();
            return "New Session ID: " + sessionId;
        }

        @GetMapping("/get-session/{sessionId}")
        public String getSessionData(@PathVariable("sessionId") String sessionId) {
            Session session = sessionRepository.findById(sessionId);
            String username = session.getAttribute("username");
            return "Username: " + username;
        }
    }

05.测试
    客户端  UserApplication         http://127.0.0.1:8101/user/{uid}
    客户端  BookApplication         http://127.0.0.1:8201/book/{bid}
    客户端  BorrowApplication       http://127.0.0.1:8301/borrow/{uid}
    ---------------------------------------------------------------------------------------------------------
    http://127.0.0.1:8080/set-session  输出 New Session ID: 282041d4-715d-48d5-a77a-3fa6dce849a0
    http://127.0.0.1:8080/get-session/282041d4-715d-48d5-a77a-3fa6dce849a0
    ---------------------------------------------------------------------------------------------------------
    在上述代码中,用户会话数据的存储在Redis中是通过Spring Session自动完成的,而不需要显式地在控制器中执行。
    Spring Session会自动将用户会话数据存储在Redis中,而不需要你在控制器中编写额外的代码来实现。
    ---------------------------------------------------------------------------------------------------------
    上述示例代码中,SessionRepository 接口用于创建和获取会话对象,并将其存储在Redis中。
    通过这些步骤,您已经成功地使用Spring Session将用户会话数据存储在Redis中,实现了分布式会话管理。
    在分布式环境中,多个应用程序节点可以共享相同的会话数据,从而实现了会话的跨应用程序共享。

3.3 四种授权模式

01.客户端模式(Client Credentials)
   这是最简单的一种模式,我们可以直接向验证服务器请求一个Token(这里可能有些小伙伴对Token的概念不是很熟悉,Token相当于是一个令牌,我们需要在验证服务器**(User Account And Authentication)**服务拿到令牌之后,才能去访问资源,比如用户信息、借阅信息等,这样资源服务器才能知道我们是谁以及是否成功登录了)
   当然,这里的前端页面只是一个例子,它还可以是其他任何类型的**客户端**,比如App、小程序甚至是第三方应用的服务。
   虽然这种模式比较简便,但是已经失去了用户验证的意义,压根就不是给用户校验准备的,而是更适用于服务内部调用的场景。

02.密码模式(Resource Owner Password Credentials)
   密码模式相比客户端模式,就多了用户名和密码的信息,用户需要提供对应账号的用户名和密码,才能获取到Token。
   虽然这样看起来比较合理,但是会直接将账号和密码泄露给客户端,需要后台完全信任客户端不会拿账号密码去干其他坏事,所以这也不是我们常见的。

03.隐式授权模式(Implicit Grant)
   首先用户访问页面时,会重定向到认证服务器,接着认证服务器给用户一个认证页面,等待用户授权,用户填写信息完成授权后,认证服务器返回Token。
   它适用于没有服务端的第三方应用页面,并且相比前面一种形式,验证都是在验证服务器进行的,敏感信息不会轻易泄露,但是Token依然存在泄露的风险。

04.授权码模式(Authrization Code)
   这种模式是最安全的一种模式,也是推荐使用的一种,比如我们手机上的很多App都是使用的这种模式。
   相比隐式授权模式,它并不会直接返回Token,而是返回授权码,真正的Token是通过应用服务器访问验证服务器获得的。在一开始的时候,应用服务器(客户端通过访问自己的应用服务器来进而访问其他服务)和验证服务器之间会共享一个`secret`,这个东西没有其他人知道,而验证服务器在用户验证完成之后,会返回一个授权码,应用服务器最后将授权码和`secret`一起交给验证服务器进行验证,并且Token也是在服务端之间传递,不会直接给到客户端。
   这样就算有人中途窃取了授权码,也毫无意义,因为,Token的获取必须同时携带授权码和secret,但是`secret`第三方是无法得知的,并且Token不会直接丢给客户端,大大减少了泄露的风险。

3.4 第1种:密码模式 + Redis缓存服务器

00.场景:SpringSecurity(OAuth2):密码模式 + Redis缓存服务器

01.说明
    由于SpringBoot中的OAuth协议是在SpringSecurity的基础上完成的
    因此首先要添加SpringSecurity依赖,要用到OAuth2,因此添加OAuth2相关依赖
    令牌可以存储在Redis缓存服务器上,同时Redis具有过期等功能,很适合令牌的存储,因此也加入Redis依赖

02.依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.3.RELEASE</version>
        </dependency>
    </dependencies>

03.配置
    spring.redis.database=0
    spring.redis.host=192.168.2.128
    spring.redis.port=6379
    spring.redis.password=myslayers
    spring.redis.jedis.pool.max-active=8
    spring.redis.jedis.pool.max-idle=8
    spring.redis.jedis.pool.max-wait=-1
    spring.redis.jedis.pool.min-idle=0

3.5 第2种:授权码模式

00.场景
    web + SpringCloudSecurity + SpirngCloudOAuth2

01.依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
    </dependencies>

02.说明
    1.授权服务器:端口(8080)
    2.资源服务器:端口(8081)
    3.第三方应用:端口(8082)

4 整合Shiro系列

4.1 简介

01.Shiro框架介绍
    是一个轻量级的安全框架,主要提供了 授权、认证、加密、会话管理这几个功能。

02.shiro安全数据源有哪些
    1.数据库
    2.静态ini文件
    3.session

03.Shiro运行流程
    比如一个登陆流程:
    1、首先调用Subject.login(token)进行登录,他会委托给SecurityManager
    2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
    3、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有就返回认证失败,有的话就继续执行操作。

04.Shiro 的优点
    1、简单的身份认证, 支持多种数据源;非常简单的加密 API;
    2、对角色的简单的授权, 支持细粒度的授权(方法级);
    3、支持一级缓存,以提升应用程序的性能;
    4、内置的基于 POJO 企业会话管理, 适用于 Web 以及非 Web 的环境;
    5、不跟任何的框架或者容器捆绑, 可以独立运行。

05.比较 SpringSecurity 和 Shiro
    1、相比 Spring Security, Shiro 在保持强大功能的同时, 使用简单性和灵活性;
    2、SpringSecurity: 即使是一个一个简单的请求,最少得经过它的 8 个Filter;
    3、SpringSecurity 必须在 Spring 的环境下使用;

06.简述 Shiro 的3个核心组件
    1.Subject
    正与系统进行交互的人, 或某一个第三方服务。
    所有 Subject 实例都被绑定到一个SecurityManager 上。
    2.SecurityManager
    Shiro 架构的心脏, 用来协调内部各安全组件, 管理内部组件实例, 并通过它来提供安全管理的各种服务。
    当 Shiro 与一个 Subject 进行交互时, 实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。
    3.Realms
    本质上是一个特定安全的 DAO. 当配置 Shiro 时, 必须指定至少一个 Realm 用来进行身份验证和授权。
    Shiro 提供了多种可用的 Realms 来获取安全相关的数据. 例如关系数据库(JDBC), INI 及属性文件等。
    可以定义自己 Realm 实现来代表自定义的数据源。

07.Shiro认证过程
    1、应用程序代码调用 Subject.login 方法,传递创建好的包含终端用户的 Principals(身份)和 Credentials(凭证)的 AuthenticationToken 实例;
    2、Subject 实例委托应用程序的 SecurityManager 通过调用securityManager.login(token) 开始真正的验证;
    Subject 实例(通常为 DelegatingSubject或它的子类)
    3、SubjectManager 接收 token,调用内部的 Authenticator 实例调用 authenticator.authenticate(token).Authenticator 通常是一个 ModularRealmAuthenticator 实例, 支持在身份验证中协调一个或多个Realm 实例;
    4、如果应用程序中配置了一个以上的 Realm, ModularRealmAuthenticator 实例将利用配置好的AuthenticationStrategy 来启动 Multi-Realm 认证尝试。在Realms 被身份验证调用之前、调用期间、调用之后,AuthenticationStrategy 被调用使其能够对每个Realm 的结果作出反应。(AuthenticationStrategy都会被调用,对每个Realm 的结果作出反应);
    5、每个配置的 Realm 用来帮助看它是否支持提交的 AuthenticationToken. 如果支持, 那么支持 Realm 的 getAuthenticationInfo 方法将会伴随着提交的 token 被调用. getAuthenticationInfo 方法有效地代表一个特定 Realm 的单一的身份验证尝试。

08.Shiro授权过程
    1、应用程序或框架代码调用任何 Subject 的hasRole*, checkRole*, isPermitted*,或者checkPermission*方法的变体, 传递任何所需的权限;
    2、Subject 的实例 调用securityManager 的对应的方法。
    Subject 实例(通常为 DelegatingSubject或它的子类)
    3、SecurityManager 调用 org.apache.shiro.authz.Authorizer 接口的对应方法.默认情况下,authorizer 实例是一个 ModularRealmAuthorizer 实例, 它支持协调任何授权操作过程中的一个或多个Realm 实例;
    4、每个配置好的 Realm 被检查是否实现了相同的 Authorizer 接口. 如果是, Realm 各自的 hasRole*, checkRole*,isPermitted*,或 checkPermission* 方法将被调用。

09.Shiro 如何自实现认证
    Shiro 的认证过程由 Realm 执行,SecurityManager 会调用 org.apache.shiro.realm.Realm 的 getAuthenticationInfo(AuthenticationToken token) 方法。实际开发中, 通常提供 org.apache.shiro.realm.AuthenticatingRealm 的实现类, 并在该实现类中提供 doGetAuthenticationInfo(AuthenticationToken token)方法的具体实现

10.如何实现自实现授权
    实际开发中,通常提供org.apache.shiro.realm.AuthorizingRealm的实现类,并提供 doGetAuthorizationInfo(PrincipalCollection principals) 方法的具体实现。

11.如何配置在 Spring 中配置使用 Shiro
    1、在 web.xml 中配置 Shiro 的 Filter;
    2、在 Spring 的配置文件中配置 Shiro;
    3、配置自定义 Realm:实现自定义认证和授权;
    4、配置 Shiro 实体类使用的缓存策略;
    5、配置 SecurityManager;
    6、配置保证 Shiro 内部 Bean 声明周期都得到执行的 Lifecycle Bean 后置处理器;
    7、配置AOP 式方法级权限检查;
    8、配置 Shiro Filter。

4.2 过滤器

4.3 JSTL标签

00.Shiro提供了JSTL标签用于JSP页面进行权限控制,如根据用户显示相应的页面按钮
    a.guest
        验证当前用户是否为 “访客”,即未认证(包含未记住)的用户
        shiro标签     <shiro:guest></shiro:guest>  
        freemark标签  <@shiro.guest></@shiro.guest> 
    b.user
        认证通过或已记住的用户
        shiro标签     <shiro:user></shiro:user>
        freemark标签  <@shiro.user></@shiro.user> 
    c.authenticated
        已认证通过的用户,不包含已记住的用户,这是与 user 标签的区别所在
        shiro标签
        freemark标签
    d.notAuthenticated
        未认证通过的用户,与 authenticated 标签相对
        shiro标签     <shiro:authenticated></shiro:authenticated>
        freemark标签  <@shiro.authenticated></@shiro.authenticated>
    e.principal
        输出当前用户信息,通常为登录帐号信息
        shiro标签     <shiro.principal property="name"/>
        freemark标签  <@shiro.principal property="name"/>
    f.hasRole
        验证当前用户是否属于该角色
        shiro标签     <shiro:hasRole name="administrator"></shiro:hasRole>
        freemark标签  <@shiro.hasRole name=”admin”></@shiro.hasRole>
    g.hasAnyRoles
        验证当前用户是否属于这些角色中的任何一个,角色之间逗号分隔
        shiro标签     <shiro:hasAnyRoles name="admin,user,operator"></shiro:hasAnyRoles>
        freemark标签  <@shiro.hasRole name=”admin”></@shiro.hasRole> 
    h.hasPermission
        验证当前用户是否拥有该权限
        shiro标签     <shiro:hasPermission name="/order:*"></shiro:hasPermission>
        freemark标签  <@shiro.hasPermission name="/order:*"></@shiro.hasPermission> 
    i.lacksRole
        验证当前用户不属于该角色,与 hasRole 标签相反
        shiro标签     <shiro:hasRole name="admin"></shiro:hasRole>
        freemark标签  <@shiro.hasRole name="admin"></@shiro.hasRole>
    j.lacksPermission
        验证当前用户不拥有某种权限,与 hasPermission 标签是相对的
        shiro标签     <shiro:lacksPermission name="/order:*"></shiro:lacksPermission>
        freemark标签  <@shiro.lacksPermission name="/order:*"></@shiro.lacksPermission>
    k.引入 shiro-JSTL 标签
        <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

4.4 整合Shiro方式一

00.场景
    SpringBoot整合Shiro + 在Thymeleaf中使用Shiro标签

01.说明
    这里不需要添加spring-boot-starter-web依赖,
    shiro-spring-boot-web-starter中已经依赖了spring-boot-starter-web
    同时,本案例使用Thymeleaf模板,因此添加Thymeleaf依赖
    另外,为了在Thymeleaf中使用 shiro标签,因此引入了thymeleaf-extras-shiro依赖

02.依赖
    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies> 

03.配置
    #开启Shiro配置,默认为true
    shiro.enabled=true

    #开启Shiro Web配置,默认为true
    shiro.web.enabled=true

    #登录地址,默认为"/loing.jsp"
    shiro.loginUrl=/login

    #登录成功地址,默认为"/"
    shiro.successUrl=/index

    #未获授权默认跳转地址
    shiro.unauthorizedUrl=/unauthorized

    #是否允许通过URL参数实现会话跟踪,如果网站支持Cookie,可以关闭此选项,默认为true
    shiro.sessionManager.sessionIdUrlRewritingEnabled=true

    #是否允许通过Cookie实现会话跟踪,默认为true
    shiro.sessionManager.sessionIdCookieEnabled=true

4.5 整合Shiro方式二

00.场景
    SpringBoot整合Shiro

01.依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

4.6 整合Shiro方式三

00.场景
    SpringBoot整合Shiro 

01.说明
    这里不需要添加spring-boot-starter-web依赖
    shiro-spring-boot-web-starter中已经依赖了spring-boot-starter-web

02.依赖
    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

03.说明
    #开启Shiro配置,默认为true
    shiro.enabled=true

    #开启Shiro Web配置,默认为true
    shiro.web.enabled=true

    #登录地址,默认为"/loing.jsp"
    shiro.loginUrl=/login

    #登录成功地址,默认为"/"
    shiro.successUrl=/index

    #未获授权默认跳转地址
    shiro.unauthorizedUrl=/unauthorized

    #是否允许通过URL参数实现会话跟踪,如果网站支持Cookie,可以关闭此选项,默认为true
    shiro.sessionManager.sessionIdUrlRewritingEnabled=true

    #是否允许通过Cookie实现会话跟踪,默认为true
    shiro.sessionManager.sessionIdCookieEnabled=true