1.案例中shrio 逻辑认证会频繁的查询数据库,消耗性能
2.改造一下,把之前学习的缓存中间件拿来用
<!--shiro整合redis缓存,和之前的缓存不同-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.2.1-RELEASE</version>
</dependency>
这种整合方式没有搞懂 会爆错
具体原因是springboot2.X默认是lettuce客户端
而报错是jedis出来XXX问题,不知道怎么解决
百度了很多都是没用的,所以懒得看了
导依赖 commons-pool2 spring-boot-starter-data-redis spring-boot-starter-cache
<!--spring2.0集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<!-- redis依赖,2.0以上使用这个依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 缓存依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
配置文件
#redis
#第几个数据库,由于redis中数据库不止一个
spring.redis.database=2
# 也可指定为127.0.0.1
spring.redis.host=127.0.0.1
# 默认端口
spring.redis.port=6379
spring.redis.password=
#默认为空
# springboot2.x以上如此配置,由于2.x的客户端是lettuce
# 单位要带上
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.min-idle=1
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=10000ms
spring.redis.lettuce.shutdown-timeout=100ms
redis缓存的一些配置
>@Slf4j
@Configuration
@EnableCaching
public class RedisCache extends CachingConfigurerSupport {
// 自定义key生成器
@Bean
public KeyGenerator keyGenerator() {
return (o, method, params) -> {
StringBuilder sb = new StringBuilder ( );
sb.append (o.getClass ( ).getName ( )); // 类目
sb.append (method.getName ( )); // 方法名
for (Object param : params) {
sb.append (param.toString ( )); // 参数名
}
return sb.toString ( );
};
}
// 配置缓存管理器
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig ( )
.entryTtl (Duration.ofSeconds (60 * 60 * 24 * 3)) // 缓存*秒失效
// 设置key的序列化方式
.serializeKeysWith (RedisSerializationContext.SerializationPair.fromSerializer (keySerializer ( )))
// 设置value的序列化方式
.serializeValuesWith (RedisSerializationContext.SerializationPair.fromSerializer (valueSerializer ( )))
// 不缓存null值
.disableCachingNullValues ( );
RedisCacheManager redisCacheManager = RedisCacheManager.builder (connectionFactory)
.cacheDefaults (config)
.transactionAware ( )
.build ( );
log.info ("自定义RedisCacheManager加载完成");
return redisCacheManager;
}
// key键序列化方式
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer ( );
}
// value值序列化方式
private GenericJackson2JsonRedisSerializer valueSerializer() {
return new GenericJackson2JsonRedisSerializer ( );
}
}
开启缓存 使用注解@EnableCaching
>@EnableCaching
@SpringBootApplication
public class ShiroApplication {
public static void main(String[] args) {
SpringApplication.run (ShiroApplication.class, args);
}
}
缓存注解,在业务层调用
@Cacheable在方法上 注意key如果定义了没有的键或值会报500,默认是参数作为simplekey
权限和角色一般都不会变 假如变更了 使用@CacheEvict移除一个或者多个缓存 详情百度
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
@Cacheable(cacheNames = "users",key ="#name")
public User getPwdByName(String name) {
System.out.println(name+":数据库查询用户中.....");
return userDao.getPwdByName (name);
}
@Override
@Cacheable(cacheNames = "roles",key = "#name")
public List<String> listRoles(String name) {
System.out.println(name+":数据库查询角色中.....");
return userDao.listRoles (name);
}
@Override
@Cacheable(cacheNames = "Permissions",key = "#name")
public List<String> listPermissions(String name) {
System.out.println(name+":数据库查询用户权限中");
return userDao.listPermissions (name);
}
测试:
正确输入账号密码都可以使用 但是出现一个Bug 输入错误的登陆会跳出error500 显然之前设置的出错跳转回登陆页失效了
原因是你输入了错误的信息数据库查询不到内容 key为用户名 value为null 缓存策略是value为空不缓存
IllegalArgumentException: Cache ‘users’ does not allow ‘null’ values. Avoid storing null via ‘@Cacheable(unless="#result == null")’ or configure RedisCache to allow ‘null’ via RedisCacheConfiguration.
所以爆了500
解决:
方案1:
@Controller public class LoginController {
//表单提交,处理后返回首页
@PostMapping("/tologin")
String tologin(String name, String password, Model model) {
//处理逻辑
/**使用Shiro编写认证操作*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject ( );
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken (name, password);
//执行登陆方法
try {
subject.login (token);
//登陆信息会交给Realm去执行认证逻辑,比如去数据库对比用户,密码
//然后会设置角色,权限
} catch (UnknownAccountException e) {
model.addAttribute ("msg", "用户名不存在");
return "/login";
} catch (IncorrectCredentialsException e) {
model.addAttribute ("msg", "密码错误");
return "/login";
}catch (Exception e){
//redis 数据库没查到值
model.addAttribute ("msg", "用户名不存在");
return "/login";
}
//这里的请求不能返回"/index"这是返回index.html了 没有经过mvc拦截到
// 必须使用"redirect:/index" 或者"forward:/index"
//catch块可以跳转是因为我的页面就叫login.html 请求也是/login
return "redirect:/index";
}
方案2:
设置缓存条件:unless="#result == null" 缓存到redis 除非结果是空
@Override
@Cacheable(cacheNames = "users",key ="#name",unless="#result == null")
public User getPwdByName(String name) {
System.out.println(name+":数据库查询用户中.....");
return userDao.getPwdByName (name);
}
shrio整合结束,如有错误多多指正~