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

编程入门 行业动态 更新时间:2024-10-08 18:33:16

SpringBoot基础学习之整合Shiro框架(<a href=https://www.elefans.com/category/jswz/34/1765805.html style=上篇)"/>

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集成缓存

官方网址:/

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的核心资源依赖 --><!-- .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.apache=WARN# Spring框架的日志
log4j.logger.springframework=WARN# 默认的Shiro日志
log4j.logger.apache.shiro=INFO# 不启用冗长日志
log4j.logger.apache.shiro.util.ThreadContext=WARN
log4j.logger.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 获取当前的用户对象SubjectSubject currentUser = SecurityUtils.getSubject();/** * 2.2 存取用户的session信息*/// 2.2.1 通过当前用户获取sessionSession 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 获取当前的用户对象SubjectSubject currentUser = SecurityUtils.getSubject();/*** 2.2 存取用户的session信息*/// 2.2.1 通过当前用户获取sessionSession 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的资源依赖 -->
<!-- .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的资源依赖 --><!-- .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=;
<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容器中去@Beanpublic 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 {/** * 授权*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");return null;}/** * 认证*/@Overrideprotected 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=;
<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容器中去@Beanpublic 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 {@Autowiredprivate UserService userService;/*** 1.授权*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");return null;}/*** 2.认证*/@Overrideprotected 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)) {// 如果不同,就抛出异常UnknownAccountExceptionreturn null;}// 2.1.4 密码认证,Shiro来做return new SimpleAuthenticationInfo("",password,"");}}
3.页面访问测试
3-1 访问addUser.html新增页

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

3-2 访问updateUser修改页

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

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

  • 用户登录验证成功

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

  • 用户登录验证失败


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


参考视频链接:(【狂神说Java】SpringBoot最新教程IDEA版通俗易懂)

更多推荐

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

本文发布于:2024-03-12 13:01:08,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1731582.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:上篇   框架   基础   SpringBoot   Shiro

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!