SpringBoot基础学习之整合Shiro框架(上篇)

前言

小伙伴们,大家好,我是狂奔の蜗牛rz,当然你们可以叫我蜗牛君,我是一个学习Java半年多时间的小菜鸟,同时还有一个伟大的梦想,那就是有朝一日,成为一个优秀的Java架构师。


这个SpringBoot基础学习系列用来记录我学习SpringBoot框架基础知识的全过程 (这个系列是参照B站狂神的SpringBoot最新教程来写的,由于是之前整理的,但当时没有发布出来,所以有些地方可能有错误,希望大家能够及时指正!)

之后我将会尽量以一天一更的速度更新这个系列,还没有学习SpringBoot的小伙伴可以参照我的博客学习一下;当然学习过的小伙伴,也可以顺便跟我一起复习一下基础。最后,希望能够和大家一同进步吧!加油吧!少年们!


由于篇幅较长,所以这里我将其分为了上下两篇博客上篇主要了解Shiro的功能,以及基本环境的搭建下篇主要学习Shiro整合Mybatis和Thymeleaf框架

今天我们来到了SpringBoot基础学习的第九站:整合Shiro框架(上篇), ,废话不多说,让我们开始今天的学习内容吧,

8.1 Shiro基础知识

8.1.1 什么是Shiro?

  • Apache Shiro是一个安全 (权限) 框架
  • Shiro可以非常容易的开发出足够好的应用,其不仅可以用于JavaSE环境,也可以用在JavaEE环境
  • Shiro可以完成认证授权加密会话管理Web集成缓存

官方网址:http://shiro.apache.org/

8.1.2 Shiro有哪些功能?

在这里插入图片描述

  • Authentication身份认证,登录,验证用户是不是拥有相应的身份

  • Authorization授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作

  • Session Management会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都会在会话中,会话可以是普通的JavaSE环境,也可以是Web环境

  • Cryptograpy加密,保护数据的安全性,如密码加密存储到三个月会议纪要中,而不是明文存储

    特别提醒会议纪要是会议工作的一项重要的环节。 它有两个目的:一是向上级汇报会议情况,以获得上级及时的指导 ;二是向下传达,以便工作贯彻落实 。

  • Web SupportWeb支持,可以非常容易的集成到Web环境

  • Caching缓存,比如用户登录后,其用户信息,拥有角色、权限不必每次去查,这样可以提高效率

  • ConcurrencyShiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动的传播过去

  • Testing提供测试支持

  • Run As:允许一个用户假装为另一个用户 (如果他们允许) 的身份进行访问

  • Remember Me记住我,这个是非常常见的功能,即一次登录后,下次再来就不用登录了

8.1.3 Shiro架构 (外部)

在这里插入图片描述

  • Subjcet

应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subjcet,Subjcet代表了当前的用户;

这个用户不一定是一个具体的人,与当前应用的交互的任何东西都是Subject,如网络爬虫,机器人等;

与Subject的所有交互都会委托给SecurityManager,Subject其实是一个门面,SubjcetManager才是实际的执行者

  • SecurityManager

安全管理器,即所有与安全相关的操作都会与SecurityManager交互,并且它管理着所有的Subject,它相当于SpringMVC的DispatcherServlet (前端控制器) 的角色

  • Realm

Shiro从Realm获取安全数据 (如用户、角色、权限),就是说SecurityMananger 要验证用户身份,那么它需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法;

也需要从Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行,把Realm看成DataSource

8.2 搭建hello-shiro模块基本环境

8.2.1 创建父工程项目

1.创建Maven项目
  • 创建一个Maven项目,无需勾选任何选项

在这里插入图片描述

2.设置项目基本信息
  • 设置 GroupId ( 即组名,表示主项目标识 ) 为 com.kuangArtifactId ( 即工程名,表示子项目 (模块) 标识 ) 为 springboot-07-shiro
3.选择项目存放位置
  • 选择一个要存储的指定位置即可
4.创建父项目成功
  • 如下图所示,创建父工程项目成功

在这里插入图片描述

5.删除多余文件
  • 父项目中的src源文件删除

在这里插入图片描述

8.2.2 搭建子模块基本环境

1.创建hello-shiro子模块
  • 父项目中创建子模块

在这里插入图片描述

  • 创建普通Maven项目,不用勾选其他

在这里插入图片描述

  • 编写子模块的基本信息,设置GroupId (组名) 和 ArtifactId (工程名)

在这里插入图片描述

  • 选择子模块存放位置

在这里插入图片描述

2.导入资源依赖
  • 子模块的pom.xml文件中导入对应的资源依赖
<dependencies>
    <!-- shiro的核心资源依赖 -->
    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.1</version>
    </dependency>
    <!-- slf4j日志资源依赖 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>1.7.21</version>
    </dependency>
    <!-- slf4j门面资源依赖 -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.21</version>
    </dependency>
    <!-- log4j资源依赖 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
</dependencies>
  • 查看右侧Maven中导入的资源依赖

在这里插入图片描述

3.项目基本结构
  • resources目录下创建log4j.properties文件 (用来设置有关日志输出的相关属性)和shiro.ini文件 (用来设置用户、角色及权限信息)

在这里插入图片描述

4.编写log4j.properties配置文件
  • 设置有关log4j日志输出的相关属性
# log4j的根日志类型
log4j.rootLogger=INFO, stdout

# log4j的日志输出
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

# apache的全局资源
log4j.logger.org.apache=WARN

# Spring框架的日志
log4j.logger.org.springframework=WARN

# 默认的Shiro日志
log4j.logger.org.apache.shiro=INFO

# 不启用冗长日志
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
5.编写shiro.ini测试文件
  • shiro.ini配置文件中设置用户角色权限信息

特别提醒ini文件用来进行shiro测试,如果ini文件没有高亮显示,创建完该文件IDEA会提示你安装相应插件

# 设置用户密码及对应角色
[users]
# 用户名为'root',密码为'secret',角色为'admin'
root = secret, admin
# 用户名'guest',密码为'guest',角色为'guest'
guest = guest, guest
# 用户'presidentskroob',密码为'123456',角色为'president'
# presidentskroob:总统斯卡普
presidentskroob = 12345, president
# 用户'darkhelmet',密码为'ludicrousspeed',角色为'darklord'和'schwartz'
# darkhelmet:黑暗头盔,ludicrousspeed:惊死人的飞速,schwartz:施瓦兹
darkhelmet = ludicrousspeed, darklord, schwartz
# 用户'lonestarr',密码为'vespa',角色为'goodguy'和'schwartz'
# lonestarr是独孤的斯塔尔
lonestarr = vespa, goodguy, schwartz

# 设置角色及相关权限
[roles]
# 角色'admin'拥有所有的权限,使用通配符'*'表示
admin = *
# 角色'schwartz'拥有lightsaber(类型)下的所有权限(使用通配符*表示)
# schwartz是指绝地武士,lightsaber是光剑
schwartz = lightsaber:*
# 角色'goodguy'只拥有'winnebago'(类型)下的'drive'(行为)
# 下的'eagle5'(即牌照,这里指具体的实例(用户)id)的权限
# goodguy:好人,winnebago:温尼贝格,drive:驾驶,eagle5老鹰5
goodguy = winnebago:drive:eagle5
6.编写Quickstart类
  • 设置shiro.ini文件存在的用户
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
 *
 * @since 0.9 RC2
 */
public class Quickstart {
    
    // 使用日志门面
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

    public static void main(String[] args) {

        /**
         * 一个简单的方式去创建Shiro安全管理器(SecurityManager),
         * 通过配置范围(realms),用户(users),角色(roles)和权限(permissions),去使用简单INI配置
         * 我们将使用一个工厂(factory)可以摄取一个.ini文件并且返回一个安全管理器实例
         */

        /**
         * 1.加载.ini配置文件,获取securityManager安全管理器(这三步是固定的)
         */
        // 1.1 通过工厂模式来加载根路径下的.ini文件(但是IniSecurityManagerFactory已经过时了)
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        // 1.2 通过工厂的单例模式来获取一个单例的安全管理器(SecurityManager)对象
        SecurityManager securityManager = factory.getInstance();
        /** 
         * 大多数应用不会这么做,而是依赖它们webapps下的容器配置或者web.xml
         */
        // 1.3 设置安全管理器为上面创建的单例securityManager对象
        SecurityUtils.setSecurityManager(securityManager);

        /** 
         * 2.目前还没有设置一个简单的Shiro环境,下面是具体步骤
         */
        // 2.1 获取当前的用户对象Subject
        Subject currentUser = SecurityUtils.getSubject();

        /** 
         * 2.2 存取用户的session信息
         */
        // 2.2.1 通过当前用户获取session
        Session session = currentUser.getSession();
        // 2.2.2 在session中存值
        session.setAttribute("someKey", "aValue");
        // 2.2.3 获取session中存入的值
        String value = (String) session.getAttribute("someKey");
        // 2.2.4 判断其是否等于session中之前存入的值
        if (value.equals("aValue")) {
            // 打印value的值
            log.info("Subject=>session[" + value + "]");
        }

        // 2.3 判断当前用户是否被认证
        if (!currentUser.isAuthenticated()) {
            // 2.3.1 如果通过认证就拿到一个Token令牌,用户名为孤独的斯塔尔(lonestarr)
            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
            // 2.3.2 开启记住我
            token.setRememberMe(true);
            try {
                // 2.3.3 执行登录操作
                /**
                 * 用户操作实际放在Realm中,而登录操作在Subject中
                 */
                currentUser.login(token);
            // 2.3.4 捕获未知账户异常(UnknownAccountException)
            } catch (UnknownAccountException uae) {
                // 打印token中获取的认证
                log.info("There is no user with username of " + token.getPrincipal());
            // 2.3.5 捕获错误凭证异常
            } catch (IncorrectCredentialsException ice) {
                // 打印token中获取的认证
                log.info("Password for account " + token.getPrincipal() + " was incorrect!");
            // 2.3.6 捕获用户被锁定异常
            } catch (LockedAccountException lae) {
                // 打印token中获取的认证
                log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                        "Please contact your administrator to unlock it.");
            }
            // 2.3.7 捕获其他的异常 (根据具体的应用需求定制)
            catch (AuthenticationException ae) {
                // 异常的条件和错误
            }
        }

        // 打印token中当前用户的认证
        log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");

        /** 
         * 2.4 测试角色
         */
        // 2.4.1 判断当前用户lonestarr(孤独的斯塔尔)的角色是否为原力(schwartz)
        if (currentUser.hasRole("schwartz")) {
            // 2.4.2 认证后登录成功,输出日志"愿原力与你同在"
            log.info("May the Schwartz be with you!");
        } else {
            // 2.4.3 认证失败,输出日志"你好,凡人"
            log.info("Hello, mere mortal.");
        }


        /** 
         * 2.5 测试用户对应权限
         */
        // 2.5.1 粗粒度权限
        // 判断当前用户(lonestarr)是否拥有lightsaber:wield(光剑:挥舞)的权限
        if (currentUser.isPermitted("lightsaber:wield")) {
            // #1 认证成功,日志输出"你使用了光剑环,明智的使用它"
            log.info("You may use a lightsaber ring.  Use it wisely.");
        } else {
            // #3 认证失败,日志输出"抱歉,原力的主人才拥有光剑环"
            log.info("Sorry, lightsaber rings are for schwartz masters only.");
        }

        /** 
         * 2.5.2 细粒度权限
         */
        // #1 判断当前用户(lonestarr)是否拥有winnebago:drive:eagle5(温尼贝格-类型:驾驶-行为:老鹰5-牌照)权限
        if (currentUser.isPermitted("winnebago:drive:eagle5")) {
            // #2 认证成功,日志输出"你拥有驾驶类型为温尼贝格,牌照(id)为eagle5的权限,钥匙在这,祝你愉快!"
            log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                    "Here are the keys - have fun!");
        } else {
            // #3 认证失败,日志输出"抱歉,你不被允许驾驶'eagle5'温尼贝格
            log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
        }

        // 2.6 注销,所有角色都可以执行该操作
        currentUser.logout();
        // 退出系统
        System.exit(0);
    }
    
}
  • 设置shiro.ini文件不存在的用户
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
 *
 * @since 0.9 RC2
 */
public class Quickstart {
    
    //使用日志门面
    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

    public static void main(String[] args) {

        /**
         * 一个简单的方式去创建Shiro安全管理器(SecurityManager),
         * 通过配置范围(realms),用户(users),角色(roles)和权限(permissions),去使用简单INI配置
         * 我们将使用一个工厂(factory)可以摄取一个.ini文件并且返回一个安全管理器实例
         */

        /** 
         * 1.加载.ini配置文件,获取securityManager安全管理器(这三步是固定的)
         */
        // 1.1 通过工厂模式来加载根路径下的.ini文件(但是IniSecurityManagerFactory已经过时了)
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        // 1.2 通过工厂的单例模式来获取一个单例的安全管理器(SecurityManager)对象
        SecurityManager securityManager = factory.getInstance();
        /** 
         * 大多数应用不会这么做,而是依赖它们webapps下的容器配置或者web.xml
         */
        // 1.3 设置安全管理器为上面创建的单例securityManager对象
        SecurityUtils.setSecurityManager(securityManager);

        /** 
         * 2.目前还没有设置一个简单的Shiro环境,下面是具体步骤
         */
        // 2.1 获取当前的用户对象Subject
        Subject currentUser = SecurityUtils.getSubject();

        /**
         * 2.2 存取用户的session信息
         */
        // 2.2.1 通过当前用户获取session
        Session session = currentUser.getSession();
        // 2.2.2 在session中存值
        session.setAttribute("someKey", "aValue");
        // 2.2.3 获取session中存入的值
        String value = (String) session.getAttribute("someKey");
        // 2.2.4 判断其是否等于session中之前存入的值
        if (value.equals("aValue")) {
            // 打印value的值
            log.info("Subject=>session[" + value + "]");
        }

        /**
         * 2.3 判断当前用户是否被认证
         */
        if (!currentUser.isAuthenticated()) {
            // 2.3.1 设置一个不存在的用户
//            UsernamePasswordToken token = new UsernamePasswordToken("lonestarr22", "vespa");
            // 2.3.2 开启记住我
            token.setRememberMe(true);
            
            // ...(由于后面的代码跟上面的代码相同,所以这里就省略掉了)...   

        /** 
         * 2.6 注销,所有角色都可以执行该操作
         */
        currentUser.logout();
        // 退出系统
        System.exit(0);
    }
        
}
7.查看结果
  • 查看设置存在的用户的控制台输出

在这里插入图片描述

结果该用户登录成功,拥有相应的权限!

  • 查看设置不存在的用户的控制台输出

在这里插入图片描述

结果该用户不存在,没有相应的权限!

8.3 SpringBoot整合Shiro环境搭建

8.3.1搭建基本环境

1.创建shiro-springboot子模块

在这里插入图片描述

2.创建Spring initializr项目

在这里插入图片描述

3.设置子模块基本信息

在这里插入图片描述

4.选择子模块的资源依赖
  • 这里勾选Spring WebThymeleaf模板引擎即可

在这里插入图片描述

5.选择子模块的存放位置

在这里插入图片描述

6.子模块创建成功

在这里插入图片描述

7.删除子项目中多余文件

在这里插入图片描述

8.3.2 搭建环境后简单测试

1.模块基本结构和编写配置文件
1-1 项目模块基本结构

在这里插入图片描述

1-2 编写application.properties配置文件
# 修改默认服务器端口号
server.port=8888
2.导入相关资源依赖
2-1 导入shiro整合springboot资源依赖
<!-- springboot整合shiro的资源依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.1</version>
</dependency>
2-2 所有的资源依赖
<dependencies>
    <!-- Shiro的三大核心:
         Subject:用户 SecurityManager:管理所有用户 Realm:连接数据 -->
    <!-- springboot整合shiro的资源依赖 -->
    <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.1</version>
    </dependency>
    <!-- thymeleaf的资源依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <!-- spring-boot-web资源依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- spring-boot-test资源依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
3.编写MyController控制器类
package com.kuang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

// 使用@Controller注解, 将UserController注册为控制器, 交由Spring的IOC容器统一管理
@Controller
public class UserController {
    
    /** 
     * 跳转到首页
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     * 多个请求使用{}包围, 使用","进行分隔 
     */
    @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        // 设置视图模型信息
        model.addAttribute("msg", "Hello,Shiro!");
        // 返回视图逻辑名
        return "index";
    }
    
}
4.编写index.html和页面访问测试
4-1 编写index.html主页面
<!DOCTYPE html>
<!-- 注意: 这里需要引入thymeleaf的命名空间, 否则无法生效 -->
<html lang="en" xmlns:th=http://www.thymeleaf.org>
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>
<!--使用th:text来显示信息-->
<p th:text="${msg}"></p>
</body>
</html>
4-2 页面访问测试

在这里插入图片描述

结果访问首页成功!

8.3.3 完善模块环境

1.模块基本结构

在这里插入图片描述

2.编写ShiroConfig和UserRealm配置类
2-1 编写ShiroConfig核心配置类
package com.kuang.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

// 使用@Configuration注解, 让ShiroConfig成为配置类, 交由Spring的IOC容器统一管理
@Configuration
public class ShiroConfig {
    
    /** 
     * 1.创建realm对象,需要自定义类
     */
    // 1.1 将userRealm作为组件, 注册到Spring的IOC容器中去
    @Bean
    public UserRealm userRealm() {
        // 1.2 返回值为创建一个UserRealm对象
        return new UserRealm();
    }
    
    /** 
     * 2.DefaultWebSecurityManager(默认的Web安全管理器)
     */
    // 2.1 使用@Bean注解,将getDefaultWebSecurityManager方法作为组件, 注册到Spring容器中去
    @Bean(name="securityManager")
    // 2.2 使用@Qualifier注解,通过名字(userRealm)获取Spring的IOC容器中的UserRealm对象
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        // 2.3 获取DefaultWebSecurityManager对象
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        // 2.4 关联userRealm对象
        securityManager.setRealm(userRealm);
        // 2.5 返回securityManager对象
        return securityManager;
    }
    
    /** 
     * 3.ShiroFilterFactoryBean(Shiro过滤器工厂Bean)
     */
    // 使用@Bean注解,将getShiroFilterFactoryBean方法作为组件, 注册到Spring的IOC容器中去
    @Bean
    // 使用@Qualifier注解,通过名字(securityManage)获取Spring容器中的DefaultWebSecurityManager对象
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        /** 
         * 3.1 获取Shiro的过滤工厂, 设置安全管理器
         */
        // 3.1.1 获取ShiroFilterFactoryBean对象
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 3.1.2 设置securityManager(安全管理器)对象
        factoryBean.setSecurityManager(securityManager);

        /**
         * 3.2 添加shiro的内置过滤器
         * anon:无需认证就可以访问
         * authc:必须认证了才能访问
         * user:必须拥有记住我功能才能使用
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才能访问
         */
        
        /** 
         * 3.2.1 拦截
         */
        //获取LinkedHashMap对象
        Map<String, String> filterMap = new LinkedHashMap<>();

         /** 
          * 3.2.2 授权
          * 使用Map集合, 设置权限对应的key-value值:key是相应的请求,value是权限值
          */
        // #1 "/user/addUser"是添加用户的请求,"perms[user:addUser]"表示只有拥有添加用户权限才能访问
         filterMap.put("/user/addUser", "perms[user:addUser]");
        // #2 "/user/*"所有的user下的请求, "authc"表示通过认证后才能访问
        filterMap.put("/user/*", "authc");

        // 3.2.3 设置FilterChainDefinitionMap(过滤链式定义Map)
        factoryBean.setFilterChainDefinitionMap(filterMap);

        // 3.4 返回factoryBean对象
        return factoryBean;
    }
    
}
2-2 编写UserRealm数据连接类
package com.kuang.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

// 自定义UserRealm,继承AuthorizingRealm类
public class UserRealm extends AuthorizingRealm {

    /** 
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        return null;
    }

    /** 
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>授权doGetAuthenticationInfo");
        return null;
    }
}
3.修改主页面和编写增加修改页面
3-1 修改index.html页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>新增用户</title>
</head>
<body>

</body>
</html>
3-2 编写addUser.html添加页
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>新增用户</title>
</head>
<body>
<h1>新增用户</h1>
</body>
</html>
3-3 编写updateUser.html修改页
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改用户</title>
</head>
<body>
<h1>修改用户</h1>
</body>
</html>
4.编写UserController控制器类
package com.kuang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

// 使用@Controller注解, 将UserController注册为控制器, 交由Spring的IOC容器统一管理
@Controller
@Controller
public class UserController {

    /** 
     * 跳转到首页
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     * 多个请求使用{}包围, 使用","进行分隔 
     */
    @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        // 设置视图模型信息
        model.addAttribute("msg", "Hello,Shiro!");
        // 返回视图逻辑名
        return "index";
    }

    /** 
     * 跳转到添加页面
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     */
    @RequestMapping("/user/addUser")
    public String toAddPage() {
        // 返回视图逻辑名
        return "user/addUser";
    }

    /** 
     * 跳转到修改页面
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     */
    @RequestMapping("/user/updateUser")
    public String toUpdatePage() {
        // 返回视图逻辑名
        return "user/updateUser";
    }

}
5.页面访问测试
5-1 访问index.html主页

在这里插入图片描述

结果访问首页成功!

5-2 访问addUser.html新增页

在这里插入图片描述

结果访问失败,404找不到资源!

5-3 访问updateUser.html修改页

在这里插入图片描述

结果访问失败,404找不到资源!

4-4 错误分析

具体分析

可以发现,请求路径上出现了login.jsp,这个login.jsp应该是Shiro默认登录JSP页面;因为我们要进行增改操作时,需要先登录进行身份验证后,才能执行用户对应角色的操作;

但由于我们并没有登录的jsp页面,所以才出现了访问失败问题;因此我们需要自定义登录页,编写相关的控制请求,以及在Shiro的核心配置类中编写登录跳转的相关请求

8.3.4 自定义登录页和页面测试访问

1.修改子模块结构和编写login.html登录页
1-2 在templates下创建login.html登录页

在这里插入图片描述

1-2 编写login.html登录页
<!DOCTYPE html>
<!-- 引入thymeleaf的命名空间 -->
<html lang="en" xmlns:th=http://www.thymeleaf.org>
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<h1>登录</h1>
<hr/>
<p th:text="${msg}" style="color: red"></p>
<!-- 登录验证表单 -->
<form th:action="@{/login}" method="post">
    <p>
        用户名:<input type="text" name="username"/>
    </p>
    <p>
        密码:<input type="password" name="password"/>
    </p>

    <input type="submit" value="登录" />
</form>

</body>
</html>
2.修改UserController控制类和ShiroConfig以及UserRealm配置类
2-1 修改UserController控制类
package com.kuang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class UserController {

    /** 
     * 跳转到首页
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     * 多个请求使用{}包围, 使用","进行分隔 
     */
    @RequestMapping({"/","/index"})
    public String toIndex(Model model) {
        // 设置视图模型信息
        model.addAttribute("msg", "Hello,Shiro!");
        // 返回视图逻辑名
        return "index";
    }

    /** 
     * 跳转到添加页面
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     */
    @RequestMapping("/user/addUser")
    public String toAddPage() {
        // 返回视图逻辑名
        return "user/addUser";
    }

    /** 
     * 跳转到修改页面
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     */
    @RequestMapping("/user/updateUser")
    public String toUpdatePage() {
        // 返回视图逻辑名
        return "user/updateUser";
    }

    
    /** 
     * 跳转到登录页面
     * 使用@RequestMapping注解, 设置请求映射路径, 请求方式为get方式
     */
    @RequestMapping("/toLogin")
    public String toLogin() {
        // 返回视图逻辑名
        return "login";
    }
    
    /**
     * 登录验证
     * 使用@PostMapping注解, 设置请求映射路径, 请求方式为post方式
     */
    @PostMapping("/login")
    public String login(String username, String password,Model model) {
        // 获取当前用户
        Subject subject = SecurityUtils.getSubject();
        // 封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        try{
            // 执行登录方法,如果没有异常就说明OK了
            subject.login(token);
            // 设置视图逻辑名为index主页
            return "index";
        // 捕获未知用户异常
        } catch(UnknownAccountException uae) {
            // 设置视图模型信息
            model.addAttribute("msg","用户名错误");
            // 设置视图逻辑名为login登录页
            return "login";
        // 捕获错误认证异常
        } catch (IncorrectCredentialsException ica) {
            // 设置视图模型信息
            model.addAttribute("msg","密码错误");
            // 设置视图逻辑名为login登录页
            return "login";
        }
    }

}
2-2 修改ShiroConfig配置类
package com.kuang.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

// 使用@Configuration注解, 让ShiroConfig成为配置类, 交由Spring的IOC容器统一管理
@Configuration
public class ShiroConfig {

    /** 
     * 1.创建realm对象,需要自定义类
     */
    // 1.1 将userRealm注册到Spring容器中去
    @Bean
    public UserRealm userRealm() {
        // 1.2 返回值为创建一个UserRealm对象
        return new UserRealm();
    }
    
    /** 
     * 2.设置DefaultWebSecurityManager(默认Web安全管理器)
     */
    //2.1 使用@Bean注解,将getDefaultWebSecurityManager方法注册到Spring容器中去
    @Bean(name="securityManager")
    //2.2 使用@Qualifier注解,通过名字userRealm获取Spring容器中的UserRealm对象
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        //2.3 获取DefaultWebSecurityManager对象
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //2.4 关联userRealm对象
        securityManager.setRealm(userRealm);
        //2.5 返回securityManager对象
        return securityManager;
    }
    
    /** 
     * 3.设置ShiroFilterFactoryBean(Shiro过滤器工厂Bean)
     */
    //使用@Bean注解,将getShiroFilterFactoryBean方法注册到Spring容器中去
    @Bean
    //使用@Qualifier注解,通过名字securityManage获取Spring容器中的DefaultWebSecurityManager对象
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        
        /** 
         * 3.1 获取Shiro的过滤工厂, 设置安全管理器
         */
        // 3.1.1 获取ShiroFilterFactoryBean对象
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // 3.1.2 设置securityManager(安全管理器)对象
        factoryBean.setSecurityManager(securityManager);
        
        /*
         * 3.2 添加shiro的内置过滤器
         * anon:无需认证就可以访问
         * authc:必须认证了才能访问
         * user:必须拥有记住我功能才能使用
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才能访问
         * */

        /** 
         * 3.2.1 拦截
         */
        //获取LinkedHashMap对象
        Map<String, String> filterMap = new LinkedHashMap<>();

         /** 
          * 3.2.2 授权
          * 使用Map集合, 设置权限对应的key-value值:key是相应的请求,value是权限值
          */
        // #1 "/user/addUser"是添加用户的请求,"perms[user:addUser]"表示只有拥有添加用户权限才能访问
         filterMap.put("/user/addUser", "perms[user:addUser]");
        // #3 "/user/updateUser"是修改用户的请求,"perms[user:updateUser]"表示只有拥有修改用户权限才能访问
        filterMap.put("/user/updateUser", "perms[user:updateUser]");
        // #2 "/user/*"所有的user下的请求, "authc"表示通过认证后才能访问
        filterMap.put("/user/*", "authc");

        // 3.2.3 设置FilterChainDefinitionMap(过滤链式定义Map)
        factoryBean.setFilterChainDefinitionMap(filterMap);

        // 3.3 设置登录和未授权的相关请求
        // 3.3.1 设置登录的请求
        factoryBean.setLoginUrl("/toLogin");

        // 3.4 返回factoryBean对象
        return factoryBean;
    }

}
2-3 修改UserRealm配置类
package com.kuang.config;

import com.kuang.pojo.User;
import com.kuang.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

// 自定义UserRealm, 继承AuthorizingRealm类
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 1.授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        return null;
    }
    
    /**
     * 2.认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>授权doGetAuthenticationInfo");
        /** 
         * 2.1 没有连接数据库
         */
        // 2.1.1 设置用户名和密码
        // 需要从数据库中取这里没有连接数据库,所以我们使用伪造数据
        String username = "root";
        String password = "123456";
        // 2.1.2 获取用户认证信息
        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
        // 2.1.3 判断用户名是否与数据库中用户名相同
         if (!userToken.getUsername().equals(username)) {
            // 如果不同,就抛出异常UnknownAccountException
            return null;
        }
        // 2.1.4 密码认证,Shiro来做
        return new SimpleAuthenticationInfo("",password,"");
    }
    
}
3.页面访问测试
3-1 访问addUser.html新增页

在这里插入图片描述

结果访问新增页面后跳转到登录页!

3-2 访问updateUser修改页

在这里插入图片描述

结果访问修改页面后跳转到登录页!

3-3 用户登录测试
  • 输入正确的用户名和密码

在这里插入图片描述

  • 用户登录验证成功

在这里插入图片描述

  • 输入错误的用户名和密码

在这里插入图片描述

  • 用户登录验证失败

在这里插入图片描述


好了,今天的有关 SpringBoot基础学习之整合Shiro框架(上篇) 的学习就到此结束啦,欢迎小伙伴们积极学习和讨论,喜欢的可以给蜗牛君点个关注,顺便来个一键三连,我们下期见,拜拜啦!


参考视频链接:https://www.bilibili.com/video/BV1PE411i7CV(【狂神说Java】SpringBoot最新教程IDEA版通俗易懂)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂奔の蜗牛rz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值