基于Spring的单点登录SSO实现(redis+JWT+SpringSecurity)

本文介绍了基于Spring的单点登录SSO实现(redis+JWT+SpringSecurity) 方法。

一、应用场景

       平台包含多个系统应用的,实现只要在一个应用登录一次,就可以访问其他相互信任的应用。常用于多应用平台中,此时常常建立门户网站系统,进行单点登录。

二、实现思路

    单点登录可基于cookie、session、token等方式。本文主要基于token和redis实现。

     登录信息及token通常存储于redis中,鉴权和校验时需要取redis的用户信息。正常单个应用只有一个服务器的一个redis数据库,同时存储其他业务信息(字典、应用系统配置、其他业务数据)和登录信息,在同一数据库中以key值区分。在没有引入单点登录时,由于多应用的其他业务信息(字典、应用系统配置、其他业务数据)和登录信息有相同的key会冲突,所以应用之间的数据库是不同的。

    单点登录的实现主要是将所有应用的登录信息存储到同一个redis数据库中,而各自的业务信息依然存储到原来应用自己的数据库。这样使得各个子系统登录信息实现共享,校验登录状态和token时取自同一信息,其他业务数据进行分离。

      因此相当于将redis设置主库从库。实现时主要应用注解@primary、@Qualifier

@Bean、@ConfigurationProperties

三、实现方法

1、配置文件

三个系统如下设置,redis配置子系统的数据(datasebase不同),token-redis配置存储token的数据(database相同)

子系统1

子系统2

子系统3

业务数据存储

10.110.1.1:6379 database: 1

10.110.1.1:6379 database: 2

10.110.12.1:6379 database: 3

Token及登录信息存储

10.110.1.1:6379 database: 4

10.110.1.1:6379 database: 4

10.110.1.1:6379 database: 4

redis:
  # 地址
  host: 10.110.1.1:6379
  # 端口,默认为6379
  port: 6379
  # 数据库索引
  database: 1
  # 密码
  password: root
  # 连接超时时间
  timeout: 10s
  lettuce:
    pool:
      # 连接池中的最小空闲连接
      min-idle: 0
      # 连接池中的最大空闲连接
      max-idle: 8
      # 连接池的最大数据库连接数
      max-active: 8
      # #连接池最大阻塞等待时间(使用负值表示没有限制)
      max-wait: -1ms
token-redis:
  # 地址
  host: 10.110.1.1
  # 端口,默认为6379
  port: 6379
  # 数据库索引
  database: 4
  # 密码
  password: root
  # 连接超时时间(s)
  timeout: 10s
  lettuce:
    pool:
      # 连接池中的最小空闲连接
      min-idle: 0
      # 连接池中的最大空闲连接
      max-idle: 8
      # 连接池的最大数据库连接数
      max-active: 8
      # #连接池最大阻塞等待时间(使用负值表示没有限制)
      max-wait: -1ms

2、redisConfig配置的主要注解和方法

src/main/java/com/inspur/framework/config/RedisConfig.java

(1)配置读取

 @ConfigurationProperties(prefix = "spring.token-redis")

 @ConfigurationProperties(prefix = "spring.token")

区分配置文件的连接,firstRedisProperties和secondRedisProperties返回RedisProperties连接配置

@Primary  

不同类实现同一接口时,不注明名称则默认用当下的bean,注解在子系统的默认redis配置

@Bean(name = "mainRedisProperties")

@Bean(name = "tokenRedisProperties")

给返回的配置RedisProperties命名,在传参时标注@Qualifier配合使用。@Bean在项目启动时生成。

(2)连接方法

连接池配置并和redis建立连接,连接参数由@Qualifier指定

// 单系统默认配置

@Primary
@Bean(name = "redisConnectionFactory")
public RedisConnectionFactory redisConnectionFactory(@Qualifier("mainRedisProperties") RedisProperties redisProperties) {

// redis单体模式连接配置
RedisStandaloneConfiguration standaloneConfig = new RedisStandaloneConfiguration();
standaloneConfig.setHostName(redisProperties.getHost());
standaloneConfig.setPort(redisProperties.getPort());
standaloneConfig.setDatabase(redisProperties.getDatabase());
standaloneConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
standaloneConfig.setDatabase(redisProperties.getDatabase());
redisConfig = standaloneConfig;

// lettuce连接池配置
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
RedisProperties.Lettuce lettuce = redisProperties.getLettuce();
if(lettuce.getPool() != null) {
    RedisProperties.Pool pool = redisProperties.getLettuce().getPool();
    // 连接池最大连接数
    poolConfig.setMaxTotal(pool.getMaxActive());
    // 连接池中的最大空闲连接
    poolConfig.setMaxIdle(pool.getMaxIdle());
    // 连接池中的最小空闲连接
    poolConfig.setMinIdle(pool.getMinIdle());
    // 连接池最大阻塞等待时间(使用负值表示没有限制)
    poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());
}
LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
// timeout
if(redisProperties.getTimeout() != null) {
    builder.commandTimeout(redisProperties.getTimeout());
}
// shutdownTimeout
if(lettuce.getShutdownTimeout() != null) {
    builder.shutdownTimeout(lettuce.getShutdownTimeout());
}
// 创建Factory对象
LettuceClientConfiguration clientConfig = builder.poolConfig(poolConfig).build();
return new LettuceConnectionFactory(redisConfig, clientConfig);

}

// tokenredis配置 

@Bean(name = "tokenRedisConnectionFactory")
public RedisConnectionFactory tokenRedisConnectionFactory(@Qualifier("tokenRedisProperties") RedisProperties redisProperties) {

// 同上

}

  (3) 连接池变量

用于实际业务中需要操作redis时的变量注入

@Bean(name = "redisTemplate")
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(@Qualifier("redisConnectionFactory") RedisConnectionFactory connectionFactory)
{

RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);

// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(fastJson2JsonRedisSerializer);

// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(fastJson2JsonRedisSerializer);

template.afterPropertiesSet();
return template;

}

(4)连接redis数据库操作

@Bean(name = "tokenRedisTemplate")
public RedisTemplate<String, Object> tokenRedisTemplate(@Qualifier("tokenRedisConnectionFactory") RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);

    // 使用StringRedisSerializer来序列化和反序列化redis的key值
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(fastJson2JsonRedisSerializer);

    // Hash的key也采用StringRedisSerializer的序列化方式
    template.setHashKeySerializer(new StringRedisSerializer());
    template.setHashValueSerializer(fastJson2JsonRedisSerializer);

    template.afterPropertiesSet();
    return template;
}

3、RedisToken增删改查操作

com/common/core/redis/RedisTokenCache.java

与原有的RedisCache.java相比基本相同,主要时redisTemplate采用的连接不同,通过注解指定名称实现@Qualifier("tokenRedisTemplate")

    @Autowired
    @Qualifier("tokenRedisTemplate")
    public RedisTemplate redisTemplate


@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisTokenCache {
    @Autowired
    @Qualifier("tokenRedisTemplate")
    public RedisTempl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值