spring boot 整合 shiro 框架
1、整合shiro
1.1、创建spring boot项目
1.2、引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.9.0</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
1.3、添加配置文件
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shirodb?characterEncoding=utf-8&useSSL=false
username: root
password: zzybzb
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
shiro:
loginUrl: /myController/login
1.4、启动类添加扫描包注解
2、登录认证实现
2.1、库表
2.1.1、建表
CREATE DATABASE IF NOT EXISTS `shirodb` CHARACTER SET utf8mb4;
USE `shirodb`;
CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` VARCHAR(30) DEFAULT NULL COMMENT '用户名',
`pwd` VARCHAR(50) DEFAULT NULL COMMENT '密码',
`rid` BIGINT(20) DEFAULT NULL COMMENT '角色编号',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
2.1.2、密码加密,添加几条数据
public class ShiroMD5 {
public static void main(String[] args) {
String password = "123456";
//为了保证安全,避免被破解还可以多次迭代加密,保证数据安全
Md5Hash md53 = new Md5Hash(password,"salt",3);
System.out.println("md5带盐的3次加密:"+md53.toHex());
}
}
2.2、创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String name;
private String pwd;
private Integer rid;
}
2.3、创建 mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2.4、创建 service
public interface UserService {
//用户登录
User getUserInfoByName(String name);
}
@Service
public class UserServiceImpl implements UserService {
private UserMapper userMapper;
@Autowired
public UserServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
//根据姓名查询
@Override
public User getUserInfoByName(String name) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name",name);
User user = userMapper.selectOne(wrapper);
return user;
}
}
2.5、自定义realm
@Component
public class MyRealm extends AuthorizingRealm {
private UserService userService;
@Autowired
public MyRealm(UserService userService){
this.userService = userService;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//自定义登录认证方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1获取用户身份信息
String username = authenticationToken.getPrincipal().toString();
//2调用业务层获取用户信息(数据库)
User user = userService.getUserInfoByName(username);
//3非空判断,将数据封装返回
if (user != null){
AuthenticationInfo info = new SimpleAuthenticationInfo(
authenticationToken.getPrincipal(),
user.getPwd(),
ByteSource.Util.bytes("salt"),
authenticationToken.getPrincipal().toString()
);
return info;
}
return null;
}
}
2.6、配置类
@Configuration
public class ShiroConfig {
private MyRealm myRealm;
@Autowired
public ShiroConfig(MyRealm myRealm){
this.myRealm = myRealm;
}
//配置SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//1创建defaultWebSecurityManager 对象
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//2创建加密对象,设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
//2.1采用md5加密
matcher.setHashAlgorithmName("MD5");
//2.2迭代加密次数
matcher.setHashIterations(3);
//3将加密对象存储到myRealm中
myRealm.setCredentialsMatcher(matcher);
//4将myRealm存入defaultWebSecurityManager 对象
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
//配置Shiro内置过滤器拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
//设置不认证可以访问的资源
definition.addPathDefinition("/my/userLogin","anon");
definition.addPathDefinition("/my/login","anon");
//设置登出过滤器
definition.addPathDefinition("/logout","logout");
//设置需要进行登录认证的拦截范围
definition.addPathDefinition("/**","authc");
//添加存在用户的过滤器(rememberMe)
definition.addPathDefinition("/**","user");
return definition;
}
}
2.7、controller
@Controller
@RequestMapping("/my")
public class MyController {
@RequestMapping("/userLogin")
@ResponseBody
public String login(String username,String password){
//1获取subject对象
Subject subject = SecurityUtils.getSubject();
//2封装请求数据到token
AuthenticationToken token = new UsernamePasswordToken(username,password);
//3调用login方法进行登录认证
try {
subject.login(token);
return "登录成功";
} catch (AuthenticationException e) {
e.printStackTrace();
return "登录失败";
}
}
}
2.8、访问,测试
2.9、实现前端页面
2.9.1、Thymeleaf依赖
dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.9.2、login 页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证</h1>
<br>
<form action="/my/userLogin">
<div>用户名:<input type="text" name="username" value=""></div>
<div>密码:<input type="password" name="password" value=""></div>
<div><input type="submit" value="登录"></div>
</form>
</body>
</html>
2.9.3、添加 index 页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>Shiro登录认证后主页面</h1>
<br>
登录用户为:<span th:text="${session.user}"></span>
</body>
2.9.4、添加 controller 方法,改造认证方法
//跳转登录页面
@RequestMapping("/login")
public String login(){
return "login";
}
2.9.5、修改配置文件
2.9.6、修改配置类
2.9.7、访问,测试
3、多个 realm 的认证策略设置
3.1、多个realm实现原理
当应用程序配置多个 Realm 时,例如:用户名密码校验、手机号验证码校验等等。
Shiro 的 ModularRealmAuthenticator 会使用内部的 AuthenticationStrategy 组件判断认证是成功还是失败。
AuthenticationStrategy 是一个无状态的组件,它在身份验证尝试中被询问 4 次(这4 次交互所需的任何必要的状态将被作为方法参数):
- 在所有 Realm 被调用之前
- 在调用 Realm 的 getAuthenticationInfo 方法之前
- 在调用 Realm 的 getAuthenticationInfo 方法之后
- 在所有 Realm 被调用之后
认证策略的另外一项工作就是聚合所有 Realm 的结果信息封装至一个
AuthenticationInfo 实例中,并将此信息返回,以此作为 Subject 的身份信息。
Shiro 中定义了 3 种认证策略的实现:
- AtLeastOneSuccessfulStrategy
只要有一个(或更多)的 Realm 验证成功,那么认证将视为成功。 - FirstSuccessfulStrategy
第一个 Realm 验证成功,整体认证将视为成功,且后续 Realm 将被忽略。 - AllSuccessfulStrategy
所有 Realm 成功,认证才视为成功。
ModularRealmAuthenticator 内置的认证策略默认实现是
AtLeastOneSuccessfulStrategy 方式。可以通过配置修改策略。
3.2、多个realm代码实现
//配置 SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//1 创建 defaultWebSecurityManager 对象
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//2 创建认证对象,并设置认证策略
ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator();
modularRealmAuthenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
//3 封装 myRealm 集合
List<Realm> list = new ArrayList<>();
list.add(myRealm);
list.add(myRealm2);
//4 将 myRealm 存入 defaultWebSecurityManager 对象
defaultWebSecurityManager.setRealms(list);
//5 返回
return defaultWebSecurityManager;
}
4、remember me 功能
Shiro 提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器,下次再打开时还是能记住你是谁, 下次访问时无需再登录即可访问。
4.1、基本流程
- 首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会 把 RememberMe 的 Cookie 写到客户端并保存下来。
- 关闭浏览器再重新打开;会发现浏览器还是记住你的。
- 访问一般的网页服务器端,仍然知道你是谁,且能正常访问。
- 但是,如果我们访问电商平台时,如果要查看我的订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。
4.2、代码实现
4.2.1、修改配置类
//cookie属性设置
public SimpleCookie rememberMeCookie(){
SimpleCookie cookie = new SimpleCookie("rememberMe");
//设置跨域
//cookie.setDomain(domain);
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30*24*60*60);
return cookie;
}
//创建Shiro的cookie管理对象
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
return cookieRememberMeManager;
}
defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
4.2.2、添加,修改 controller
添加
//登录认证验证 rememberMe
@GetMapping("userLoginRm")
public String userLogin(HttpSession session) {
session.setAttribute("user","rememberMe");
return "index";
}
修改
4.2.3、修改登录页面
<div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
4.3、访问测试
4.3.1、未勾选
- 登录
- 访问userLoginRm
- 关掉浏览器后打开,重新访问
4.3.2、勾选“记住我”
- 登录
- 关掉浏览器再访问
5、退出
用户登录后,配套的有登出操作。直接通过Shiro过滤器即可实现登出。
5.1、index页面
<a href="/logout">登出</a>
5.2、修改配置类
//设置登出过滤器
definition.addPathDefinition("/logout","logout");
5.3、测试
6、授权、角色认证
6.1、授权
用户登录后,需要验证是否具有指定角色指定权限。Shiro也提供了方便的工具进行判断。这个工具就是Realm的doGetAuthorizationInfo方法进行判断。
触发权限判断的有两种方式
- 在页面中通过shiro:****属性判断
- 在接口服务中通过注解@Requires****进行判断
6.2、后端接口服务注解
通过给接口服务方法添加注解可以实现权限校验,可以加在控制器方法上,也可以加在业务方法上,一般加在控制器方法上。常用注解如下:
- @RequiresAuthentication
验证用户是否登录,等同于方法subject.isAuthenticated() - @RequiresUser
验证用户是否被记忆:
登录认证成功subject.isAuthenticated()为true。
登录后被记忆subject.isRemembered()为true。 - @RequiresGuest
验证是否是一个guest的请求,是否是游客的请求。
此时subject.getPrincipal()为null。 @RequiresRoles
验证subject是否有相应角色,有角色访问方法,没有则会抛出异常AuthorizationException。
例如:@RequiresRoles(“aRoleName”)//只有subject有aRoleName角色才能访问方法someMethod()
void someMethod();@RequiresPermissions
验证subject是否有相应权限,有权限访问方法,没有则会抛出异常AuthorizationException。
例如:@RequiresPermissions (“file:read”,”wite:aFile.txt”)//subject必须同时含有file:read和wite:aFile.txt权限才能访问方法someMethod()
void someMethod();
6.3、授权验证-没有角色无法访问
6.3.1、controller代码
//登录认证验证角色
@RequiresRoles("admin")
@GetMapping("userLoginRoles")
@ResponseBody
public String userLoginRoles(){
System.out.println("登录认证验证角色");
return "验证角色成功";
}
6.3.2、index页面
<a shiro:hasRole="admin" href="/my/userLoginRoles">测试授权-角色验证</a>
6.3.3、修改MyRealm类的doGetAuthorizationInfo方法
6.3.4、测试,没有admin角色,会报错
6.4、授权验证-获取角色进行验证
6.4.1、修改 MyRealm 方法
自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//自定义添加角色
info.addRole("admin");
6.4.2、角色表,用户-角色中间表
CREATE TABLE `role` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` VARCHAR(30) DEFAULT NULL COMMENT '角色名',
`desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
`realname` VARCHAR(20) DEFAULT NULL COMMENT '角色显示名',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色表';
CREATE TABLE `role_user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`uid` BIGINT(20) DEFAULT NULL COMMENT '用户 id',
`rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色用户映射
表';
自定义数据–角色表
自定义数据–用户角色中间表
6.4.3、查询 sql
根据用户名查询对应角色信息
SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE
uid=(SELECT id FROM USER WHERE NAME='zhangsan'));
6.4.4、mapper 方法
@Select("SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE uid=(SELECT id FROM USER WHERE NAME=#{principal}))")
List<String> getUserRoleInfoMapper(@Param("principal")String principal );
6.4.5、service 方法
/**
* 根据用户查询角色
* @param principal
* @return
*/
@Override
public List<String> getUserRoleInfo(String principal) {
return userMapper.getUserRoleInfoMapper(principal);
}
6.4.6、MyRealm 方法改造
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("进入自定义授权方法");
//1获取用户身份信息
String principal = principalCollection.getPrimaryPrincipal().toString();
//2调用业务层获取用户的角色信息(数据库)
List<String> roles = userService.getUserRoleInfo(principal);
System.out.println("当前用户角色信息 = " + roles);
//2.5调用业务层获取用户的权限信息(数据库)
//3创建对象,封装当前登录用户的角色、权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//自定义添加角色
info.addRoles(roles);
//返回信息
return info;
}
6.4.7、启动测试
6.5、授权验证-获取权限进行验证
6.5.1、权限表(菜单表),角色权限中间表
CREATE TABLE `permissions` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` VARCHAR(30) DEFAULT NULL COMMENT '权限名',
`info` VARCHAR(30) DEFAULT NULL COMMENT '权限信息',
`desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='权限表';
CREATE TABLE `role_ps` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
`rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
`pid` BIGINT(20) DEFAULT NULL COMMENT '权限 id',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色权限映射表';
自定义数据–权限表
自定义数据–角色权限表
6.5.2、查询 sql
根据角色名查询对应权限信息
SELECT info FROM permissions WHERE id IN (SELECT pid FROM role_ps WHERE rid
IN (SELECT id FROM role WHERE NAME IN('admin','user')));
6.5.3、mapper 方法
@Select({
"<script>",
"select info FROM permissions WHERE id IN ",
"(SELECT pid FROM role_ps WHERE rid IN (",
"SELECT id FROM role WHERE NAME IN ",
"<foreach collection='roles' item='name' open='(' separator=',' close=')'>",
"#{name}",
"</foreach>",
"))",
"</script>"
})
List<String> getUserPermissionInfoMapper(@Param("roles")List<String> roles);
6.5.4、service代码
/**
* 根据角色查看权限
* @param roles
* @return
*/
@Override
public List<String> getUserPermissionInfo(List<String> roles) {
return userMapper.getUserPermissionInfoMapper(roles);
}
6.5.5、realm代码
6.5.6、controller 方法
//登录认证验证权限
@RequiresPermissions("user:del")
@GetMapping("userPermissions")
@ResponseBody
public String userLoginPermissions(){
System.out.println("登录认证验证权限");
return "验证权限成功";
}
6.5.7、index页面
<a shiro:hasPermission="user:delete" href="/my/userPermissions">测试授权-权限验证</a>
6.5.8、测试
6.6、授权验证-异常处理
创建认证异常处理类,使用@ControllerAdvice 加@ExceptionHandler 实现特殊异常处理。
package com.bz.shiro.controller;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class PermissionsException {
@ResponseBody
@ExceptionHandler(UnauthorizedException.class)
public String unauthorizedException(Exception e){
return "无权限";
}
@ResponseBody
@ExceptionHandler(AuthorizationException.class)
public String authorizationException(Exception e){
return "权限认证失败";
}
}
lisi账号登录
6.7、前页面授权验证
6.7.1、引入依赖
<!--配置Thymeleaf与Shrio的整合依赖-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
6.7.2、配置类添加新配置
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
6.7.3、Thymeleaf 中常用的 shiro:属性
guest 标签
用户没有身份验证时显示相应信息,即游客访问信息。user 标签
用户已经身份验证/记住我登录后显示相应的信息。authenticated 标签
用户已经身份验证通过,即 Subject.login 登录成功,不是记住我登录的。notAuthenticated 标签
用户已经身份验证通过,即没有调用 Subject.login 进行登录,包括记住我自动登录的也属于未进行身份验证。principal 标签
相当于((User)Subject.getPrincipals()).getUsername()。lacksPermission 标签
如果当前 Subject 没有权限将显示 body 体内容。hasRole 标签
如果当前 Subject 有角色将显示 body 体内容。hasAnyRoles 标签
如果当前 Subject 有任意一个角色(或的关系)将显示 body 体内容。lacksRole 标签
如果当前 Subject 没有角色将显示 body 体内容。hasPermission 标签
如果当前 Subject 有权限将显示 body 体内容
6.7.4、修改index页面
- zhangsan登录
- lisi登录
7、实现缓存
7.1、缓存工具EhCache
EhCache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。可以和大部分Java项目无缝整合,例如:Hibernate中的缓存就是基于EhCache实现的。
EhCache支持内存和磁盘存储,默认存储在内存中,如内存不够时把缓存数据同步到磁盘中。EhCache支持基于Filter的Cache实现,也支持Gzip压缩算法。
EhCache直接在JVM虚拟机中缓存,速度快,效率高;
EhCache缺点是缓存共享麻烦,集群分布式应用使用不方便。
7.2、新建项目
7.2.1、引入依赖
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.6.11</version>
<type>pom</type>
</dependency>
7.2.2、添加配置文件 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--磁盘的缓存位置-->
<diskStore path="java.io.tmpdir/ehcache"/>
<!--默认缓存-->
<defaultCache
maxEntriesLocalHeap="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!--helloworld缓存-->
<cache name="HelloWorldCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="5"
timeToLiveSeconds="5"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<!--
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,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
</ehcache>
7.2.3、测试类
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import java.io.InputStream;
public class TestEH {
public static void main(String[] args) {
//获取编译目录下的资源的流对象
InputStream input =
TestEH.class.getClassLoader().getResourceAsStream("ehcache.xml");
//获取EhCache的缓存管理对象
CacheManager cacheManager = new CacheManager(input);
//获取缓存对象
Cache cache = cacheManager.getCache("HelloWorldCache");
//创建缓存数据
Element element = new Element("name","zhang3");
//存入缓存
cache.put(element);
//从缓存中取出数据输出
Element element1 = cache.get("name");
System.out.println("缓存中数据 = " + element1.getObjectValue());
}
}
7.2.4、启动
7.3、Shiro整合EhCache
Shiro官方提供了shiro-ehcache,实现了整合EhCache作为Shiro的缓存工具。可以缓存认证执行的Realm方法,减少对数据库的访问,提高认证效率。
7.3.1、引入依赖
<!--Shiro整合EhCache-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
7.3.2、在 resources 下添加配置文件 ehcache/ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="ehcache" updateCheck="false">
<!--磁盘的缓存位置-->
<diskStore path="java.io.tmpdir"/>
<!--默认缓存-->
<defaultCache
maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false">
</defaultCache>
<!--登录认证信息缓存:缓存用户角色权限-->
<cache name="loginRolePsCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true"/>
</ehcache>
7.3.3、修改配置类 ShiroConfig
//缓存管理器
public EhCacheManager getEhCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
InputStream is =null;
try {
is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
} catch (IOException e) {
e.printStackTrace();
}
CacheManager cacheManager = new CacheManager(is);
ehCacheManager.setCacheManager(cacheManager);
return ehCacheManager;
}
7.3.4、测试
- 第一次登录可以看到查询角色、权限信息
先清除日志,再点击角色认证、权限认证,查看日志,没有查询数据库
8、会话管理
8.1、SessionManager
会话管理器,负责创建和管理用户的会话(Session)生命周期,它能够在任何环境中在本地管理用户会话,即使没有Web/Servlet/EJB容器,也一样可以保存会话。默认情况下,Shiro会检测当前环境中现有的会话机制(比如Servlet容器)进行适配,如果没有(比如独立应用程序或者非Web环境),它将会使用内置的企业会话管理器来提供相应的会话管理服务,其中还涉及一个名为SessionDAO的对象。SessionDAO负责Session的持久化操作(CRUD),允许Session数据写入到后端持久化数据库。
8.2、会话管理实现
SessionManager由SecurityManager管理。
Shiro提供了三种实现
- DefaultSessionManager:用于JavaSE环境
- ServletContainerSessionManager:用于web环境,直接使用Servlet容器的会话
- DefaultWebSessionManager:用于web环境,自己维护会话(不使用Servlet容器的 会话管理)。
8.3、获得session方式
实现
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute(“key”,”value”)说明
Controller 中的 request,在 shiro 过滤器中的 doFilerInternal 方法,被包装成ShiroHttpServletRequest。
SecurityManager 和 SessionManager 会话管理器决定 session 来源于 ServletRequest还是由 Shiro 管理的会话。
无论是通过 request.getSession 或 subject.getSession 获取到 session,操作session,两者都是等价的。
结束!!!
人应尊敬他自己,并应自视能配得上最高尚的东西。——黑格尔
还没有评论,来说两句吧...