利用Shiro和Redis优化用户信息管理模块

利用Shiro和Redis优化用户信息管理模块

引言

用户信息管理在很多应用中的核心功能之一,它关乎到数据的安全性,以及系统的性能和响应速度。为了提供更稳定、高效的用户信息管理服务,开发者们一直在不断探索和尝试优化的策略。在这篇博客中,我将详细介绍在我的最近的项目中,如何利用Apache Shiro和Redis来优化用户信息管理模块。

Apache Shiro简介

Apache Shiro是一个强大且易用的Java安全框架,提供了认证、授权、加密和会话管理等功能,为开发安全的应用程序提供了全面的保障。Shiro的架构简洁,易于理解和使用,同时具有高度的可扩展性。

Shiro的主要特性

  1. 认证: Shiro支持多种方式的认证,如:LDAP、数据库、CAS等。它还支持一次性验证和多次验证。
  2. 授权: Shiro支持基于角色的访问控制(RBAC)和基于权限的访问控制(PBAC)。它也支持细粒度的权限控制。
  3. 会话管理: Shiro可以管理用户的会话信息,包括会话的创建、过期、更新等。
  4. 加密: Shiro提供了多种加密算法,如MD5、SHA-256等。

Shiro的架构

Shiro的架构主要包括以下几个核心组件:

  1. Subject: 在Shiro中,Subject是一个安全相关的概念,代表了当前的用户。Subject可以是一个人,也可以是第三方服务、守护进程账户、时间调度任务等。
  2. Security Manager: 安全管理器是Shiro架构的核心,它是Shiro的实际运行环境。
  3. Realm: Realm是Shiro与应用安全数据之间的桥梁,也是真实的安全数据源,可以从数据库、LDAP、文本配置文件、Active Directory等获取。

Redis简介

Redis是一个开源的内存数据库,数据以键值对的形式存储,支持多种数据结构。由于Redis所有数据都存储在内存中,所以其读写速度非常快,可以作为缓存来使用。

Redis的主要特性

  1. 性能高: 由于所有数据都存储在内存中,Redis能提供高性能的读写操作。据统计,Redis每秒可以处理约110000次的读操作,和约81000次的写操作。
  2. 丰富的数据类型: Redis支持字符串、列表、集合、有序集合和哈希表等数据类型,满足各种应用场景的需求。
  3. 支持数据持久化: Redis提供了多种数据持久化方式,包括RDB快照和AOF日志。
  4. 支持数据过期: Redis可以为每个键设置过期时间,实现自动的数据管理。

Shiro在用户信息管理中的应用

在我们的项目中,我们使用Shiro进行用户的认证和授权处理。在用户登录时,Shiro会接收用户输入的用户名和密码,通过其内置的认证流程,对用户信息进行校验,如果用户名和密码匹配,用户就能够成功登录,否则会返回错误信息。此外,Shiro还可以通过定义一些角色和权限,对用户进行更精细的授权控制。

Shiro的认证流程

用户首先输入用户名和密码,Shiro会创建一个Token并将这些信息放入Token中,然后Shiro会将Token传递给SecurityManager,SecurityManager将Token传递给Authenticator,Authenticator通过Realm从数据库中获取用户的真实信息,然后将用户输入的信息和数据库中的信息进行比较,如果信息匹配,认证成功,否则认证失败。

Shiro的授权流程

Shiro的授权主要通过两种方式实现:基于角色的访问控制(RBAC)和基于权限的访问控制(PBAC)。在RBAC中,我们首先定义一些角色,然后为每个角色分配一些权限,最后我们将用户分配到某个角色,用户就能拥有该角色的所有权限。在PBAC中,我们直接为用户分配权限。

Redis在用户信息管理中的应用

我们使用Redis作为用户会话信息和常用用户信息的缓存,当用户登录成功后,我们将用户的会话信息和常用用户信息存储到Redis中,这样在后续的请求中,我们可以直接从Redis中获取这些信息,而不需要再次查询数据库,大大提高了应用的响应速度。同时,我们也利用Redis的过期策略,对用户会话进行有效期的管理。

用户会话信息的存储

当用户登录成功后,我们将用户的会话信息存储到Redis中,包括用户的ID、用户名、角色、权限等信息。我们将这些信息以键值对的形式存储在Redis中,键为用户的ID,值为用户的会话信息。这样,在后续的请求中,我们只需要根据用户的ID,就可以快速获取用户的会话信息。

用户会话信息的获取

当用户进行请求时,我们会首先从Redis中获取用户的会话信息,如果Redis中没有该用户的会话信息,我们再从数据库中获取。通过这种方式,我们大大减少了对数据库的访问,提高了应用的响应速度。

用户会话的有效期管理

我们利用Redis的过期策略,对用户会话进行有效期的管理。我们为每个用户的会话设置一个过期时间,当过期时间到达后,Redis会自动删除该用户的会话信息。这样,我们不仅可以防止用户的会话信息在Redis中占用过多的空间,还可以防止用户的会话信息被恶意利用。

项目中Shiro和Redis的整合

我们的项目后端基于SpringBoot,可以方便地整合Shiro和Redis。我们使用了SpringBoot的Shiro Starter和Redis Starter,只需要在application.yml中进行简单的配置,就可以实现Shiro和Redis的整合。我们将Shiro的会话管理改为使用Redis,这样可以实现分布式会话,让我们的应用更适应分布式环境。

Shiro和Redis的整合配置

我们首先在application.yml中配置Redis的相关信息,如主机名、端口、密码等。然后,我们在Shiro的配置文件中,将会话管理器的实现类设置为RedisSessionDAO。这样,Shiro就会使用Redis来管理用户的会话信息。

# mysql
spring:
    # 环境 dev|test|pro
    profiles:
        active: prod
    # jackson时间格式化
    jackson:
        time-zone: GMT+8
        date-format: yyyy-MM-dd HH:mm:ss
    http:
        multipart:
            max-file-size: 100MB
            max-request-size: 100MB
            enabled: true
    # 指定静态资源的路径
    resources:
        static-locations: classpath:/static/,classpath:/views/
    redis:
        open: false  # 是否开启redis缓存  true开启   false关闭
        database: 0
        host: localhost
        port: 6380
        password: 123456      # 密码(默认为空)
        timeout: 6000  # 连接超时时长(毫秒)
        pool:
            max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
            max-wait: -1      # 连接池最大阻塞等待时间(使用负值表示没有限制)
            max-idle: 10      # 连接池中的最大空闲连接
            min-idle: 5       # 连接池中的最小空闲连接

Shiro和Redis的整合使用

在整合了Shiro和Redis之后,我们的应用就可以同时利用Shiro的认证、授权功能和Redis的缓存功能。当用户登录时,我们通过Shiro进行认证,认证成功后,我们将用户的会话信息存储到Redis中。在后续的请求中,我们首先从Redis中获取用户的会话信息,如果获取失败,我们再通过Shiro进行认证。通过这种方式,我们大大提高了应用的响应速度。

package cn.jeefast.config;

import org.apache.shiro.mgt.SecurityManager;
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.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import cn.jeefast.system.oauth2.OAuth2Filter;
import cn.jeefast.system.oauth2.OAuth2Realm;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro配置
 *
 * @author theodo
 * @email 36780272@qq.com
 * @date 2017-10-20 18:33
 */
@Configuration
public class ShiroConfig {

    @Bean("sessionManager")
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        //sessionManager.setSessionIdCookieEnabled(false);
        return sessionManager;
    }

    @Bean("securityManager")
    public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(oAuth2Realm);
        securityManager.setSessionManager(sessionManager);

        return securityManager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth2", new OAuth2Filter());
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/druid/**", "anon");
        filterMap.put("/api/**", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/**/*.css", "anon");
        filterMap.put("/**/*.js", "anon");
        filterMap.put("/**/*.html", "anon");
        filterMap.put("/img/**", "anon");
        filterMap.put("/fonts/**", "anon");
        filterMap.put("/plugins/**", "anon");
        filterMap.put("/swagger/**", "anon");
        filterMap.put("/favicon.ico", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/sys/regsave", "anon");
        filterMap.put("/scxxgs.html", "anon");
        filterMap.put("/sysIndex/**", "anon");
        filterMap.put("/upload/**", "anon");

        //        文本编辑器
        filterMap.put("/**/*.eot", "anon");
        filterMap.put("/**/*.ttf", "anon");
        filterMap.put("/**/*.woff", "anon");

        filterMap.put("/", "anon");
        filterMap.put("/**", "oauth2");
        shiroFilter.setFilterChainDefinitionMap(filterMap);

        return shiroFilter;
    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}

package cn.jeefast.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * Redis配置
 *
 * @author theodo
 * @email 36780272@qq.com
 * @date 2017-10-10 19:22
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    @Bean
    public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForHash();
    }

    @Bean
    public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
        return redisTemplate.opsForValue();
    }

    @Bean
    public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForList();
    }

    @Bean
    public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForSet();
    }

    @Bean
    public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {
        return redisTemplate.opsForZSet();
    }
}

总结

通过整合Shiro和Redis,我们优化了用户信息管理模块,提高了应用的性能,增强了应用的安全性。我希望我的这份经验分享能够对你有所帮助,如果你有任何问题或者想法,欢迎在下方评论区交流。在未来的工作中,我还将继续探索更多的优化策略,以提供更好的服务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值