diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/.gitignore b/sa-token-demo/sa-token-demo-alone-redis-cluster/.gitignore new file mode 100644 index 00000000..99a6e767 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/.gitignore @@ -0,0 +1,12 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ +.classpath +.project + +.idea/ + +.factorypath \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/pom.xml b/sa-token-demo/sa-token-demo-alone-redis-cluster/pom.xml new file mode 100644 index 00000000..c9833996 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + cn.dev33 + sa-token-demo-alone-redis-cluster + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 2.5.14 + + + + + + 1.33.0 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + + + cn.dev33 + sa-token-spring-boot-starter + ${sa-token.version} + + + + + cn.dev33 + sa-token-dao-redis-jackson + ${sa-token.version} + + + + + org.apache.commons + commons-pool2 + + + + + cn.dev33 + sa-token-alone-redis + ${sa-token.version} + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/SaTokenAloneRedisClusterApplication.java b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/SaTokenAloneRedisClusterApplication.java new file mode 100644 index 00000000..d28a40c7 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/SaTokenAloneRedisClusterApplication.java @@ -0,0 +1,21 @@ +package com.pj; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import cn.dev33.satoken.SaManager; + +/** + * Sa-Token整合SpringBoot 示例 + * @author kong + * + */ +@SpringBootApplication +public class SaTokenAloneRedisClusterApplication { + + public static void main(String[] args) throws ClassNotFoundException { + SpringApplication.run(SaTokenAloneRedisClusterApplication.class, args); + System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig()); + } + +} \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/test/AjaxJson.java b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/test/AjaxJson.java new file mode 100644 index 00000000..1581888d --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/test/AjaxJson.java @@ -0,0 +1,162 @@ +package com.pj.test; + +import java.io.Serializable; +import java.util.List; + + +/** + * ajax请求返回Json格式数据的封装 + */ +public class AjaxJson implements Serializable{ + + private static final long serialVersionUID = 1L; // 序列化版本号 + + public static final int CODE_SUCCESS = 200; // 成功状态码 + public static final int CODE_ERROR = 500; // 错误状态码 + public static final int CODE_WARNING = 501; // 警告状态码 + public static final int CODE_NOT_JUR = 403; // 无权限状态码 + public static final int CODE_NOT_LOGIN = 401; // 未登录状态码 + public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码 + + public int code; // 状态码 + public String msg; // 描述信息 + public Object data; // 携带对象 + public Long dataCount; // 数据总数,用于分页 + + /** + * 返回code + * @return + */ + public int getCode() { + return this.code; + } + + /** + * 给msg赋值,连缀风格 + */ + public AjaxJson setMsg(String msg) { + this.msg = msg; + return this; + } + public String getMsg() { + return this.msg; + } + + /** + * 给data赋值,连缀风格 + */ + public AjaxJson setData(Object data) { + this.data = data; + return this; + } + + /** + * 将data还原为指定类型并返回 + */ + @SuppressWarnings("unchecked") + public T getData(Class cs) { + return (T) data; + } + + // ============================ 构建 ================================== + + public AjaxJson(int code, String msg, Object data, Long dataCount) { + this.code = code; + this.msg = msg; + this.data = data; + this.dataCount = dataCount; + } + + // 返回成功 + public static AjaxJson getSuccess() { + return new AjaxJson(CODE_SUCCESS, "ok", null, null); + } + public static AjaxJson getSuccess(String msg) { + return new AjaxJson(CODE_SUCCESS, msg, null, null); + } + public static AjaxJson getSuccess(String msg, Object data) { + return new AjaxJson(CODE_SUCCESS, msg, data, null); + } + public static AjaxJson getSuccessData(Object data) { + return new AjaxJson(CODE_SUCCESS, "ok", data, null); + } + public static AjaxJson getSuccessArray(Object... data) { + return new AjaxJson(CODE_SUCCESS, "ok", data, null); + } + + // 返回失败 + public static AjaxJson getError() { + return new AjaxJson(CODE_ERROR, "error", null, null); + } + public static AjaxJson getError(String msg) { + return new AjaxJson(CODE_ERROR, msg, null, null); + } + + // 返回警告 + public static AjaxJson getWarning() { + return new AjaxJson(CODE_ERROR, "warning", null, null); + } + public static AjaxJson getWarning(String msg) { + return new AjaxJson(CODE_WARNING, msg, null, null); + } + + // 返回未登录 + public static AjaxJson getNotLogin() { + return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null); + } + + // 返回没有权限的 + public static AjaxJson getNotJur(String msg) { + return new AjaxJson(CODE_NOT_JUR, msg, null, null); + } + + // 返回一个自定义状态码的 + public static AjaxJson get(int code, String msg){ + return new AjaxJson(code, msg, null, null); + } + + // 返回分页和数据的 + public static AjaxJson getPageData(Long dataCount, Object data){ + return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount); + } + + // 返回,根据受影响行数的(大于0=ok,小于0=error) + public static AjaxJson getByLine(int line){ + if(line > 0){ + return getSuccess("ok", line); + } + return getError("error").setData(line); + } + + // 返回,根据布尔值来确定最终结果的 (true=ok,false=error) + public static AjaxJson getByBoolean(boolean b){ + return b ? getSuccess("ok") : getError("error"); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @SuppressWarnings("rawtypes") + @Override + public String toString() { + String data_string = null; + if(data == null){ + + } else if(data instanceof List){ + data_string = "List(length=" + ((List)data).size() + ")"; + } else { + data_string = data.toString(); + } + return "{" + + "\"code\": " + this.getCode() + + ", \"msg\": \"" + this.getMsg() + "\"" + + ", \"data\": " + data_string + + ", \"dataCount\": " + dataCount + + "}"; + } + + + + + +} diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/test/TestController.java b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/test/TestController.java new file mode 100644 index 00000000..634faeed --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/java/com/pj/test/TestController.java @@ -0,0 +1,39 @@ +package com.pj.test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import cn.dev33.satoken.stp.StpUtil; + +/** + * 测试专用Controller + * @author kong + * + */ +@RestController +@RequestMapping("/test/") +public class TestController { + + @Autowired + StringRedisTemplate stringRedisTemplate; + + // 测试Sa-Token缓存, 浏览器访问: http://localhost:8081/test/login + @RequestMapping("login") + public AjaxJson login(@RequestParam(defaultValue="10001") String id) { + System.out.println("--------------- 测试Sa-Token缓存"); + StpUtil.login(id); + return AjaxJson.getSuccess(); + } + + // 测试业务缓存 浏览器访问: http://localhost:8081/test/test + @RequestMapping("test") + public AjaxJson test() { + System.out.println("--------------- 测试业务缓存"); + stringRedisTemplate.opsForValue().set("hello", "Hello World"); + return AjaxJson.getSuccess(); + } + +} diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/resources/application.yml new file mode 100644 index 00000000..45805308 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/resources/application.yml @@ -0,0 +1,66 @@ +# 端口 +server: + port: 8081 + +# Sa-Token配置 +sa-token: + # Token名称 (同时也是cookie名称) + token-name: satoken + # Token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # Token风格 + token-style: uuid + # 配置Sa-Token单独使用的Redis连接 + alone-redis: + # 普通集群 + pattern: cluster + # Redis服务器连接用户名(默认为空) + username: + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10s + cluster: + # Redis集群服务器节点地址 + nodes: 127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002 + # 最大重定向次数 + maxRedirects: 2 + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + +spring: + # 配置业务使用的Redis连接 + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10s + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + + + + + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/resources/application_sentinel.yml b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/resources/application_sentinel.yml new file mode 100644 index 00000000..fc8b73ab --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis-cluster/src/main/resources/application_sentinel.yml @@ -0,0 +1,68 @@ +# 端口 +server: + port: 8081 + +# Sa-Token配置 +sa-token: + # Token名称 (同时也是cookie名称) + token-name: satoken + # Token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # Token风格 + token-style: uuid + # 配置Sa-Token单独使用的Redis连接 + alone-redis: + # 哨兵模式 + pattern: sentinel + # Redis数据库索引(默认为0) + database: 2 + # Redis服务器连接用户名(默认为空) + username: + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10s + sentinel: + #哨兵的名字 + master: master_name + # Redis集群服务器节点地址 + nodes: 127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002 + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + +spring: + # 配置业务使用的Redis连接 + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10s + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + + + + + \ No newline at end of file diff --git a/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml index ed46043e..aeb5f94e 100644 --- a/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml @@ -11,7 +11,9 @@ sa-token: # Token风格 token-style: uuid # 配置Sa-Token单独使用的Redis连接 - alone-redis: + alone-redis: + # Redis模式(默认单体) + # pattern: single # Redis数据库索引(默认为0) database: 2 # Redis服务器地址 diff --git a/sa-token-plugin/sa-token-alone-redis/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java b/sa-token-plugin/sa-token-alone-redis/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java index c0e64eae..0cab14f2 100644 --- a/sa-token-plugin/sa-token-alone-redis/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java +++ b/sa-token-plugin/sa-token-alone-redis/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java @@ -1,5 +1,7 @@ package cn.dev33.satoken.dao.alone; +import cn.dev33.satoken.dao.*; +import cn.dev33.satoken.exception.SaTokenException; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; @@ -14,13 +16,6 @@ import org.springframework.data.redis.connection.lettuce.LettuceClientConfigurat import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; -import cn.dev33.satoken.dao.SaTokenDao; -import cn.dev33.satoken.dao.SaTokenDaoDefaultImpl; -import cn.dev33.satoken.dao.SaTokenDaoRedis; -import cn.dev33.satoken.dao.SaTokenDaoRedisFastjson; -import cn.dev33.satoken.dao.SaTokenDaoRedisFastjson2; -import cn.dev33.satoken.dao.SaTokenDaoRedisJackson; - import java.util.List; import java.util.stream.Collectors; @@ -59,25 +54,13 @@ public class SaAloneRedisInject implements EnvironmentAware{ // ------------------- 开始注入 - // 获取cfg对象 + // 获取cfg对象 RedisProperties cfg = Binder.get(environment).bind(ALONE_PREFIX, RedisProperties.class).get(); // 1. Redis配置 RedisConfiguration redisAloneConfig; - if (cfg.getCluster().getNodes().size() > 0) { - // 集群模式 - RedisClusterConfiguration redisClusterConfig = new RedisClusterConfiguration(); - redisClusterConfig.setUsername(cfg.getUsername()); - redisClusterConfig.setPassword(cfg.getPassword()); - RedisProperties.Cluster cluster = cfg.getCluster(); - List serverList = cluster.getNodes().stream().map(node -> { - String[] ipAndPort = node.split(":"); - return new RedisNode(ipAndPort[0].trim(), Integer.parseInt(ipAndPort[1])); - }).collect(Collectors.toList()); - redisClusterConfig.setClusterNodes(serverList); - redisClusterConfig.setMaxRedirects(cluster.getMaxRedirects()); - redisAloneConfig = redisClusterConfig; - } else { + String pattern = environment.getProperty(ALONE_PREFIX + ".pattern", "single"); + if (pattern.equals("single")) { // 单体模式 RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(); redisConfig.setHostName(cfg.getHost()); @@ -86,6 +69,63 @@ public class SaAloneRedisInject implements EnvironmentAware{ redisConfig.setPassword(RedisPassword.of(cfg.getPassword())); redisConfig.setDatabase(cfg.getDatabase()); redisAloneConfig = redisConfig; + + } else if (pattern.equals("cluster")){ + // 普通集群模式 + RedisClusterConfiguration redisClusterConfig = new RedisClusterConfiguration(); + redisClusterConfig.setUsername(cfg.getUsername()); + redisClusterConfig.setPassword(RedisPassword.of(cfg.getPassword())); + + RedisProperties.Cluster cluster = cfg.getCluster(); + List serverList = cluster.getNodes().stream().map(node -> { + String[] ipAndPort = node.split(":"); + return new RedisNode(ipAndPort[0].trim(), Integer.parseInt(ipAndPort[1])); + }).collect(Collectors.toList()); + redisClusterConfig.setClusterNodes(serverList); + redisClusterConfig.setMaxRedirects(cluster.getMaxRedirects()); + + redisAloneConfig = redisClusterConfig; + } else if (pattern.equals("sentinel")) { + // 哨兵集群模式 + RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration(); + redisSentinelConfiguration.setDatabase(cfg.getDatabase()); + redisSentinelConfiguration.setUsername(cfg.getUsername()); + redisSentinelConfiguration.setPassword(RedisPassword.of(cfg.getPassword())); + + RedisProperties.Sentinel sentinel = cfg.getSentinel(); + redisSentinelConfiguration.setMaster(sentinel.getMaster()); + redisSentinelConfiguration.setSentinelPassword(sentinel.getPassword()); + List serverList = sentinel.getNodes().stream().map(node -> { + String[] ipAndPort = node.split(":"); + return new RedisNode(ipAndPort[0].trim(), Integer.parseInt(ipAndPort[1])); + }).collect(Collectors.toList()); + redisSentinelConfiguration.setSentinels(serverList); + + redisAloneConfig = redisSentinelConfiguration; + } else if (pattern.equals("socket")) { + // socket 连接单体 Redis + RedisSocketConfiguration redisSocketConfiguration = new RedisSocketConfiguration(); + redisSocketConfiguration.setDatabase(cfg.getDatabase()); + redisSocketConfiguration.setUsername(cfg.getUsername()); + redisSocketConfiguration.setPassword(RedisPassword.of(cfg.getPassword())); + String socket = environment.getProperty(ALONE_PREFIX + ".socket", ""); + redisSocketConfiguration.setSocket(socket); + + redisAloneConfig = redisSocketConfiguration; + } else if (pattern.equals("aws")) { + // AWS ElastiCache + // AWS Redis 远程主机地址: String hoseName = "****.***.****.****.cache.amazonaws.com"; + String hostName = cfg.getHost(); + int port = cfg.getPort(); + RedisStaticMasterReplicaConfiguration redisStaticMasterReplicaConfiguration = new RedisStaticMasterReplicaConfiguration(hostName, port); + redisStaticMasterReplicaConfiguration.setDatabase(cfg.getDatabase()); + redisStaticMasterReplicaConfiguration.setUsername(cfg.getUsername()); + redisStaticMasterReplicaConfiguration.setPassword(RedisPassword.of(cfg.getPassword())); + + redisAloneConfig = redisStaticMasterReplicaConfiguration; + } else { + // 模式无法识别 + throw new SaTokenException("SaToken 无法识别 Alone-Redis 配置的模式: " + pattern); } // 2. 连接池配置 @@ -112,7 +152,7 @@ public class SaAloneRedisInject implements EnvironmentAware{ if(lettuce.getShutdownTimeout() != null) { builder.shutdownTimeout(lettuce.getShutdownTimeout()); } - // 创建Factory对象 + // 创建Factory对象 LettuceClientConfiguration clientConfig = builder.poolConfig(poolConfig).build(); LettuceConnectionFactory factory = new LettuceConnectionFactory(redisAloneConfig, clientConfig); factory.afterPropertiesSet();