diff --git a/sa-token-demo/sa-token-demo-alone-redis/.gitignore b/sa-token-demo/sa-token-demo-alone-redis/.gitignore new file mode 100644 index 00000000..99a6e767 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis/.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/pom.xml b/sa-token-demo/sa-token-demo-alone-redis/pom.xml new file mode 100644 index 00000000..53502013 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + cn.dev33 + sa-token-demo-alone-redis + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.RELEASE + + + + + + 1.20.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 + ${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/src/main/java/com/pj/SaTokenAloneRedisApplication.java b/sa-token-demo/sa-token-demo-alone-redis/src/main/java/com/pj/SaTokenAloneRedisApplication.java new file mode 100644 index 00000000..f78e850e --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis/src/main/java/com/pj/SaTokenAloneRedisApplication.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 SaTokenAloneRedisApplication { + + public static void main(String[] args) throws ClassNotFoundException { + SpringApplication.run(SaTokenAloneRedisApplication.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/src/main/java/com/pj/test/AjaxJson.java b/sa-token-demo/sa-token-demo-alone-redis/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/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/src/main/java/com/pj/test/TestController.java b/sa-token-demo/sa-token-demo-alone-redis/src/main/java/com/pj/test/TestController.java new file mode 100644 index 00000000..33ce9bce --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis/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/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml new file mode 100644 index 00000000..e94c89f9 --- /dev/null +++ b/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml @@ -0,0 +1,64 @@ +# 端口 +server: + port: 8081 + +spring: + # Sa-Token配置 + sa-token: + # Token名称 (同时也是cookie名称) + token-name: satoken + # Token有效期,单位s 默认30天, -1代表永不过期 + timeout: 2592000 + # Token风格 + token-style: uuid + + # 配置Sa-Token单独使用的Redis连接 + alone-redis: + # Redis数据库索引(默认为0) + database: 2 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10ms + lettuce: + pool: + # 连接池最大连接数 + max-active: 200 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + # 连接池中的最大空闲连接 + max-idle: 10 + # 连接池中的最小空闲连接 + min-idle: 0 + + # 业务使用的Redis连接 + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10000ms + 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-plugin/pom.xml b/sa-token-plugin/pom.xml index 86b4c222..3f0fd74c 100644 --- a/sa-token-plugin/pom.xml +++ b/sa-token-plugin/pom.xml @@ -25,4 +25,13 @@ sa-token-temp-jwt + + + + org.springframework.boot + spring-boot-configuration-processor + 2.0.0.RELEASE + true + + \ No newline at end of file diff --git a/sa-token-plugin/sa-token-alone-redis/.gitignore b/sa-token-plugin/sa-token-alone-redis/.gitignore new file mode 100644 index 00000000..f56feec7 --- /dev/null +++ b/sa-token-plugin/sa-token-alone-redis/.gitignore @@ -0,0 +1,12 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ +.classpath +.project + +.factorypath + +.idea/ \ No newline at end of file diff --git a/sa-token-plugin/sa-token-alone-redis/pom.xml b/sa-token-plugin/sa-token-alone-redis/pom.xml new file mode 100644 index 00000000..a19aa97a --- /dev/null +++ b/sa-token-plugin/sa-token-alone-redis/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + cn.dev33 + sa-token-plugin + 1.20.0 + + jar + + sa-token-alone-redis + sa-token-alone-redis + sa-token-alone-redis + + + + + cn.dev33 + sa-token-dao-redis + ${sa-token-version} + true + + + cn.dev33 + sa-token-dao-redis-jackson + ${sa-token-version} + true + + + + org.apache.commons + commons-pool2 + 2.5.0 + true + + + + 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 new file mode 100644 index 00000000..75f96503 --- /dev/null +++ b/sa-token-plugin/sa-token-alone-redis/src/main/java/cn/dev33/satoken/dao/alone/SaAloneRedisInject.java @@ -0,0 +1,123 @@ +package cn.dev33.satoken.dao.alone; + +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Lettuce; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +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.SaTokenDaoRedisJackson; + +/** + * 为 SaTokenDao 单独设置Redis连接信息 + * @author kong + */ +@Configuration +public class SaAloneRedisInject implements EnvironmentAware{ + + /** + * 配置信息的前缀 + */ + public static final String ALONE_PREFIX = "spring.sa-token.alone-redis"; + + /** + * Sa-Token 持久层接口 + */ + @Autowired(required = false) + public SaTokenDao saTokenDao; + + /** + * 开始注入 + */ + @Override + public void setEnvironment(Environment environment) { + + // 如果为空或者默认实现,则不进行任何操作 + if(saTokenDao == null || saTokenDao instanceof SaTokenDaoDefaultImpl) { + return; + } + + // ------------------- 开始注入 + + // 获取cfg对象 + RedisProperties cfg = Binder.get(environment).bind(ALONE_PREFIX, RedisProperties.class).get(); + + // 1. Redis配置 + RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(); + redisConfig.setHostName(cfg.getHost()); + redisConfig.setPort(cfg.getPort()); + redisConfig.setDatabase(cfg.getDatabase()); + redisConfig.setPassword(RedisPassword.of(cfg.getPassword())); + + // 2. 连接池配置 + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + // pool配置 + Lettuce lettuce = cfg.getLettuce(); + if(lettuce.getPool() != null) { + RedisProperties.Pool pool = cfg.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(cfg.getTimeout() != null) { + builder.commandTimeout(cfg.getTimeout()); + } + // shutdownTimeout + if(lettuce.getShutdownTimeout() != null) { + builder.shutdownTimeout(lettuce.getShutdownTimeout()); + } + // 创建Factory对象 + LettuceClientConfiguration clientConfig = builder.poolConfig(poolConfig).build(); + LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfig, clientConfig); + factory.afterPropertiesSet(); + + // 3. 开始初始化 SaTokenDao + // 如果是SaTokenDaoRedis + try { + Class.forName("cn.dev33.satoken.dao.SaTokenDaoRedis"); + SaTokenDaoRedis dao = (SaTokenDaoRedis)saTokenDao; + dao.isInit = false; + dao.init(factory); + return; + } catch (ClassNotFoundException e) { + } + // 如果是SaTokenDaoRedisJackson + try { + Class.forName("cn.dev33.satoken.dao.SaTokenDaoRedisJackson"); + SaTokenDaoRedisJackson dao = (SaTokenDaoRedisJackson)saTokenDao; + dao.isInit = false; + dao.init(factory); + return; + } catch (ClassNotFoundException e) { + } + } + + /** + * 骗过编辑器,增加配置文件代码提示 + * @return 配置对象 + */ + @ConfigurationProperties(prefix = ALONE_PREFIX) + public RedisProperties getSaAloneRedisConfig() { + return new RedisProperties(); + } + +} diff --git a/sa-token-plugin/sa-token-alone-redis/src/main/resources/META-INF/spring.factories b/sa-token-plugin/sa-token-alone-redis/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..8baaf515 --- /dev/null +++ b/sa-token-plugin/sa-token-alone-redis/src/main/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.dao.alone.SaAloneRedisInject \ No newline at end of file diff --git a/sa-token-plugin/sa-token-dao-redis-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedisJackson.java b/sa-token-plugin/sa-token-dao-redis-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedisJackson.java index a8766c8b..0451102d 100644 --- a/sa-token-plugin/sa-token-dao-redis-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedisJackson.java +++ b/sa-token-plugin/sa-token-dao-redis-jackson/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedisJackson.java @@ -36,15 +36,21 @@ public class SaTokenDaoRedisJackson implements SaTokenDao { /** * String专用 */ - @Autowired public StringRedisTemplate stringRedisTemplate; /** * Object专用 */ public RedisTemplate objectRedisTemplate; + + /** + * 标记:是否已初始化成功 + */ + public boolean isInit; + @Autowired - public void setObjectRedisTemplate(RedisConnectionFactory connectionFactory) { + public void init(RedisConnectionFactory connectionFactory) { + // 指定相应的序列化方案 StringRedisSerializer keySerializer = new StringRedisSerializer(); GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer(); @@ -58,6 +64,10 @@ public class SaTokenDaoRedisJackson implements SaTokenDao { } catch (Exception e) { System.err.println(e.getMessage()); } + // 构建StringRedisTemplate + StringRedisTemplate stringTemplate = new StringRedisTemplate(); + stringTemplate.setConnectionFactory(connectionFactory); + stringTemplate.afterPropertiesSet(); // 构建RedisTemplate RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory); @@ -66,8 +76,12 @@ public class SaTokenDaoRedisJackson implements SaTokenDao { template.setValueSerializer(valueSerializer); template.setHashValueSerializer(valueSerializer); template.afterPropertiesSet(); - if(this.objectRedisTemplate == null) { + + // 开始初始化相关组件 + if(this.isInit == false) { + this.stringRedisTemplate = stringTemplate; this.objectRedisTemplate = template; + this.isInit = true; } } diff --git a/sa-token-plugin/sa-token-dao-redis/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedis.java b/sa-token-plugin/sa-token-dao-redis/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedis.java index 781daa10..43acf598 100644 --- a/sa-token-plugin/sa-token-dao-redis/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedis.java +++ b/sa-token-plugin/sa-token-dao-redis/src/main/java/cn/dev33/satoken/dao/SaTokenDaoRedis.java @@ -27,18 +27,27 @@ public class SaTokenDaoRedis implements SaTokenDao { /** * String专用 */ - @Autowired public StringRedisTemplate stringRedisTemplate; /** * Objecy专用 */ public RedisTemplate objectRedisTemplate; + + /** + * 标记:是否已初始化成功 + */ + public boolean isInit; + @Autowired - public void setObjectRedisTemplate(RedisConnectionFactory connectionFactory) { + public void init(RedisConnectionFactory connectionFactory) { // 指定相应的序列化方案 StringRedisSerializer keySerializer = new StringRedisSerializer(); JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer(); + // 构建StringRedisTemplate + StringRedisTemplate stringTemplate = new StringRedisTemplate(); + stringTemplate.setConnectionFactory(connectionFactory); + stringTemplate.afterPropertiesSet(); // 构建RedisTemplate RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory); @@ -47,8 +56,12 @@ public class SaTokenDaoRedis implements SaTokenDao { template.setValueSerializer(valueSerializer); template.setHashValueSerializer(valueSerializer); template.afterPropertiesSet(); - if(this.objectRedisTemplate == null) { + + // 开始初始化相关组件 + if(this.isInit == false) { + this.stringRedisTemplate = stringTemplate; this.objectRedisTemplate = template; + this.isInit = true; } } diff --git a/sa-token-plugin/sa-token-quick-login/pom.xml b/sa-token-plugin/sa-token-quick-login/pom.xml index fe0b0a22..91a14592 100644 --- a/sa-token-plugin/sa-token-quick-login/pom.xml +++ b/sa-token-plugin/sa-token-quick-login/pom.xml @@ -28,13 +28,6 @@ spring-boot-starter-thymeleaf 2.0.0.RELEASE - - - org.springframework.boot - spring-boot-configuration-processor - 2.0.0.RELEASE - true -