springBoot学习 整合mvc,mybatis ,shiro 缓存使用redis (-)

  本人第一次写博客,其中有问题的地方希望大家指正。

     springboot基础安装就不说了,网上太多了。

先贴下pom.xml  后面就不说具体引入哪个了。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>test2</groupId>
	<artifactId>test2</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>test2</name>
	<url>http://maven.apache.org</url>

	<!-- spring boot parent节点,引入这个之后,在下面和spring boot相关的就不需要引入版本了; -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
	</parent>

	<properties>
		<!-- 指定一下jdk的版本 ,这里我们使用jdk 1.8 ,默认是1.6 -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>




	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-redis</artifactId>
		    <version>1.2.8.RELEASE</version>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional><!-- optional=true,依赖不会传递,该项目依赖devtools;之后依赖该项目的项目如果想要使用devtools,需要重新引入 -->
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<!-- servlet 依赖. -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>

		<!-- tomcat 的支持. -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.0</version>
		</dependency>

		<dependency>
    		<groupId>org.apache.shiro</groupId>
    		<artifactId>shiro-spring</artifactId>
    		<version>1.4.0</version>
		</dependency>

	<dependency>
		<groupId>org.apache.shiro</groupId>
		<artifactId>shiro-ehcache</artifactId>
		<version>1.2.3</version>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-cache</artifactId>
	</dependency>
	
	</dependencies>
	 <build>
        <plugins>
            <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                  <!--fork :  如果没有该项配置则devtools不会起作用,即应用不会restart -->
                <fork>true</fork>
            </configuration>
            </plugin>
        </plugins>
    </build>
</project>

1.整合mvc

在application.properties配置

##mvc配置spring.mvc.view.prefix=/WEB-INF/jsp/spring.mvc.view.suffix=.jspspring.mvc.static-path-pattern=/static/**spring.resources.static-locations=/WEB-INF/static/

资源目录结构是

因为本人习惯使用jsp,所以没有使用官方推荐的thymeleaf 感觉都是模板引擎技术为什么不用熟悉的,既然都使用

了jsp那目录结构也用webapp了。

2.整合mybatis

##mybatis.typeAliasesPackage=com.**.model  深坑  不能使用通配只好改成全名mybatis.mapperLocations=classpath:mybatis/mapper/**/*.xmlmybatis.configLocations=classpath:mybatis/mybatis-config.xml

这里有个问题就是在配置文件里配置别名,不能使用通配符不知道为什么。接下mybatis全局配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 参数设置 -->
	  <!-- 设置运行参数 -->  
    <settings>  
        <!-- 全局映射器启用缓存 -->  
        <setting name="cacheEnabled" value="true" />  
        <!-- 查询时,关闭关联对象及时加载以提高性能 -->  
        <setting name="lazyLoadingEnabled" value="false" />  
        <!-- 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指定),不会加载关联表的所有字段,以提高性能 -->  
        <setting name="aggressiveLazyLoading" value="false" />  
        <!-- 对于位置的SQL查询,允许返回不同的结果集以达到通用的效果 -->  
        <setting name="multipleResultSetsEnabled" value="true" />  
        <!-- 允许使用列标签代替列明 -->  
        <setting name="useColumnLabel" value="true" />  
        <!-- 允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值), 数据表的pk生成策略将被覆盖 -->  
        <setting name="useGeneratedKeys" value="true" />  
        <!-- 给予被嵌套的resultMap以字段-属性的映射支持 -->  
        <setting name="autoMappingBehavior" value="PARTIAL" />  
        <!-- 对于批量更新操作缓存SQL以提高性能 -->  
        <setting name="defaultExecutorType" value="REUSE" />  
        <!-- 数据库超过25000秒仍未响应则超时 -->  
        <setting name="defaultStatementTimeout" value="25000" />  
        <!-- 打印查询语句 -->  
       <!--  <setting name="logImpl" value="STDOUT_LOGGING" />  --> 
    </settings> 
</configuration>  



这里遇到了一个问题,这里关联对象延迟加载无效,找了好久都没有解决,大神看到了指导一下。

在springmvc我们一般通过映射接口注入到org.mybatis.spring.mapper.MapperScannerConfigurer类bean中basePackage属性

中。而springboot中只需要在启动上加一行注解就可以了

package com;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
@EnableTransactionManagement 
@MapperScan("com.**.dao")
public class App {
	
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

这样与mybatsi整合就完成了。

3.整合shiro

学习shiro推荐看这篇博文 , 感觉写的挺好的
http://jinnianshilongnian.iteye.com/blog/2018936/
刚开始整合shiro使用的是shiro官网上和springboot整合的例子。但是官网上的内容太少了,出了bug一直解决不了,所以还是使用shiro-spring的方式整合进去的。
官网例子 是http://shiro.apache.org/spring-boot.html
首先配置real,这个是最重要的实现。
package com.base.shiro;

import java.util.List;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.context.annotation.Lazy;

import com.system.model.Role;
import com.system.model.User;
import com.system.service.UserService;
/**
 * shiro验证类
 * @author gl 
 * @time  2018-01-09
 */
public class SystemUserReal extends AuthorizingRealm  {
	
	//@lazy这个注解为了解决缓存问题  应为shiro的bean在rediscach之前注入  导致springboot会默认吧ehcache注入到cache  导致redis缓存配置无效的情况
	//好像通过改变注入顺序  advice实现  具体源码不懂  
	@Resource
	@Lazy
	private UserService userService;
	
	//权限认证
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
	     SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		  User user= (User) SecurityUtils.getSubject().getPrincipal();
		  User newUser=userService.queryByUserName(user.getUsername());
		  Role role=newUser.getRole();
		  if(role!=null){
			  List<com.system.model.Resource> resourceList=role.getList();
			  // 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
			  for (com.system.model.Resource resource : resourceList) {
				  info.addStringPermission(resource.getResourceUrl());
			}
		  }
	  
		return info;
	}
	
	//身份认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		//获取登录时的用户输入的账号密码
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;
		//此处的username字段一定要命名为username  因为登录时自动调用sbuject的login()方法  此时token中username字段自动把前端提交数据用户名set进去
		//如果不相同则用户名取出来的是空的  不过可以自定义令牌(暂时不知道怎么搞  后续补上) 可以通过重写formAuthenticationFilter实现
		String username = (String)upToken.getPrincipal();
		String password=new String( upToken.getPassword());
		//根据用户名获取数据库中的用户信息
		User dUser=userService.queryByUserName(username);
		if(dUser.getPassword().equals(password)){//此处省略密码加密验证 可自定义密码验证类  再返回info时shiro框架会自动进行密码验证
			//设置用户信息session   shiro中的session
			 	Session session = SecurityUtils.getSubject().getSession();
		        session.setAttribute("userSession", dUser);
		        session.setAttribute("userSessionId", dUser.getId());
			return new SimpleAuthenticationInfo(dUser, password, dUser.getUsername());//此处应是真正用户名称 不是账号 此处省略 后续补上
		}
		return null;
	}

}



real中主要实现权限认证,和登录认证。
接下来是shiroconfig  
package com.base.config;

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

import javax.annotation.Resource;
import javax.servlet.Filter;

import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.util.StringUtils;

import com.base.shiro.MyFormAuthenticationFilter;
import com.base.shiro.SystemUserReal;
import com.system.dao.ResourceDao;
/**
 * 
 * @author gl
 *遇到的神坑  把@Configuration写成了@Configurable  结果配置无效  找了三天多才发现    我的天啊
 *
 */


@Configuration
public class ShiroConfig {

	
	@Resource
	private ResourceDao resourceDao;
	
	
	// 保证实现了Shiro内部lifecycle函数的bean执行 
	//在此重点说明这个方法,如果不设置为静态方法会导致bean对象无法注入进来,
	   @Bean(name = "lifecycleBeanPostProcessor")
	    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
	        return new LifecycleBeanPostProcessor();
	    }
	  
	    @Bean(name="ehCacheManager")
	    public EhCacheManager ehCacheManager() {  
	        EhCacheManager em = new EhCacheManager();  
	        em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");  
	        return em;  
	    } 
	   
	   
	  	@Bean(name="systemUserReal")
	    public  SystemUserReal systemUserReal(){  
	  		SystemUserReal realm = new SystemUserReal(); 
	  	   //启用缓存,默认false
	  		realm.setCachingEnabled(true);
	  	    //  启用身份验证缓存,即缓存AuthenticationInfo信息,默认false;
	  		realm.setAuthenticationCachingEnabled(true);
	  	    //  缓存AuthenticationInfo信息的缓存名称,即配置在ehcache.xml中的cache name
	  		realm.setAuthenticationCacheName("authenticationCache");
	  	    //  启用授权缓存,即缓存AuthorizationInfo信息,默认false;
	  		realm.setAuthorizationCachingEnabled(true);
	  	    //  缓存AuthorizationInfo信息的缓存名称;
	  		realm.setAuthorizationCacheName("authorizationCache");
	  		return realm;
	    }  
	  	
		@Bean(name="securityManager")
	    public SecurityManager  securityManager(@Qualifier("systemUserReal") SystemUserReal authRealm){
	        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
	        //设置realm
	        securityManager.setRealm(authRealm);
	        securityManager.setCacheManager(ehCacheManager());
	        return securityManager;
	    }
		
//	    @Bean(name="myFormAuthenticationFilter")
//	    public MyFormAuthenticationFilter myFormAuthenticationFilter(){
//	    	return new MyFormAuthenticationFilter();
//	    }
	    
		
	    @Bean(name="shirFilter")
	    public  ShiroFilterFactoryBean  shirFilter(@Qualifier("securityManager") SecurityManager securityManager){
	        System.out.println("**************shirFilter**************");
	        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
			shiroFilterFactoryBean.setSecurityManager(securityManager);
	        shiroFilterFactoryBean.setLoginUrl("/login");
	        // 登录成功后要跳转的链接
	        shiroFilterFactoryBean.setSuccessUrl("/index");
	        //未授权界面
	        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
	        Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters();
	        //限制相同账号重复登录问题
	        filtersMap.put("authc",new MyFormAuthenticationFilter());
	      	shiroFilterFactoryBean.setFilters(filtersMap);
	      
	        //拦截器
	        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();

	        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
	        filterChainDefinitionMap.put("/logout", "logout");
	        filterChainDefinitionMap.put("/static/**","anon");
	        //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
	        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
	        //自定义加载权限资源关系
	        List<com.system.model.Resource> resourcesList = resourceDao.queryAll();
	         for(com.system.model.Resource resource:resourcesList){
	            if (!StringUtils.isEmpty(resource.getResourceUrl())){
	                String permission = "perms[" + resource.getResourceUrl()+ "]";
	                filterChainDefinitionMap.put(resource.getResourceUrl(),permission);
	            }
	        }
	         filterChainDefinitionMap.put("/**", "authc");


	        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	        return shiroFilterFactoryBean;
	    }
	   
	    
	    /**
	     *  开启shiro aop注解(RequiresRoles等)支持  Controller才能使用@RequiresPermissions
	     *  使用代理方式;所以需要开启代码支持;
	     * @param securityManager
	     * @return
	     */
	    @Bean
	    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")SecurityManager securityManager){
	        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
	        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
	        return authorizationAttributeSourceAdvisor;
	    }
	    
	    
	    @Bean 
	    @DependsOn({"lifecycleBeanPostProcessor"})
		public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
			DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); 
			daap.setProxyTargetClass(true); 
			return daap; 
		} 
	   
	    
	    

//	    @Bean
//	    protected CacheManager cacheManager() {
//	        return new MemoryConstrainedCacheManager();
//	    }

}

以上就是shiro的整合,说下遇到问题。最大的坑就是把配置类注解敲错了,简直晕了。然后就是自定义表单拦截器的注入问题,

不知道为毛用bean注入的方式一直报错只好改成new对象的方式注入了。

4.缓存的使用

这里使用了两个缓存。一个是ehCache作为shiro的缓存,一个是使用redis作为系统缓存,主要缓存查询信息的。

整合ehcache:

首先添加ehcache-shiro.xml配置文件,放到resource目录下。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">
    <diskStore path="java.io.tmpdir"/>
    <!--
       name:缓存名称。
       maxElementsInMemory:缓存最大数目
       maxElementsOnDisk:硬盘最大缓存个数。 
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。 
       overflowToDisk:是否保存到磁盘,当系统当机时
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. 
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 
       clearOnFlush:内存数量最大时是否清除。
       memoryStoreEvictionPolicy:
       Ehcache的三种清空策略;
          FIFO,first in first out,这个是大家最熟的,先进先出。
          LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
          LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    -->
    <cache name="authorizationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <cache name="authenticationCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>

    <cache name="shiro-activeSessionCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>



然后将ehcache注入bean中

    @Bean(name="ehCacheManager")
   public EhCacheManager ehCacheManager() {  
       EhCacheManager em = new EhCacheManager();  
       em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");  
       return em;  
   } 

再将缓存注入到securityManager中。

@Bean(name="securityManager")
   public SecurityManager  securityManager(@Qualifier("systemUserReal") SystemUserReal authRealm){
       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
       //设置realm
       securityManager.setRealm(authRealm);
       securityManager.setCacheManager(ehCacheManager());
       return securityManager;
   }

Shiro提供了CachingRealm,其实现了CacheManagerAware接口,提供了缓存的一些基础实现;另外AuthenticatingRealm及AuthorizingRealm分别提供了对AuthenticationInfo 和AuthorizationInfo信息的缓存。

接着整合redis:

package com.base.config;



import java.util.Arrays;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;



/**
 * @ClassName: RedisConfig
 * @Description: TODO
 * @author: gl
 * @date: 2018年1月17日 下午3:48:34
 */
@Configuration
@EnableCaching
public class RedisConfig {
	
	
	  @Bean(name="redisTemplate")
	    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
	        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
	        template.setConnectionFactory(factory);
//	        template.setKeySerializer(new StringRedisSerializer());
//	        template.setValueSerializer(new RedisObjectSerializer());
	        return template;
	    }
	
	//将redis缓存设置到spring缓存机制中    后续可以通过注解启动缓存
    @Bean(name="cacheManager")
    public CacheManager cacheManager(@Qualifier("redisTemplate")RedisTemplate<Object, Object> redisTemplate) {
        String[] cacheNames = {"app_default", "users", "blogs", "goods", "configs", "info"};
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate, Arrays.asList(cacheNames));
        redisCacheManager.setDefaultExpiration(18000);
        return redisCacheManager;
    }
    
  
    

    

  
}

然后再想使用缓存的地方使用@Cacheable等等缓存注解就可以了。遇到的最大问题就是shiro缓存和redis缓存同时使用时redis缓存无效,后面在网上百度出来答案是加@lazy注解,调整注入bean的顺序解决的。

以上就是全部内容了,还有好多bug没有解决希望大神们指导一波。因为本人也是菜鸟,就没有什么源码讲解,主要是如何配置使用。写的时候参考了好多网上博客的内容,谢谢大神们的分享。

项目下载地址点击打开链接










阅读更多

没有更多推荐了,返回首页