1.先把springboot和mybatis整合好,然后整合jsp或者thymeleaf。这里用jsp
2.pom:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lyh</groupId>
<artifactId>shiro_springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro_springboot</name>
<description>Demo project for Spring Boot</description>
<packaging>war</packaging>
<properties>
<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>
<!--最新版本,匹配spring Boot1.5 or higher-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<!--JavaServer Pages Standard Tag Library,JSP标准标签库-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!--内置tocat对Jsp支持的依赖,用于编译Jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!--<scope>provided</scope>-->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3.springboot的application.properties配置文件
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url = jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
#指定bean所在包
mybatis.type-aliases-package=com.lyh.shiro_springboot.domain
#指定映射文件
mybatis.mapper-locations=classpath:mapper/*.xml
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
4.自定义realm
import com.lyh.shiro_springboot.dao.UserMapper;
import com.lyh.shiro_springboot.domain.User;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserMapper userMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user=(User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if(user.getRoles()!=null && !user.getRoles().equals("")){
info.addRole(user.getRoles());
}
if(user.getPermission()!=null && !user.getPermission().equals("")){
info.addStringPermission(user.getPermission());
}
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = authenticationToken.getPrincipal().toString();
User userByName = userMapper.getUserByName(username);
ByteSource salt = ByteSource.Util.bytes(userByName.getPassword_salt());
return new SimpleAuthenticationInfo(userByName,userByName.getPassword(),salt,getName());
}
}
5.shiro的配置类编写代码
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionManager;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
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.web.servlet.handler.SimpleMappingExceptionResolver;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@Configuration
public class shiroConfig {
//配置shiro的具体拦截器
@Bean(name="shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
Map<String, String> fMap = new HashMap<>();
//拦截页面
fMap.put("/gologin", "anon");
fMap.put("/tologin", "anon");
fMap.put("/logout", "logout");
fMap.put("/**", "user");
shiroFilterFactoryBean.setLoginUrl("/gologin");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/goerror");
shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap);
return shiroFilterFactoryBean;
}
//创建DefaultWebSecurityManager
@Bean(name="defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm,@Qualifier("sessionManager")SessionManager sessionManager,@Qualifier("rememberMeManager")CookieRememberMeManager cookieRememberMeManager){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);//设置自定义realm
defaultWebSecurityManager.setSessionManager(sessionManager);//设置session管理器
defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);//设置记住我功能
return defaultWebSecurityManager;
}
//设置shiro的session配置
@Bean(name = "sessionManager")
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionIdCookieEnabled(true);
return sessionManager;
}
//凭证匹配器
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
return hashedCredentialsMatcher;
}
//自定义realm
@Bean(name = "userRealm")
public UserRealm getRealm(@Qualifier("hashedCredentialsMatcher")HashedCredentialsMatcher hashedCredentialsMatcher){
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(hashedCredentialsMatcher);//把加密算法的凭证器设置进realm
return userRealm;
}
//开启shiro aop注解支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
//不加这个注解不生效,具体不详
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//生命周期管理(可以不管)
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean(name = "rememberMeCookie")
public SimpleCookie rememberMeCookie() {
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//如果httyOnly设置为true,则客户端不会暴露给客户端脚本代码,使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击;
simpleCookie.setHttpOnly(true);
//记住我cookie生效时间,单位是秒
simpleCookie.setMaxAge(600);
return simpleCookie;
}
//cookie管理器
@Bean(name = "rememberMeManager")
public CookieRememberMeManager rememberMeManager(@Qualifier("rememberMeCookie")SimpleCookie simpleCookie) {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//rememberme cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位),通过以下代码可以获取
//KeyGenerator keygen = KeyGenerator.getInstance("AES");
//SecretKey deskey = keygen.generateKey();
//System.out.println(Base64.encodeToString(deskey.getEncoded()));
byte[] cipherKey = Base64.decode("wGiHplamyXlVB11UXWol8g==");
cookieRememberMeManager.setCipherKey(cipherKey);
cookieRememberMeManager.setCookie(simpleCookie);
return cookieRememberMeManager;
}
//异常处理配置
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "/goerror");//数据库异常处理
mappings.setProperty("UnauthorizedException","/goerror");//授权异常
mappings.setProperty("UnauthenticatedException","/goerror");//认证异常
r.setExceptionMappings(mappings); // None by default
return r;
}
}
6.controller
import com.lyh.shiro_springboot.dao.UserMapper;
import com.lyh.shiro_springboot.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class shiroController {
@Autowired
UserMapper userMapper;
@RequestMapping("test")
@RequiresRoles("admin")
public ModelAndView testThyleaf(){
List<Map<String,String>> lst = new ArrayList<>();
Map<String,String> map1=new HashMap<>();
map1.put("key1","value1");
map1.put("key2","value2");
map1.put("key3","value3");
Map<String,String> map2=new HashMap<>();
map2.put("key1","value1");
map2.put("key2","value2");
map2.put("key3","value3");
Map<String,String> map3=new HashMap<>();
map3.put("key1","value1");
map3.put("key2","value2");
map3.put("key3","value3");
lst.add(map1);
lst.add(map2);
lst.add(map3);
ModelAndView modelAndView = new ModelAndView("index");
modelAndView.addObject("list",lst);
return modelAndView;
}
@RequestMapping("gologin")
public ModelAndView gologin(){
return new ModelAndView("login");
}
@RequestMapping("tologin")
public ModelAndView tologin(String userName, String passwd, String rememberMe){
ModelAndView modelAndView = new ModelAndView();
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(userName, passwd);
if (rememberMe != null){
if (rememberMe.equals("on")) {
//说明选择了记住我
token.setRememberMe(true);
} else {
token.setRememberMe(false);
}
}else{
token.setRememberMe(false);
}
try {
subject.login(token);
} catch (Exception e) {
modelAndView.addObject("msg", "用户名或密码错误!");
modelAndView.setViewName("login");
return modelAndView;
}
User user = userMapper.getUserByName(userName);
Session session = subject.getSession();
session.setAttribute("user",user);
modelAndView.setViewName("index");
return modelAndView;
}
@RequestMapping("goerror")
public ModelAndView error(){
return new ModelAndView("error");
}
@RequestMapping("logout")
public ModelAndView logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return gologin();
}
}
7.shiro集成ehcache缓存
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
</dependency>
上述shiro-all的pom文件里面已经包含shiro支持ehcache和ehcache本身的所有jar包
创建ehcache.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="java.io.tmpdir"/>
<defaultCache
eternal="false"
maxElementsInMemory="1000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="0"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
<!--ehcache.xml 文件配置详解-->
<!--部分资料来源于网络-->
<!--diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。-->
<!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。-->
<!--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:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。-->
<!--FIFO,first in first out,先进先出-->
<!--LFU, Less Frequently Used,一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 -->
<!--LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。-->
编写shiro的配置类
//创建DefaultWebSecurityManager
@Bean(name="defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm,@Qualifier("sessionManager")SessionManager sessionManager,@Qualifier("rememberMeManager")CookieRememberMeManager cookieRememberMeManager){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);//设置自定义realm
defaultWebSecurityManager.setSessionManager(sessionManager);//设置session管理器
defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);//设置记住我功能
defaultWebSecurityManager.setCacheManager(ehCacheManager());//设置缓存
return defaultWebSecurityManager;
}
//ehcache缓存管理器
@Bean
public EhCacheManager ehCacheManager() {
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
}
8.解决通过记住我方式登录后没有session的问题
解决办法我们自己写一个拦截器,验证当通过记住我方式登录后,把session加进去.
SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer
import com.lyh.shiro_springboot.interceptor.RememberMeInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfg implements WebMvcConfigurer {
/**
* @param registry 添加拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
InterceptorRegistration interceptorRegistration = registry.addInterceptor(new RememberMeInterceptor());
//排除配置
interceptorRegistration.excludePathPatterns("/gologin");
interceptorRegistration.excludePathPatterns("/tologin");
interceptorRegistration.excludePathPatterns("/goerror");
interceptorRegistration.excludePathPatterns("/logout");
//配置拦截策略
interceptorRegistration.addPathPatterns("/**");
}
}
定义拦截器 implements HandlerInterceptor
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.lyh.shiro_springboot.dao.UserMapper;
import com.lyh.shiro_springboot.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 拦截rememberMe的请求,添加user到session中
* @author echo
*
*/
public class RememberMeInterceptor implements HandlerInterceptor {
@Autowired
UserMapper userMapper;
public RememberMeInterceptor() {
// TODO Auto-generated constructor stub
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("测试方法进入preHandle");
// 获取session中的subject
Subject subject = SecurityUtils.getSubject();
// 判断是不是通过记住我登录
if( !subject.isAuthenticated() && subject.isRemembered()) {
Session session = subject.getSession();
User user= (User)subject.getPrincipal();
session.setAttribute("user",user);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
注:
三个重载方法说明
preHandle:调用Controller某个方法之前
postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理
网友评论