本人第一次写博客,其中有问题的地方希望大家指正。
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
##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
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;
}
}
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没有解决希望大神们指导一波。因为本人也是菜鸟,就没有什么源码讲解,主要是如何配置使用。写的时候参考了好多网上博客的内容,谢谢大神们的分享。
项目下载地址点击打开链接