单点登录逻辑:
目录
定义controller接收请求
web服务器基于url过滤请求,首先排除不需要过滤url(白名单),然后基于请求的cookie或token匹配redis中的session_id
① 创建过滤器注册Bean(FilterRegistrationBean)
FilterRegistrationBean 是 Spring 提供的用于配置和注册 Servlet 过滤器的类。它允许通过 Java 代码的方式配置过滤器,并提供了一系列方法来设置过滤器的属性。这类过滤器的初始化参数通常用于在过滤器初始化时提供一些配置信息。
FilterRegistrationBean registration = new FilterRegistrationBean();
通过这行代码创建了一个 FilterRegistrationBean 实例,用于配置和注册过滤器。
设置过滤器名称:
registration.setName("XxlSsoWebFilter");
通过 setName 方法设置了过滤器的名称。这个名称可以在 Servlet 容器中唯一标识这个过滤器。
设置过滤器执行顺序:
registration.setOrder(1);
通过 setOrder 方法设置了过滤器的执行顺序。数字越小,优先级越高。
设置过滤器的 URL 匹配模式:
registration.addUrlPatterns("/*");
通过 addUrlPatterns 方法设置了过滤器要拦截的 URL 模式。这里设置为 "/*" 表示拦截所有请求。
设置过滤器实例:
registration.setFilter(new XxlSsoWebFilter());
通过 setFilter 方法设置了要注册的过滤器实例,这里是 XxlSsoWebFilter 类的一个实例。
设置过滤器的初始化参数:
registration.addInitParameter(Conf.SSO_SERVER, xxlSsoServer);
registration.addInitParameter(Conf.SSO_LOGOUT_PATH, xxlSsoLogoutPath);
registration.addInitParameter(Conf.SSO_EXCLUDED_PATHS, xxlSsoExcludedPaths);
通过 addInitParameter 方法为过滤器设置初始化参数。这些参数在过滤器初始化时会传递给 init 方法,供过滤器内部使用。
这些初始化参数的具体用途,通常由对应的过滤器的实现逻辑来决定。在过滤器的 init 方法中,可以通过 FilterConfig 对象获取这些初始化参数,以便在过滤器运行期间使用。
返回 FilterRegistrationBean 实例:
return registration;
最后,通过 return registration; 将配置好的 FilterRegistrationBean 实例返回,从而使其生效。
过滤器的初始化参数通常在过滤器的 init 方法中用到,这是过滤器的生命周期中的一个阶段,用于执行一些初始化操作。当过滤器被容器初始化时,init 方法被调用,传入一个 FilterConfig 对象,该对象包含了通过 addInitParameter 设置的初始化参数。过滤器可以在 init 方法中读取这些参数,并进行相应的初始化配置。
过滤器的创建时机取决于 Spring 容器和 Servlet 容器的初始化顺序。通常,在 Spring Boot 中,过滤器会在应用启动时由 Spring 容器创建并注册到 Servlet 容器中。当应用启动时,Spring 容器会解析并执行相关的配置类,其中包括 FilterRegistrationBean 的配置,从而创建并注册相应的过滤器。
基于 Ant表达式 匹配路径,匹配放行,从而排除SSO客户端不需要过滤的路径,达到类似白名单的效果。
private static ShardedJedis getInstance() {
if (shardedJedisPool == null) {
try {
if (INSTANCE_INIT_LOCL.tryLock(2, TimeUnit.SECONDS)) {
try {
if (shardedJedisPool == null) {
// JedisPoolConfig
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(200);
config.setMaxIdle(50);
config.setMinIdle(8);
config.setMaxWaitMillis(10000); // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
config.setTestOnBorrow(true); // 在获取连接的时候检查有效性, 默认false
config.setTestOnReturn(false); // 调用returnObject方法时,是否进行有效检查
config.setTestWhileIdle(true); // Idle时进行连接扫描
config.setTimeBetweenEvictionRunsMillis(30000); // 表示idle object evitor两次扫描之间要sleep的毫秒数
config.setNumTestsPerEvictionRun(10); // 表示idle object evitor每次扫描的最多的对象数
config.setMinEvictableIdleTimeMillis(60000); // 表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
// JedisShardInfo List
List<JedisShardInfo> jedisShardInfos = new LinkedList<JedisShardInfo>();
String[] addressArr = address.split(",");
for (int i = 0; i < addressArr.length; i++) {
JedisShardInfo jedisShardInfo = new JedisShardInfo(addressArr[i]);
jedisShardInfos.add(jedisShardInfo);
}
shardedJedisPool = new ShardedJedisPool(config, jedisShardInfos);
logger.info(">>>>>>>>>>> xxl-sso, JedisUtil.ShardedJedisPool init success.");
}
} finally {
INSTANCE_INIT_LOCL.unlock();
}
}
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
}
if (shardedJedisPool == null) {
throw new NullPointerException(">>>>>>>>>>> xxl-sso, JedisUtil.ShardedJedisPool is null.");
}
ShardedJedis shardedJedis = shardedJedisPool.getResource();
return shardedJedis;
}
这段代码主要是用于获取 Redis 的 ShardedJedis 实例,通过连接池管理多个 Redis 节点的连接。下面对代码的不同部分进行详细解释:
为什么使用 ShardedJedis: ShardedJedis
是 Jedis 库提供的一种实现,用于处理 Redis 的分片连接。在分布式 Redis 环境中,可能会有多个 Redis 节点,使用 ShardedJedis
可以将数据分布到不同的节点上,提高数据的存储和读取性能。
为什么使用 ShardedJedisPool: ShardedJedisPool
是 ShardedJedis 的连接池实现,用于管理和复用连接。连接池的使用可以减少连接的创建和销毁开销,提高性能。
为什么使用单例模式创建连接池: 使用 INSTANCE_INIT_LOCL
可重入锁进行单例模式的实现,确保在多线程环境下只创建一个连接池。这样可以避免重复创建连接池,提高效率,同时保证线程安全。
连接池配置:
javaCopy code
JedisPoolConfig config = new JedisPoolConfig(); // ...(省略了一些连接池的配置,如最大连接数、最小空闲连接数等)
这里通过 JedisPoolConfig
对象对连接池进行了一些基本配置,如最大连接数、最小空闲连接数等,以及一些其他连接池的配置参数。
Redis 节点信息配置:
javaCopy code
List<JedisShardInfo> jedisShardInfos = new LinkedList<JedisShardInfo>(); String[] addressArr = address.split(","); for (int i = 0; i < addressArr.length; i++) { JedisShardInfo jedisShardInfo = new JedisShardInfo(addressArr[i]); jedisShardInfos.add(jedisShardInfo); }
这里根据传入的 Redis 节点地址列表,创建了一个 JedisShardInfo
的列表。每个 JedisShardInfo
对象表示一个 Redis 节点的连接信息。
创建 ShardedJedisPool:
shardedJedisPool = new ShardedJedisPool(config, jedisShardInfos);
最终通过连接池的构造函数创建了 ShardedJedisPool
对象。
获取 ShardedJedis 实例:
ShardedJedis shardedJedis = shardedJedisPool.getResource(); return shardedJedis;
最后通过连接池获取 ShardedJedis
实例,用于执行 Redis 相关的操作。shardedJedisPool.getResource()
表示从连接池中获取一个可用的连接。
为什么使用可重入锁:
if (INSTANCE_INIT_LOCL.tryLock(2, TimeUnit.SECONDS)) { // ...(省略了部分代码) }
使用可重入锁 INSTANCE_INIT_LOCL
,确保在多线程环境下只有一个线程创建连接池。这样可以避免多个线程同时创建连接池,导致资源浪费和性能问题。
总体来说,这段代码的目的是确保在多线程环境下创建一个单例的 ShardedJedisPool
对象,用于管理多个 Redis 节点的连接,并提供了一些配置项来优化连接池的性能。
重新new个cookie => 初始化 => response.addCookie(cookie);
设置响应文本类型 ? ? ? ? => ?res.setContentType("application/json;charset=utf-8");
构建错误码和错误消息(json字符串) ? ? => ?String errInfo = ""
将错误消息写入响应输出流 => ?res.getWriter().println(errInfo)