Marco's Java【SpringBoot进阶(五) 之 SpringBoot集成Shiro及Mybatis】

前言

不知不觉咱们的SpringBoot已经更新到第十期了,结合之前的Shiro项目,再加上上一节学习的SpringBoot整合数据库的操作,咱们将这些知识点,再 “串一串” ,来个大串烧。
友情提示:如果没有阅览过下面两篇文章的朋友,建议先提前瞅瞅,否则很有可能跟本节的内容串接不起来哦!当然有这方面知识储备或者看过下面文章的朋友请接着往下看啦~
Marco’s Java【Shiro进阶(一) 之 Shiro+SMM集成Maven项目串烧篇(上)】
Marco’s Java【Shiro进阶(一) 之 Shiro+SMM集成Maven项目串烧篇(中)】
Marco’s Java【Shiro进阶(一) 之 Shiro+SMM集成Maven项目串烧篇(下)】
Marco’s Java【SpringBoot进阶(三) 之 SpringBoot数据源配置和自动管理】
Marco’s Java【SpringBoot入门(五) 之 Thymeleaf模板引擎的使用】
Marco’s Java【SpringBoot进阶(四) 之 启用Druid数据源分别集成JdbcTemplate及Mybatis】
Marco’s Java【小工具篇 之 lombok的使用】

SpringBoot集成Mybaits

第一步:创建Springboot项目
首先在Available中依次搜索下方我们勾选的内容,如Lombok,Thymeleaf等,导入之后就不需要再单独在pom.xml中配置它们的starter启动装置了。
在这里插入图片描述
第二步:添加项目依赖jar包
除了上面我们在项目初创的时候引入的一些statrer,还需要手动的引入下方的依赖配置,因为SpringBoot中默认没有提供这些启动装置。

<!-- 引入druid的依赖 -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid-spring-boot-starter</artifactId>
	<version>1.1.18</version>
</dependency>

<!-- pagehelper-spring-boot-starter -->
<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.2.12</version>
</dependency>

<!-- 引入shiro依赖 -->
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.4.1</version>
</dependency>
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.1</version>
</dependency>
<!--shrio和thymeleaf集成的扩展依赖,为了能在页面上使用xsln:shrio的标签 -->
<dependency>
	<groupId>com.github.theborakompanioni</groupId>
	<artifactId>thymeleaf-extras-shiro</artifactId>
	<version>2.0.0</version>
</dependency>

第三步:配置数据源并集成Mybatis配置
接着咱们再配置数据源,数据源的配置在博文 Marco’s Java【SpringBoot进阶(四) 之 启用Druid数据源分别集成JdbcTemplate及Mybatis】 中已经讲到过,在这里就不过多赘述了。

spring:
  datasource:
    druid: 
      url: jdbc:mysql://127.0.0.1:3306/shiro?serverTimezone=UTC
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: password
      max-active: 20
      initial-size: 5
      max-wait: 5000
      validation-query: select 'x' #配合定期检查的sql,每隔一段时间检查sql是否正常
      keep-alive: true #定期检查开启
      enable: true #启用,默认为false
      ## 监控页面的配置
      filters: stat
      stat-view-servlet: 
        login-username: root
        login-password: password
        allow: 
        deny: 
        enabled: true  #启用,默认为false
        url-pattern: /druid/*
      web-stat-filter:
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
        enabled: true #启用,默认为false
        profile-enable: true
        session-stat-enable: true
        principal-session-name: USER_SESSION
        principal-cookie-name: USER_COOKIE
        url-pattern: /druid/*

#集成mybatis 
mybatis:
  #注入log日志配置,打印sql
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  #注入xml的配置
  mapper-locations:
  - 'classpath:mapper/*Mapper.xml'

第四步:创建项目下所有的包及类
这里的类全部搬迁自Shiro进阶(一) 之 Shiro+SMM集成Maven项目串烧篇中,因此没有变化的代码我就不重复发啦,大家可以去之前的博文中拷贝。
在这里插入图片描述

SpringBoot集成Shiro

第五步:创建Shrio的自动配置类ShiroAutoConfig
接下来的这一步才是我们的重中之重,虽然Shiro帮我们提供了启动装置,但是依然需要我们自行创建配置类,并注入相应的配置值,以下的操作就是将我们之前创建的application-shiro.xml、springmvc.xml以及web.xml中的关于shiro的配置复刻过来(shiro的xml配置请参考之前的博文),唯一多出来的配置就是ShiroDialect shiroDialect()这一部分,主要是为了能在html页面引用shiro标签,具体是啥标签咱们后面会给大家演示。

package com.marco.config;

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

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.marco.realms.UserRealm;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import lombok.Data;

/**
 * shiro自动配置
 * @author Marco
 *
 */

@Data
@Configuration
@ConfigurationProperties(value="shiro")
public class ShiroAutoConfig {

	/**
	 * 定义的所有Bean的名称,并声明为常量
	 */
	private static final String SHIRO_DIALECT = "shiroDialect";
	private static final String SHIRO_FILTER = "shiroFilter";
	private static final String SECURITY_MANAGER = "securityManager";
	private static final String USER_REALM = "userRealm";
	private static final String CREDENTIALS_MATCHER = "credentialsMatcher";
	/**
	 * 以下所有的成员属性值全部从application.yml文件中获取,配置头为"shiro"
	 */
	private String hashAlgorithmName;
	private int hashIterations;
	private String loginUrl;
	private String unauthorizedUrl;
	private String[] anonUrl;
	private String[] authcUrl;
	private String logoutUrl;
	
	/**
	 * 创建凭证匹配器
	 * @return
	 */
	@Bean(CREDENTIALS_MATCHER)
	public HashedCredentialsMatcher getHashedCredentialsMatcher() {
		HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
		credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);//注入算法
		credentialsMatcher.setHashIterations(hashIterations);//注入散列次数
		return credentialsMatcher;
	}
	
	/**
	 * 创建自定义领域对象
	 * @param credentialsMatcher :需要依赖credentialsMatcher
	 * @return
	 */
	@Bean(USER_REALM)
	public UserRealm getUserRealm(HashedCredentialsMatcher credentialsMatcher) {
		UserRealm userRealm = new UserRealm();
		userRealm.setCredentialsMatcher(credentialsMatcher);//注入credentialsMatcher
		return userRealm;
	}
	
	/**
	 * 创建安全管理器
	 * @param userRealm
	 * @return
	 */
	@Bean(SECURITY_MANAGER)
	public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(userRealm);//注入自定义Realm
		return securityManager;
	}
	
	/**
	 * 创建Shiro的过滤器链
	 * @param securityManager
	 * @return
	 */
	@Bean(SHIRO_FILTER)
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();//创建shiro的过滤器工厂
		shiroFilterFactoryBean.setSecurityManager(securityManager);//注入securityManager
		shiroFilterFactoryBean.setLoginUrl(loginUrl);//设置用户登录的url地址
		shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);//设置认证失败的跳转地址
		Map<String, String> filterChain = new HashMap<>();
		/*
         * anon:表示可以匿名使用。 
           authc:表示需要认证(登录)才能使用,没有参数 
           roles:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/user/query/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。 
           perms:参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/user/update/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 
           rest:根据请求的方法,相当于/user/query/**=perms[user:method] ,其中method为post,get,delete等。 
           port:当请求的url的端口不是8082时跳转到schemal://serverName:8081?requestPath,其中schmal是协议http或https等,serverName是你访问的host,8082是url配置里port的端口,requestPath就是你想访问的具体哪个资源路径
           authcBasic:没有参数表示httpBasic认证 
           ssl:表示安全的url请求,协议为https 
           user:当登入操作时不做检查
         */
		//注入匿名放行的路径
		if(anonUrl != null && anonUrl.length > 0) {
			for (String url : anonUrl) {
				filterChain.put(url, "anon");
			}
		}
		//注入拦截的路径
		if(authcUrl != null && authcUrl.length > 0) {
			for (String url : authcUrl) {
				filterChain.put(url, "authc");
			}
		}
		//注入退出的路径
		filterChain.put(logoutUrl, "logout");
		//将我们的过滤器链注册到SpringBoot的Filter中
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChain);
		return shiroFilterFactoryBean;
	}
	
	/**
	 * 加入shiro注解的使用
	 * @param securityManager
	 * @return
	 */
	@Bean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		return defaultAdvisorAutoProxyCreator;
	}
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}
	
	/**
	 * 注册shiro的委托过滤器,相当于之前在web.xml里面配置的部分
	 * @return
	 */
	@Bean
	public FilterRegistrationBean<DelegatingFilterProxy> getFilterRegistrationBean() {
		FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>();//创建过滤器注册器
		DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy();//创建过滤器代理类
		delegatingFilterProxy.setTargetFilterLifecycle(true);//设置shiro在SpringBoot中的生命周期范围
		delegatingFilterProxy.setTargetBeanName(SHIRO_FILTER);//注入shiro过滤器链
		registrationBean.setFilter(delegatingFilterProxy);//注册过滤器
		return registrationBean;
	}
	
	// 这里是为了能在html页面引用shiro标签,上面两个函数必须添加,不然会报错
	@Bean(SHIRO_DIALECT)
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}
}

getFilterRegistrationBean()方法就是对照着我们之前在web.xml的如下配置,大家可以对比着看看。
在这里插入图片描述
关于shiro注解的配置的分析:
这里咱们再单独提一下关于shiro注解的配置,具体配置如下
在这里插入图片描述
然后咱们再对比一下之前的这一部分的配置,发现好像少了一部分?<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean> 这个配置好像没有用到?
在这里插入图片描述
这样,我们把LifecycleBeanPostProcessor的配置加上再试着运行看看会有什么效果
在这里插入图片描述
发现抛出Caused by: java.lang.NullPointerException: chainName cannot be null or empty这么个异常,说chain(过滤器链名)不能为空?可是我们过滤器链不是已经命名为shiroFilter了么?为了解决咱们的困惑,我来debug给大家分析一下,究竟是怎么回事儿~
在这里插入图片描述
大家注意看,我们的程序都debug到return shiroFilterFactoryBean这个部分了,但是ShiroFilterFactoryBean对象中的值一个都没有注入进来?这是什么原因呢?
在这里插入图片描述
其实说来说去就是我们配置的LifecycleBeanPostProcessor在搞鬼!
根本的原因是在org.apache.shiro.spring.config包中Spring默认的帮我们整合了LifecycleBeanPostProcessor,当SpringBoot启动的时候,会默认创建这么个类,并且这个类不应该配置在我们ShiroAutoConfig中,因为LifecycleBeanPostProcessor的作用更像是一个 “工具” ,而不是配置!由于IoC容器中已经创建了LifecycleBeanPostProcessor对象,当我们再次去创建一个新的对象时,会覆盖之前的对象,我们知道LifecycleBeanPostProcessor是掌管Bean的生命周期的,被你这么一覆盖,之前的配置是不是都会丢失了?
在这里插入图片描述
从源码上分析也能看出来我们创建新的LifecycleBeanPostProcessor之后,之前的LifecycleBeanPostProcessor走销毁流程,之前的数据也会随之清空,大家可以测试一下,当我们没有创建新的LifecycleBeanPostProcessor时,项目启动是不会走这一步的!
在这里插入图片描述
因此我们在配置的时候,是不用自己去创建LifecycleBeanPostProcessor对象的!
第六步:修改index页面
此次的index页面和之前在Marco’s Java【Shiro进阶(一) 之 Shiro+SMM集成Maven项目串烧篇(中)】 中的页面有所区别,因为我们这次使用的是thymeleaf模板引擎,是不是比之前简洁了很多?

<!DOCTYPE html>
<html  lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>系统首页</title>
</head>
<body>
	系统首页
	<hr>
		<a href="/user/query" shiro:hasPermission="user:query">查询用户</a>
		<br>
		<a href="/user/add" shiro:hasPermission="user:query">添加用户</a>
		<br>
		<a href="/user/update" shiro:hasPermission="user:update">修改用户</a>
		<br>
		<a href="/user/delete" shiro:hasPermission="user:delete">删除用户</a>
		<br>
		<a href="/user/export" shiro:hasPermission="user:export">导出用户</a>
		<br>
</body>
</html>

第七步:修改yml添加shiro配置
接下来就是咱们shiro集成收尾的一步了,通过这种将配置类和具体的配置分离开的方式,可以更加灵活的对我们的权限管理进行操作。

shiro:
  anon-url: #匿名访问路径
  - /index.html
  - /login/toLogin*
  - /login/login*
  logout-url: /login/logout #登出
  login-url: /login/toLogin #登录
  authc-url: #登录拦截路径
  - /**
  unauthorized-url: /unauthorized.html #未授权
  hash-algorithm-name: md5 #算法名称
  hash-iterations: 2 #散列次数  

第七步:代码测试
咱们先登录 http://127.0.0.1:8080/ 测试看看。发现自动跳转到用户登录页面了,证明我们的登录拦截生效了!
在这里插入图片描述
账号密码测试都是正常的
在这里插入图片描述
然后输入正确的账号密码,就会进入到下方界面,因为我没有export权限(之前的博文有测试过),因此证明权限也没有问题!
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值