当前位置: 首页 > article >正文

一文上手SpringSecurity【九】

在校验token的过滤器当中, 由于需要根据用户名称, 去查询出要认证的对象,然后再去数据库当中查询出角色列表、权限列表.频繁的操作数据库,可能会带来性能瓶颈, 那么我们该如何解决呢?
我们可以引入Redis, 将认证的对象,存储到Redis当中,在校验token的过滤器当中,可以直接从Redis当中拿,这样就避免了频繁查询数据库的操作.

一、引入Redis

1.1 Redis配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

添加配置文件

spring:
  data:
    redis:
      host: ip地址
      port: 6379
      password: Rj1024
      client-type: lettuce
      database: 0

配置文件

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        ObjectMapper objectMapper = new ObjectMapper();

        // 开启序列化LocalDate/LocalDateTime
        // jsr310里包括的.
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        objectMapper.registerModule(javaTimeModule);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
        // 设置key和value的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置hashKey和hashValue的序列化规则
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

        // 设置支持事务
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

二、修改业务逻辑

2.1 修改service层

在生成token的时候,我们直接返回给前端了,此处我们将token直接存储到Redis当中.
1
修改之后的代码如下所示:
2
修改校验token的过滤器,从Redis缓存中取数据
3
修改完代码如下所示
6

2.2 测试效果

启动服务器,请求,redis存储的数据如下所示
90
但是在校验token的过滤器当中, 从redis反序化出User对象的时候抛出了异常, 异常信息如下图所示
900
123
找到User源码, 查看一下, 发现User确实没有无参构造,造成反序列化错误
66
默认内置提供的User对象,不能满足我们的需求,此时我们只能自定义认证对象,自己去实现UserDetails接口了.

2.3 自定义认证对象

自定义一个类,实现UserDetails接口

JsonIncludeProperties({"tbSysUser", "authorityList"})
public class LoginUser implements UserDetails {
    private TbSysUser tbSysUser;
    // 用于接收权限列表, 通过此集合转换为Collection<? extends GrantedAuthority>类型的权限列表
    // 因为GrantedAuthority的实现类SimpleGrantedAuthority也没有无参构造,所以要避免序列化
    // SimpleGrantedAuthority对象,我们直接序列化List<String>
    // 在使用的时候,将List<String>转换为Collection<? extends GrantedAuthority>即可
    private List<String> authorityList;

    @JsonIgnore()
    private List<SimpleGrantedAuthority> simpleGrantedAuthorityList;

    public LoginUser() {
    }

    public LoginUser(TbSysUser tbSysUser, List<String> authorityList) {
        this.tbSysUser = tbSysUser;
        this.authorityList = authorityList;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(simpleGrantedAuthorityList != null){
            return simpleGrantedAuthorityList;
        }

        simpleGrantedAuthorityList = new ArrayList<SimpleGrantedAuthority>();
        for (String s : authorityList) {
            simpleGrantedAuthorityList.add(new SimpleGrantedAuthority(s));
        }
        return simpleGrantedAuthorityList;
    }

    @Override
    public String getPassword() {
        return tbSysUser.getPassword();
    }

    @Override
    public String getUsername() {
        return tbSysUser.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

这里需要注意的是: GrantedAuthority的实现类SimpleGrantedAuthority也没有无参构造,所以要避免序列化.

修改UserDetailsService实现类
876
修改放入redis的对象
43
修改校验token的过滤器,从redis当中反序列化出LoginUser对象
89
redis当中保存的数据
123
测试一下效果
redis3
确实从缓存当中反序列化的数据,并没有去查询数据库.到此,替换完成.

三、总结

3.1 重点内容

  • 将认证对象放到缓存当中避免频繁的操作数据库
  • User对象、SimpleGrantedAuthority对象都没有无参构造, 导致jackson无法进行序列化,这里我们使用了自定义UserDetails对象的方式来解决
  • 注意jackson注解的使用

http://www.kler.cn/news/324949.html

相关文章:

  • Kafka技术详解[1]:简介与基础概念
  • 基于springboot+vue+mysql公益旧物捐赠系统(源码+参考文档+定制)
  • pytorch U²-Net教程
  • NTLM Relay攻击原理 + 工具使用
  • 【SQL】累计统计方法,使用SQL详细写出
  • itc保伦股份智慧高校整体解决方案推动教育强国、科技强国、人才强国建设!
  • 【AI写代码】使用 ChatGPT 写 ila
  • [Unity Demo]从零开始制作空洞骑士Hollow Knight第十一集:制作法术系统的回血机制和火球机制
  • YOLO V8半自动标注工具设计
  • macOS 开发环境配置与应用开发
  • Selenium与数据库结合:数据爬取与存储的技术实践
  • 项目管理系统中的风险管理:如何识别和应对项目风险?
  • three.js----快速上手,如何用vue在web页面中导入 gltf/glb , fbx , obj 模型
  • JavaScript动态数据可视化
  • 微信小程序环境下的相亲交友系统源码优化
  • Java必修课——Spring框架
  • 深度学习:残差网络(ResNet)的原理及优缺点
  • OpenCV-图像透视变换
  • Web 3.0 介绍
  • Docker实践与应用举例
  • MPI程序实例:自适应数值积分
  • k8s中,pod生命周期,初始化容器,容器探针,事件处理函数,理解其设计思路及作用
  • 字段映射和数据转换为什么是数据集成的关键?
  • 数据结构:栈 及其应用
  • 汽车总线之---- LIN总线
  • 一文上手SpringSecurity【二】
  • Flink 结合kafka 实现端到端的一致性原理
  • 一文说完c++全部基础知识,IO流(二)
  • 2、Java 基础 - 面向对象基础
  • Qt 信号重载问题--使用lambda表达式--解决方法