diff --git a/README.md b/README.md index 68a4c337..63f289e7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # sa-token 一个好用的JavaWeb权限认证框架 + +在线文档:[http://sa-token.dev33.cn/](http://sa-token.dev33.cn/) \ No newline at end of file diff --git a/sa-token-demo-springboot/.classpath b/sa-token-demo-springboot/.classpath new file mode 100644 index 00000000..cd04a794 --- /dev/null +++ b/sa-token-demo-springboot/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sa-token-demo-springboot/.gitignore b/sa-token-demo-springboot/.gitignore new file mode 100644 index 00000000..ae57b0f9 --- /dev/null +++ b/sa-token-demo-springboot/.gitignore @@ -0,0 +1,6 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ \ No newline at end of file diff --git a/sa-token-demo-springboot/.project b/sa-token-demo-springboot/.project new file mode 100644 index 00000000..4772c71f --- /dev/null +++ b/sa-token-demo-springboot/.project @@ -0,0 +1,23 @@ + + + sa-token-demo-springboot + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/sa-token-demo-springboot/lib/sa-token-spring-1.0.0.jar b/sa-token-demo-springboot/lib/sa-token-spring-1.0.0.jar new file mode 100644 index 00000000..cce96b09 Binary files /dev/null and b/sa-token-demo-springboot/lib/sa-token-spring-1.0.0.jar differ diff --git a/sa-token-demo-springboot/pom.xml b/sa-token-demo-springboot/pom.xml new file mode 100644 index 00000000..aa6c274a --- /dev/null +++ b/sa-token-demo-springboot/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + cn.dev33 + sa-token-demo-springboot + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + + + cn.dev33.sa-token + sa-token-spring + 1.0.0 + system + ${project.basedir}/lib/sa-token-spring-1.0.0.jar + + + + + org.springframework.boot + spring-boot-starter-redis + RELEASE + + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + lib/ + com.pj.SaTokenDemoApplication + + + + lib/sa-token-spring-1.0.0.jar + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + package + + copy-dependencies + + + + ${project.build.directory}/lib + + + + + + + + + + \ No newline at end of file diff --git a/sa-token-demo-springboot/src/main/java/com/pj/SaTokenDemoApplication.java b/sa-token-demo-springboot/src/main/java/com/pj/SaTokenDemoApplication.java new file mode 100644 index 00000000..384540c6 --- /dev/null +++ b/sa-token-demo-springboot/src/main/java/com/pj/SaTokenDemoApplication.java @@ -0,0 +1,18 @@ +package com.pj; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import cn.dev33.satoken.spring.SaTokenSetup; + +@SaTokenSetup // 标注启动 sa-token +@SpringBootApplication +public class SaTokenDemoApplication { + + public static void main(String[] args) throws JsonProcessingException { + SpringApplication.run(SaTokenDemoApplication.class, args); // run--> + } + +} \ No newline at end of file diff --git a/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java b/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java new file mode 100644 index 00000000..b7a70598 --- /dev/null +++ b/sa-token-demo-springboot/src/main/java/com/pj/satoken/SaTokenDaoRedis.java @@ -0,0 +1,91 @@ +package com.pj.satoken; + +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +// import org.springframework.stereotype.Component; + +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.session.SaSession; + +/** + * sa-token持久层的实现类 , 基于redis + */ +// @Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token与redis的集成 +public class SaTokenDaoRedis implements SaTokenDao { + + + // string专用 + @Autowired + StringRedisTemplate stringRedisTemplate; + + // SaSession专用 + RedisTemplate redisTemplate; + @Autowired + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void setRedisTemplate(RedisTemplate redisTemplate) { + RedisSerializer stringSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringSerializer); + this.redisTemplate = redisTemplate; + } + + + // 根据key获取value ,如果没有,则返回空 + @Override + public String getValue(String key) { + return stringRedisTemplate.opsForValue().get(key); + } + + // 写入指定key-value键值对,并设定过期时间(单位:秒) + @Override + public void setValue(String key, String value, long timeout) { + stringRedisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS); + } + + // 删除一个指定的key + @Override + public void delKey(String key) { + stringRedisTemplate.delete(key); + } + + + // 根据指定key的session,如果没有,则返回空 + @Override + public SaSession getSaSession(String sessionId) { + return redisTemplate.opsForValue().get(sessionId); + } + + // 将指定session持久化 + @Override + public void saveSaSession(SaSession session, long timeout) { + redisTemplate.opsForValue().set(session.getId(), session, timeout, TimeUnit.SECONDS); + } + + // 更新指定session + @Override + public void updateSaSession(SaSession session) { + long expire = redisTemplate.getExpire(session.getId()); + if(expire == -2) { // -2 = 无此键 + return; + } + redisTemplate.opsForValue().set(session.getId(), session, expire, TimeUnit.SECONDS); + } + + // 删除一个指定的session + @Override + public void delSaSession(String sessionId) { + redisTemplate.delete(sessionId); + } + + + + + + + + +} diff --git a/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpCustom.java b/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpCustom.java new file mode 100644 index 00000000..6de6ce8f --- /dev/null +++ b/sa-token-demo-springboot/src/main/java/com/pj/satoken/StpCustom.java @@ -0,0 +1,28 @@ +package com.pj.satoken; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Component; + +import cn.dev33.satoken.stp.StpInterface; + +/** + * 自定义权限验证接口扩展 + */ +@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展 +public class StpCustom implements StpInterface { + + @Override + public List getPermissionCodeList(Object login_id, String login_key) { + List list = new ArrayList(); + list.add("101"); + list.add("user-add"); + list.add("user-delete"); + list.add("user-update"); + list.add("user-get"); + list.add("article-get"); + return list; + } + +} diff --git a/sa-token-demo-springboot/src/main/java/com/pj/test/AjaxJson.java b/sa-token-demo-springboot/src/main/java/com/pj/test/AjaxJson.java new file mode 100644 index 00000000..f8c12a10 --- /dev/null +++ b/sa-token-demo-springboot/src/main/java/com/pj/test/AjaxJson.java @@ -0,0 +1,163 @@ +package com.pj.test; + +import java.io.Serializable; +import java.util.List; + + +/** + * ajax返回Json的封装 + * 【此类封装了Meta和Body的功能,写法不同,但是返回数据格式相同】 + */ +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-springboot/src/main/java/com/pj/test/TestController.java b/sa-token-demo-springboot/src/main/java/com/pj/test/TestController.java new file mode 100644 index 00000000..fd63fe4a --- /dev/null +++ b/sa-token-demo-springboot/src/main/java/com/pj/test/TestController.java @@ -0,0 +1,83 @@ +package com.pj.test; + +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.session.SaSessionUtil; +import cn.dev33.satoken.stp.StpUtil; + +@RestController +@RequestMapping("/test/") +public class TestController { + + // 测试登录接口, 浏览器访问: http://localhost:8081/test/login + @RequestMapping("login") + public AjaxJson login(@RequestParam(defaultValue="10001") String id) { + System.out.println("======================= 进入方法,测试登录接口 ========================= "); + System.out.println("当前会话的token:" + StpUtil.getTokenValue()); + System.out.println("当前是否登录:" + StpUtil.isLogin()); + System.out.println("当前登录账号:" + StpUtil.getLoginId_defaultNull()); + StpUtil.setLoginId(id); // 在当前会话登录此账号 + System.out.println("登录成功"); + System.out.println("当前是否登录:" + StpUtil.isLogin()); + System.out.println("当前登录账号:" + StpUtil.getLoginId()); + System.out.println("当前登录账号:" + StpUtil.getLoginId_asInt()); // 获取登录id并转为int + + System.out.println("当前token信息:" + StpUtil.getTokenInfo()); // 获取登录id并转为int + + return AjaxJson.getSuccess(); + } + + // 测试权限接口, 浏览器访问: http://localhost:8081/test/jur + @RequestMapping("jur") + public AjaxJson jur() { + System.out.println("======================= 进入方法,测试权限接口 ========================= "); + + System.out.println("是否具有权限101" + StpUtil.hasPermission(101)); + System.out.println("是否具有权限user-add" + StpUtil.hasPermission("user-add")); + System.out.println("是否具有权限article-get" + StpUtil.hasPermission("article-get")); + + System.out.println("没有user-add权限就抛出异常"); + StpUtil.checkPermission("user-add"); + + System.out.println("在【101、102】中只要拥有一个就不会抛出异常"); + StpUtil.checkPermissionOr("101", "102"); + + System.out.println("在【101、102】中必须全部拥有才不会抛出异常"); + StpUtil.checkPermissionAnd("101", "102"); + + System.out.println("权限测试通过"); + + return AjaxJson.getSuccess(); + } + + // 测试会话session接口, 浏览器访问: http://localhost:8081/test/session + @RequestMapping("session") + public AjaxJson session() { + System.out.println("======================= 进入方法,测试会话session接口 ========================= "); + System.out.println("当前是否登录:" + StpUtil.isLogin()); + System.out.println("当前登录账号session的id" + StpUtil.getSession().getId()); + System.out.println("当前登录账号session的id" + StpUtil.getSession().getId()); + System.out.println("测试取值name:" + StpUtil.getSession().getAttribute("name")); + StpUtil.getSession().setAttribute("name", "张三"); // 写入一个值 + System.out.println("测试取值name:" + StpUtil.getSession().getAttribute("name")); + return AjaxJson.getSuccess(); + } + + // 测试自定义session接口, 浏览器访问: http://localhost:8081/test/session2 + @RequestMapping("session2") + public AjaxJson session2() { + System.out.println("======================= 进入方法,测试自定义session接口 ========================= "); + // 自定义session就是无需登录也可以使用 的session :比如拿用户的手机号当做 key, 来获取 session + System.out.println("自定义 session的id为:" + SaSessionUtil.getSessionById("1895544896").getId()); + System.out.println("测试取值name:" + SaSessionUtil.getSessionById("1895544896").getAttribute("name")); + SaSessionUtil.getSessionById("1895544896").setAttribute("name", "张三"); // 写入值 + System.out.println("测试取值name:" + SaSessionUtil.getSessionById("1895544896").getAttribute("name")); + System.out.println("测试取值name:" + SaSessionUtil.getSessionById("1895544896").getAttribute("name")); + return AjaxJson.getSuccess(); + } + + + +} diff --git a/sa-token-demo-springboot/src/main/java/com/pj/test/TopController.java b/sa-token-demo-springboot/src/main/java/com/pj/test/TopController.java new file mode 100644 index 00000000..a31775b2 --- /dev/null +++ b/sa-token-demo-springboot/src/main/java/com/pj/test/TopController.java @@ -0,0 +1,57 @@ +package com.pj.test; + +import java.io.IOException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ModelAttribute; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; + +/** + * 加强版控制器 + */ +@ControllerAdvice // 可指定包前缀,比如:(basePackages = "com.zyd.blog.controller.admin") +public class TopController { + + // 在每个控制器之前触发的操作 + @ModelAttribute + public void get(HttpServletRequest request) throws IOException { + + } + + // 全局异常拦截(拦截项目中的所有异常) + @ExceptionHandler + public void handlerException(Exception e, HttpServletRequest request, HttpServletResponse response) + throws Exception { + + e.printStackTrace(); // 打印堆栈,以供调试 + + response.setContentType("application/json; charset=utf-8"); // http说明,我要返回JSON对象 + + // 如果是未登录异常 + if (e instanceof NotLoginException) { + String jsonStr = new ObjectMapper().writeValueAsString(AjaxJson.getNotLogin()); + response.getWriter().print(jsonStr); + return; + } + // 如果是权限异常 + if (e instanceof NotPermissionException) { + NotPermissionException ee = (NotPermissionException) e; + String jsonStr = new ObjectMapper().writeValueAsString(AjaxJson.getNotJur("无此权限:" + ee.getCode())); + response.getWriter().print(jsonStr); + return; + } + + // 普通异常输出:500 + 异常信息 + response.getWriter().print(new ObjectMapper().writeValueAsString(AjaxJson.getError(e.getMessage()))); + + } + +} diff --git a/sa-token-demo-springboot/src/main/resources/application.yml b/sa-token-demo-springboot/src/main/resources/application.yml new file mode 100644 index 00000000..792fab7f --- /dev/null +++ b/sa-token-demo-springboot/src/main/resources/application.yml @@ -0,0 +1,44 @@ +# 端口 +server: + port: 8081 + +spring: + # sa-token配置 + sa-token: + # token名称(同时也是cookie名称) + token-name: satoken + # token有效期,单位s 默认30天,-1为永不过期 + timeout: 2592000 + # 在多人登录同一账号时,是否共享会话(为true时共用一个,为false时新登录挤掉旧登录) + is-share: true + # 是否在cookie读取不到token时,继续从请求header里继续尝试读取 + is-read-head: true + # 是否在header读取不到token时,继续从请求题参数里继续尝试读取 + is-read-body: true + # 是否在初始化配置时打印版本字符画 + is-v: true + + + # redis配置 + redis: + # Redis数据库索引(默认为0) + database: 1 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接池最大连接数(使用负值表示没有限制) + pool: + maxActive: 20 + # 连接池最大阻塞等待时间(使用负值表示没有限制) + maxWait: -1 + # 连接池中的最大空闲连接 + maxIdle: 8 + # 连接池中的最小空闲连接 + minIdle: 1 + # 连接超时时间(毫秒) + timeout: 0 + + \ No newline at end of file diff --git a/sa-token-dev/.classpath b/sa-token-dev/.classpath new file mode 100644 index 00000000..cd04a794 --- /dev/null +++ b/sa-token-dev/.classpath @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sa-token-dev/.gitignore b/sa-token-dev/.gitignore new file mode 100644 index 00000000..ae57b0f9 --- /dev/null +++ b/sa-token-dev/.gitignore @@ -0,0 +1,6 @@ +target/ + +node_modules/ +bin/ +.settings/ +unpackage/ \ No newline at end of file diff --git a/sa-token-dev/.project b/sa-token-dev/.project new file mode 100644 index 00000000..994c19a7 --- /dev/null +++ b/sa-token-dev/.project @@ -0,0 +1,23 @@ + + + sa-token-dev + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/sa-token-dev/pom.xml b/sa-token-dev/pom.xml new file mode 100644 index 00000000..90a11e14 --- /dev/null +++ b/sa-token-dev/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + cn.dev33 + sa-token-dev + 0.0.1-SNAPSHOT + + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + + + + \ No newline at end of file diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenManager.java b/sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenManager.java new file mode 100644 index 00000000..0f25e17b --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenManager.java @@ -0,0 +1,78 @@ +package cn.dev33.satoken; + +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.config.SaTokenConfigFactory; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.dao.SaTokenDaoDefault; +import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.stp.StpInterfaceDefaultImpl; + +/** + * 管理sa-token所有对象 + * @author kong + * + */ +public class SaTokenManager { + + // 配置文件 Bean + private static SaTokenConfig config; + public static SaTokenConfig getConfig() { + if (config == null) { + initConfig(); + } + return config; + } + public static void setConfig(SaTokenConfig config) { + SaTokenManager.config = config; + if(config.getIsV()) { + SaTokenUtil.printSaToken(); + } + } + public synchronized static void initConfig() { + if (config == null) { + setConfig(SaTokenConfigFactory.createConfig()); + } + } + + // 持久化 Bean + public static SaTokenDao dao; + public static SaTokenDao getDao() { + if (dao == null) { + initDao(); + } + return dao; + } + public static void setDao(SaTokenDao dao) { + SaTokenManager.dao = dao; + } + public synchronized static void initDao() { + if (dao == null) { + setDao(new SaTokenDaoDefault()); + } + } + + + // 权限认证 Bean + public static StpInterface stp; + public static StpInterface getStp() { + if (stp == null) { + initStp(); + } + return stp; + } + public static void setStp(StpInterface stp) { + SaTokenManager.stp = stp; + } + public synchronized static void initStp() { + if (stp == null) { + setStp(new StpInterfaceDefaultImpl()); + } + } + + + + + + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenUtil.java new file mode 100644 index 00000000..93590059 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/SaTokenUtil.java @@ -0,0 +1,30 @@ +package cn.dev33.satoken; + +/** + * sa-token 工具类 + */ +public class SaTokenUtil { + + + // sa-token 版本号 + public static final String version = "v1.0.0"; + + // sa-token 开源地址 + public static final String github_url = "https://github.com/click33/sa-token"; + + // 打印 sa-token + public static void printSaToken() { + String str = + "____ ____ ___ ____ _ _ ____ _ _ \r\n" + + "[__ |__| __ | | | |_/ |___ |\\ | \r\n" + + "___] | | | |__| | \\_ |___ | \\| \r\n" + + "sa-token:" + version + " \r\n" + + "GitHub:" + github_url + "\r\n"; + System.out.println(str); + } + + // 如果token为本次请求新创建的,则以此字符串为key存储在当前request中 + public static final String just_created_save_key= "just_created_save_key_"; + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java b/sa-token-dev/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java new file mode 100644 index 00000000..108dea56 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java @@ -0,0 +1,65 @@ +package cn.dev33.satoken.config; + +/** + * sa-token 总配置类 + */ +public class SaTokenConfig { + + private String tokenName = "satoken"; // token名称(同时也是cookie名称) + private long timeout = 30 * 24 * 60 * 60; // token有效期,单位s 默认30天,-1为永不过期 + private Boolean isShare = true; // 在多人登录同一账号时,是否共享会话(为true时共用一个,为false时新登录挤掉旧登录) + private Boolean isReadHead = false; // 是否在cookie读取不到token时,继续从请求header里继续尝试读取 + private Boolean isReadBody = false; // 是否在header读取不到token时,继续从请求题参数里继续尝试读取 + + private Boolean isV = true; // 是否在初始化配置时打印版本字符画 + + + public String getTokenName() { + return tokenName; + } + public void setTokenName(String tokenName) { + this.tokenName = tokenName; + } + public long getTimeout() { + return timeout; + } + public void setTimeout(long timeout) { + this.timeout = timeout; + } + public Boolean getIsShare() { + return isShare; + } + public void setIsShare(Boolean isShare) { + this.isShare = isShare; + } + public Boolean getIsReadHead() { + return isReadHead; + } + public void setIsReadHead(Boolean isReadHead) { + this.isReadHead = isReadHead; + } + public Boolean getIsReadBody() { + return isReadBody; + } + public void setIsReadBody(Boolean isReadBody) { + this.isReadBody = isReadBody; + } + public Boolean getIsV() { + return isV; + } + public void setIsV(Boolean isV) { + this.isV = isV; + } + + + @Override + public String toString() { + return "SaTokenConfig [tokenName=" + tokenName + ", timeout=" + timeout + ", isShare=" + isShare + + ", isReadHead=" + isReadHead + ", isReadBody=" + isReadBody + ", isV=" + isV + "]"; + } + + + + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/config/SaTokenConfigFactory.java b/sa-token-dev/src/main/java/cn/dev33/satoken/config/SaTokenConfigFactory.java new file mode 100644 index 00000000..ae3d39d4 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/config/SaTokenConfigFactory.java @@ -0,0 +1,128 @@ +package cn.dev33.satoken.config; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + 创建一个配置文件 + */ +public class SaTokenConfigFactory { + + + public static String configPath = "sa-token.properties"; // 默认配置文件地址 + + /** + * 根据指定路径获取配置信息 + * @return 一个SaTokenConfig对象 + */ + public static SaTokenConfig createConfig() { + Map map = readPropToMap(configPath); + if(map == null){ + // throw new RuntimeException("找不到配置文件:" + configPath, null); + } + return (SaTokenConfig)initPropByMap(map, new SaTokenConfig()); + } + + + + /** + * 将指定路径的properties配置文件读取到Map中 + * @param propertiesPath 配置文件地址 + * @return 一个Map + */ + private static Map readPropToMap(String propertiesPath){ + Map map = new HashMap(); + try { + InputStream is = SaTokenConfigFactory.class.getClassLoader().getResourceAsStream(propertiesPath); + if(is == null){ + return null; + } + Properties prop = new Properties(); + prop.load(is); + for (String key : prop.stringPropertyNames()) { + map.put(key, prop.getProperty(key)); + } + } catch (IOException e) { + throw new RuntimeException("配置文件(" + propertiesPath + ")加载失败", e); + } + return map; + } + + + /** + * 将 Map 的值映射到 Model 上 + * @param map 属性集合 + * @param obj 对象,或类型 + * @return 返回实例化后的对象 + */ + private static Object initPropByMap(Map map, Object obj){ + + if(map == null){ + map = new HashMap<>(); + } + + // 1、取出类型 + Class> cs = null; + if(obj instanceof Class){ // 如果是一个类型,则将obj=null,以便完成静态属性反射赋值 + cs = (Class>)obj; + obj = null; + }else{ // 如果是一个对象,则取出其类型 + cs = obj.getClass(); + } + + // 2、遍历类型属性,反射赋值 + for (Field field : cs.getDeclaredFields()) { + String value = map.get(field.getName()); + if (value == null) { + continue; // 如果为空代表没有配置此项 + } + try { + Object valueConvert = getObjectByClass(value, field.getType()); // 转换值类型 + field.setAccessible(true); + field.set(obj, valueConvert); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException("属性赋值出错:" + field.getName(), e); + } + } + return obj; + } + + /** + * 将字符串转化为指定数据类型 + * @param str 值 + * @param cs 要转换的类型 + * @return + */ + @SuppressWarnings("unchecked") + private static T getObjectByClass(String str, Class cs){ + Object value = null; + if(str == null){ + value = null; + }else if (cs.equals(String.class)) { + value = str; + } else if (cs.equals(int.class)||cs.equals(Integer.class)) { + value = new Integer(str); + } else if (cs.equals(long.class)||cs.equals(Long.class)) { + value = new Long(str); + } else if (cs.equals(short.class)||cs.equals(Short.class)) { + value = new Short(str); + } else if (cs.equals(float.class)||cs.equals(Float.class)) { + value = new Float(str); + } else if (cs.equals(double.class)||cs.equals(Double.class)) { + value = new Double(str); + } else if (cs.equals(boolean.class)||cs.equals(Boolean.class)) { + value = new Boolean(str); + }else{ + throw new RuntimeException("未能将值:" + str + ",转换类型为:" + cs, null); + } + return (T)value; + } + + + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java b/sa-token-dev/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java new file mode 100644 index 00000000..54e99b5e --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/dao/SaTokenDao.java @@ -0,0 +1,61 @@ +package cn.dev33.satoken.dao; + +import cn.dev33.satoken.session.SaSession; + +/** + * sa-token持久层的接口 + */ +public interface SaTokenDao { + + + /** + * 根据key获取value ,如果没有,则返回空 + * @param key 键名称 + * @return + */ + public String getValue(String key); + + /** + * 写入指定key-value键值对,并设定过期时间 (单位:秒) + * @param key 键名称 + * @param value 值 + * @param timeout 过期时间,单位:s + */ + public void setValue(String key, String value, long timeout); + + /** + * 删除一个指定的key + * @param key + */ + public void delKey(String key); + + + + /** + * 根据指定key的session,如果没有,则返回空 + * @param key 键名称 + * @return + */ + public SaSession getSaSession(String sessionId); + + /** + * 将指定session持久化 + * @param key 键名称 + * @param value 值 + * @param timeout 过期时间,单位: s + */ + public void saveSaSession(SaSession session, long timeout); + + /** + * 更新指定session + */ + public void updateSaSession(SaSession session); + + /** + * 删除一个指定的session + * @param key 键名称 + */ + public void delSaSession(String sessionId); + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefault.java b/sa-token-dev/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefault.java new file mode 100644 index 00000000..ee195dbc --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/dao/SaTokenDaoDefault.java @@ -0,0 +1,62 @@ +package cn.dev33.satoken.dao; + +import java.util.HashMap; +import java.util.Map; + +import cn.dev33.satoken.session.SaSession; + +/** + * sa-token持久层默认的实现类 , 基于内存Map + */ +public class SaTokenDaoDefault implements SaTokenDao { + + /** + * 所有数据集合 + */ + Map dataMap = new HashMap(); + + + @Override + public String getValue(String key) { + return (String)dataMap.get(key); + } + + @Override + public void setValue(String key, String value, long timeout) { + dataMap.put(key, value); + } + + @Override + public void delKey(String key) { + dataMap.remove(key); + } + + + @Override + public SaSession getSaSession(String sessionId) { + return (SaSession)dataMap.get(sessionId); + } + + @Override + public void saveSaSession(SaSession session, long timeout) { + dataMap.put(session.getId(), session); + } + + @Override + public void updateSaSession(SaSession session) { + // 无动作 + } + + @Override + public void delSaSession(String sessionId) { + dataMap.remove(sessionId); + } + + + + + + + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/exception/NotLoginException.java b/sa-token-dev/src/main/java/cn/dev33/satoken/exception/NotLoginException.java new file mode 100644 index 00000000..a9d67c84 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/exception/NotLoginException.java @@ -0,0 +1,21 @@ +package cn.dev33.satoken.exception; + +/** + * 没有登陆抛出的异常 + */ +public class NotLoginException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 6806129545290130142L; + + /** + * 创建一个 + */ + public NotLoginException() { + super("当前账号未登录"); + } + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/exception/NotPermissionException.java b/sa-token-dev/src/main/java/cn/dev33/satoken/exception/NotPermissionException.java new file mode 100644 index 00000000..d7d4d221 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/exception/NotPermissionException.java @@ -0,0 +1,33 @@ +package cn.dev33.satoken.exception; + +/** + * 没有指定权限码,抛出的异常 + */ +public class NotPermissionException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 6806129545290130142L; + + private Object code; + + + /** + * @return 获得权限码 + */ + public Object getCode() { + return code; + } + + public NotPermissionException(Object code) { + super("无此权限:" + code); + this.code = code; + } + +// public NotPermissionException(Object code, String s) { +// super(s); +// this.code = code; +// } + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/session/SaSession.java b/sa-token-dev/src/main/java/cn/dev33/satoken/session/SaSession.java new file mode 100644 index 00000000..bb812c32 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/session/SaSession.java @@ -0,0 +1,129 @@ +package cn.dev33.satoken.session; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import cn.dev33.satoken.SaTokenManager; + + +/** + * session会话 + * @author kong + * + */ +public class SaSession implements Serializable { + + private static final long serialVersionUID = 1L; + + private String id; // 会话id + private long createTime; // 当前会话创建时间 + private Map dataMap; // 当前会话键值对 + + + /** + * 构建一个 session对象 + * @param id + */ + public SaSession(String id) { + this.id = id; + this.createTime = System.currentTimeMillis(); + this.dataMap = new HashMap(); + } + + /** + * 获取会话id + * @return + */ + public String getId() { + return id; + } + + /** + * 当前会话创建时间 + */ + public long getCreateTime() { + return createTime; + } + + /** + * 写入值 + */ + public void setAttribute(String key, Object value) { + dataMap.put(key, value); + update(); + } + + /** + * 取值 + */ + public Object getAttribute(String key) { + return dataMap.get(key); + } + + /** + * 取值,并指定取不到值时的默认值 + */ + public Object getAttribute(String key, Object default_value) { + Object value = getAttribute(key); + if(value != null) { + return value; + } + return default_value; + } + + + /** + * 移除一个key + */ + public void removeAttribute(String key) { + dataMap.remove(key); + update(); + } + + /** + * 清空所有key + */ + public void clearAttribute() { + dataMap.clear(); + update(); + } + + /** + * 是否含有指定key + */ + public boolean containsAttribute(String key) { + return dataMap.keySet().contains(key); + } + + /** + * 当前session会话所有key + */ + public Set getAttributeKeys() { + return dataMap.keySet(); + } + + /** + * 获取数据集合(如果更新map里的值,请调用session.update()方法避免数据过时 ) + */ + public Map getDataMap() { + return dataMap; + } + + /** + * 将这个session从持久库更新一下 + */ + public void update() { + SaTokenManager.getDao().updateSaSession(this); + } + + +// /** 注销会话(注销后,此session会话将不再存储服务器上) */ +// public void logout() { +// SaTokenManager.getDao().delSaSession(this.id); +// } + + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/session/SaSessionUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/session/SaSessionUtil.java new file mode 100644 index 00000000..ceb57e26 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/session/SaSessionUtil.java @@ -0,0 +1,44 @@ +package cn.dev33.satoken.session; + +import cn.dev33.satoken.SaTokenManager; + +/** + * sa-session工具类 + * @author kong + * + */ +public class SaSessionUtil { + + // 添加上指定前缀,防止恶意伪造session + public static String session_key = "custom"; + public static String getSessionKey(String sessionId) { + return SaTokenManager.getConfig().getTokenName() + ":" + session_key + ":session:" + sessionId; + } + + /** 指定key的session是否存在 */ + public boolean isExists(String sessionId) { + return SaTokenManager.getDao().getSaSession(getSessionKey(sessionId)) != null; + } + + /** 获取指定key的session, 如果没有,is_create=是否新建并返回 */ + public static SaSession getSessionById(String sessionId, boolean is_create) { + SaSession session = SaTokenManager.getDao().getSaSession(getSessionKey(sessionId)); + if(session == null && is_create) { + session = new SaSession(getSessionKey(sessionId)); + SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout()); + } + return session; + } + /** 获取指定key的session, 如果没有则新建并返回 */ + public static SaSession getSessionById(String sessionId) { + return getSessionById(sessionId, true); + } + + /** 删除指定key的session */ + public static void delSessionById(String sessionId) { + SaTokenManager.getDao().delSaSession(getSessionKey(sessionId)); + } + + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/spring/SaTokenSetup.java b/sa-token-dev/src/main/java/cn/dev33/satoken/spring/SaTokenSetup.java new file mode 100644 index 00000000..9f839b44 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/spring/SaTokenSetup.java @@ -0,0 +1,21 @@ +package cn.dev33.satoken.spring; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * 将此注解加到springboot启动类上,即可完成sa-token与springboot的集成 + */ +@Documented +@Target({java.lang.annotation.ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Configuration +@Import({SpringSaToken.class}) +public @interface SaTokenSetup { + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/spring/SpringSaToken.java b/sa-token-dev/src/main/java/cn/dev33/satoken/spring/SpringSaToken.java new file mode 100644 index 00000000..def6d041 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/spring/SpringSaToken.java @@ -0,0 +1,48 @@ +package cn.dev33.satoken.spring; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +import cn.dev33.satoken.SaTokenManager; +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.stp.StpInterface; + +/** + * 与SpringBoot集成, 保证此类被扫描,即可完成sa-token与SpringBoot的集成 + * @author kongyongshun + * + */ +@Component +public class SpringSaToken { + + + // 获取配置Bean + @Bean + @ConfigurationProperties(prefix="spring.sa-token") + public SaTokenConfig getSaTokenConfig() { + return new SaTokenConfig(); + } + + // 注入配置Bean + @Autowired + public void setConfig(SaTokenConfig saTokenConfig){ + SaTokenManager.setConfig(saTokenConfig); + } + + // 注入持久化Bean + @Autowired(required = false) + public void setDao(SaTokenDao dao){ + SaTokenManager.setDao(dao); + } + + // 注入权限认证Bean + @Autowired(required = false) + public void setStp(StpInterface stp){ + SaTokenManager.setStp(stp); + } + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpInterface.java b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpInterface.java new file mode 100644 index 00000000..005c37e3 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpInterface.java @@ -0,0 +1,14 @@ +package cn.dev33.satoken.stp; + +import java.util.List; + +/** + * 开放权限验证接口,方便重写 + */ +public interface StpInterface { + + /** 返回指定login_id所拥有的权限码集合 */ + public List getPermissionCodeList(Object login_id, String login_key); + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpInterfaceDefaultImpl.java b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpInterfaceDefaultImpl.java new file mode 100644 index 00000000..aed2fb5d --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpInterfaceDefaultImpl.java @@ -0,0 +1,16 @@ +package cn.dev33.satoken.stp; + +import java.util.ArrayList; +import java.util.List; + +/** + * 权限验证接口 ,默认实现 + */ +public class StpInterfaceDefaultImpl implements StpInterface { + + @Override + public List getPermissionCodeList(Object login_id, String login_key) { + return new ArrayList(); + } + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpLogic.java new file mode 100644 index 00000000..022f681d --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -0,0 +1,306 @@ +package cn.dev33.satoken.stp; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import cn.dev33.satoken.SaTokenManager; +import cn.dev33.satoken.SaTokenUtil; +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.util.SaCookieUtil; +import cn.dev33.satoken.util.SpringMVCUtil; + +/** + * sa-token 权限验证,逻辑 实现类 + * + * (stp = sa-token-permission 的缩写 ) + * + */ +public class StpLogic { + + + private String login_key = ""; // 持久化的key前缀,多账号体系时以此值区分,比如:login、user、admin + + public StpLogic(String login_key) { + this.login_key = login_key; + } + + // =================== 获取token 相关 =================== + + + /** 随机生成一个tokenValue */ + public String randomTokenValue() { + return UUID.randomUUID().toString(); + } + + /** 获取当前tokenValue */ + public String getTokenValue(){ + // 0、获取相应对象 + HttpServletRequest request = SpringMVCUtil.getRequest(); + SaTokenConfig config = SaTokenManager.getConfig(); + String key_tokenName = getKey_tokenName(); + + // 1、尝试从request里读取 + if(request.getAttribute(SaTokenUtil.just_created_save_key) != null) { + return String.valueOf(request.getAttribute(SaTokenUtil.just_created_save_key)); + } + + // 2、尝试从cookie里读取 + Cookie cookie = SaCookieUtil.getCookie(request, key_tokenName); + if(cookie != null){ + String tokenValue = cookie.getValue(); + if(tokenValue != null) { + return tokenValue; + } + } + + // 3、尝试从header力读取 + if(config.getIsReadHead() == true){ + String tokenValue = request.getHeader(key_tokenName); + if(tokenValue != null) { + return tokenValue; + } + } + + // 4、尝试从请求体里面读取 + if(config.getIsReadBody() == true){ + String tokenValue = request.getParameter(key_tokenName); + if(tokenValue != null) { + return tokenValue; + } + } + + // 5、都读取不到,那算了吧还是 + return null; + } + + /** 获取指定id的tokenValue */ + public String getTokenValueByLoginId(Object login_id) { + return SaTokenManager.getDao().getValue(getKey_LoginId(login_id)); + } + + /** 获取当前会话的token信息:tokenName与tokenValue */ + public Map getTokenInfo() { + Map map = new HashMap(); + map.put("tokenName", getKey_tokenName()); + map.put("tokenValue", getTokenValue()); + return map; + } + + + // =================== 登录相关操作 =================== + + /** 在当前会话上登录id ,建议的类型:(long | int | String) */ + public void setLoginId(Object login_id) { + + // 1、获取相应对象 + HttpServletRequest request = SpringMVCUtil.getRequest(); + SaTokenConfig config = SaTokenManager.getConfig(); + SaTokenDao dao = SaTokenManager.getDao(); + + // 2、获取tokenValue + String tokenValue = getTokenValueByLoginId(login_id); // 获取旧tokenValue + if(tokenValue == null){ // 为null则创建一个新的 + tokenValue = randomTokenValue(); + } else { + // 不为null, 并且配置不共享,则删掉原来,并且创建新的 + if(config.getIsShare() == false){ + dao.delKey(getKey_TokenValue(tokenValue)); + tokenValue = randomTokenValue(); + } + } + + // 3、持久化 + dao.setValue(getKey_TokenValue(tokenValue), String.valueOf(login_id), config.getTimeout()); // token -> uid + dao.setValue(getKey_LoginId(login_id), tokenValue, config.getTimeout()); // uid -> token + request.setAttribute(SaTokenUtil.just_created_save_key, tokenValue); // 保存到本次request里 + SaCookieUtil.addCookie(SpringMVCUtil.getResponse(), getKey_tokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入 + } + + /** 当前会话注销登录 */ + public void logout() { + Object login_id = getLoginId_defaultNull(); + if(login_id != null) { + logoutByLoginId(login_id); + SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie + } + } + + /** 指定login_id的会话注销登录(踢人下线) */ + public void logoutByLoginId(Object login_id) { + + // 获取相应tokenValue + String tokenValue = getTokenValueByLoginId(login_id); + if(tokenValue == null) { + return; + } + + // 清除相关数据 + SaTokenManager.getDao().delKey(getKey_TokenValue(tokenValue)); // 清除token-id键值对 + SaTokenManager.getDao().delKey(getKey_LoginId(login_id)); // 清除id-token键值对 + SaTokenManager.getDao().delKey(getKey_session(login_id)); // 清除其session + // SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie + } + + // 查询相关 + + /** 获取当前会话是否已经登录 */ + public boolean isLogin() { + return getLoginId_defaultNull() != null; + } + + /** 获取当前会话登录id, 如果未登录,则抛出异常 */ + public Object getLoginId() { + Object login_id = getLoginId_defaultNull(); + if(login_id == null) { + throw new NotLoginException(); + } + return login_id; + } + + /** 获取当前会话登录id, 如果未登录,则返回默认值 */ + @SuppressWarnings("unchecked") + public T getLoginId(T default_value) { + Object login_id = getLoginId_defaultNull(); + if(login_id == null) { + return default_value; + } + return (T)login_id; + } + + /** 获取当前会话登录id, 如果未登录,则返回null */ + public Object getLoginId_defaultNull() { + String tokenValue = getTokenValue(); + if(tokenValue != null) { + Object login_id = SaTokenManager.getDao().getValue(getKey_TokenValue(tokenValue)); + if(login_id != null) { + return login_id; + } + } + return null; + } + + /** 获取当前会话登录id, 并转换为String */ + public String getLoginId_asString() { + return String.valueOf(getLoginId()); + } + + /** 获取当前会话登录id, 并转换为int */ + public int getLoginId_asInt() { + // Object login_id = getLoginId(); +// if(login_id instanceof Integer) { +// return (Integer)login_id; +// } + return Integer.valueOf(String.valueOf(getLoginId())); + } + + /** 获取当前会话登录id, 并转换为long */ + public long getLoginId_asLong() { +// Object login_id = getLoginId(); +// if(login_id instanceof Long) { +// return (Long)login_id; +// } + return Long.valueOf(String.valueOf(getLoginId())); + } + + + + // =================== session相关 =================== + + /** 获取指定key的session, 如果没有,is_create=是否新建并返回 */ + protected SaSession getSessionBySessionId(String sessionId, boolean is_create) { + SaSession session = SaTokenManager.getDao().getSaSession(sessionId); + if(session == null && is_create) { + session = new SaSession(sessionId); + SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout()); + } + return session; + } + + /** 获取指定login_id的session */ + public SaSession getSessionByLoginId(Object login_id) { + return getSessionBySessionId(getKey_session(login_id), false); + } + + /** 获取当前会话的session */ + public SaSession getSession() { + return getSessionBySessionId(getKey_session(getLoginId()), true); + } + + + + // =================== 权限验证操作 =================== + + /** 指定login_id是否含有指定权限 */ + public boolean hasPermission(Object login_id, Object pcode) { + List pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key); + return !(pcodeList == null || pcodeList.contains(pcode) == false); + } + + /** 当前会话是否含有指定权限 */ + public boolean hasPermission(Object pcode) { + return hasPermission(getLoginId(), pcode); + } + + /** 当前账号是否含有指定权限 , 没有就抛出异常 */ + public void checkPermission(Object pcode) { + if(hasPermission(pcode) == false) { + throw new NotPermissionException(pcode); + } + } + + /** 当前账号是否含有指定权限 , 【指定多个,必须全都有】 */ + public void checkPermissionAnd(Object... pcodeArray){ + Object login_id = getLoginId(); + List pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key); + for (Object pcode : pcodeArray) { + if(pcodeList.contains(pcode) == false) { + throw new NotPermissionException(pcode); // 没有权限抛出异常 + } + } + } + + /** 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】 */ + public void checkPermissionOr(Object... pcodeArray){ + Object login_id = getLoginId(); + List pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key); + for (Object pcode : pcodeArray) { + if(pcodeList.contains(pcode) == true) { + return; // 有的话提前退出 + } + } + if(pcodeArray.length > 0) { + throw new NotPermissionException(pcodeArray[0]); // 没有权限抛出异常 + } + } + + + // =================== 返回相应key =================== + + /** 获取key:客户端 tokenName */ + public String getKey_tokenName() { + return SaTokenManager.getConfig().getTokenName(); + } + /** 获取key: tokenValue 持久化 */ + public String getKey_TokenValue(String tokenValue) { + return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":token:" + tokenValue; + } + /** 获取key: id 持久化 */ + public String getKey_LoginId(Object login_id) { + return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":id:" + login_id; + } + /** 获取key: session 持久化 */ + public String getKey_session(Object login_id) { + return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":session:" + login_id; + } + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java new file mode 100644 index 00000000..40058e47 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java @@ -0,0 +1,131 @@ +package cn.dev33.satoken.stp; + +import java.util.Map; + +import org.springframework.stereotype.Service; + +import cn.dev33.satoken.session.SaSession; + +/** + * 一个默认的实现 + */ +@Service +public class StpUtil { + + // 底层的 StpLogic 对象 + public static StpLogic stpLogic = new StpLogic("login"); + + + // =================== 获取token 相关 =================== + + + /** 获取当前tokenValue */ + public static String getTokenValue() { + return stpLogic.getTokenValue(); + } + + /** 获取指定id的tokenValue */ + public static String getTokenValueByLoginId(Object login_id) { + return stpLogic.getTokenValueByLoginId(login_id); + } + + /** 获取当前会话的token信息:tokenName与tokenValue */ + public static Map getTokenInfo() { + return stpLogic.getTokenInfo(); + } + + // =================== 登录相关操作 =================== + + /** 在当前会话上设置登录id,建议的类型:(long | int | String) */ + public static void setLoginId(Object login_id) { + stpLogic.setLoginId(login_id); + } + + /** 当前会话注销登录 */ + public static void logout() { + stpLogic.logout(); + } + + /** 指定login_id的会话注销登录(踢人下线) */ + public static void logoutByLoginId(Object login_id) { + stpLogic.logoutByLoginId(login_id); + } + + // 查询相关 + + /** 获取当前会话是否已经登录 */ + public static boolean isLogin() { + return stpLogic.isLogin(); + } + + /** 获取当前会话登录id, 如果未登录,则抛出异常 */ + public static Object getLoginId() { + return stpLogic.getLoginId(); + } + + /** 获取当前会话登录id, 如果未登录,则返回默认值 */ + public static T getLoginId(T default_value) { + return stpLogic.getLoginId(default_value); + } + + /** 获取当前会话登录id, 如果未登录,则返回null */ + public static Object getLoginId_defaultNull() { + return stpLogic.getLoginId_defaultNull(); + } + + /** 获取当前会话登录id, 并转换为String */ + public static String getLoginId_asString() { + return stpLogic.getLoginId_asString(); + } + + /** 获取当前会话登录id, 并转换为int */ + public static int getLoginId_asInt() { + return stpLogic.getLoginId_asInt(); + } + + /** 获取当前会话登录id, 并转换为long */ + public static long getLoginId_asLong() { + return stpLogic.getLoginId_asLong(); + } + + // =================== session相关 =================== + + /** 获取指定login_id的session */ + public static SaSession getSessionByLoginId(Object login_id) { + return stpLogic.getSessionByLoginId(login_id); + } + + /** 获取当前会话的session */ + public static SaSession getSession() { + return stpLogic.getSession(); + } + + // =================== 权限验证操作 =================== + + /** 指定login_id是否含有指定权限 */ + public static boolean hasPermission(Object login_id, Object pcode) { + return stpLogic.hasPermission(login_id, pcode); + } + + /** 当前会话是否含有指定权限 */ + public static boolean hasPermission(Object pcode) { + return stpLogic.hasPermission(pcode); + } + + /** 当前账号是否含有指定权限 , 没有就抛出异常 */ + public static void checkPermission(Object pcode) { + stpLogic.checkPermission(pcode); + } + + /** 当前账号是否含有指定权限 , 【指定多个,必须全都有】 */ + public static void checkPermissionAnd(Object... pcodeArray) { + stpLogic.checkPermissionAnd(pcodeArray); + } + + /** 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】 */ + public static void checkPermissionOr(Object... pcodeArray) { + stpLogic.checkPermissionOr(pcodeArray); + } + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/util/SaCookieUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SaCookieUtil.java new file mode 100644 index 00000000..1943d8c4 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SaCookieUtil.java @@ -0,0 +1,80 @@ +package cn.dev33.satoken.util; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * cookie工具类 + * @author kong + * + */ +public class SaCookieUtil { + + + /** + * 获取指定cookie + */ + public static Cookie getCookie(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if(cookies != null) { + for(int i = 0; i < cookies.length; i++) { + Cookie cookie = cookies[i]; + if(cookie != null && cookieName.equals(cookie.getName())) { + return cookie; + } + } + } + return null; + } + + + /** + * 添加cookie + */ + public static void addCookie(HttpServletResponse response,String name,String value,String path,int timeout) { + Cookie cookie = new Cookie(name, value); + if(path == null) { + path = "/"; + } + cookie.setPath(path); + cookie.setMaxAge(timeout); + response.addCookie(cookie); + } + + + /** + * 删除cookie + */ + public static void delCookie(HttpServletRequest request,HttpServletResponse response,String name) { + Cookie[] cookies = request.getCookies(); + if(cookies != null){ + for(Cookie cookie : cookies) { + if(cookies != null && (name).equals(cookie.getName())) { + addCookie(response,name,null,null,0); + return; + } + } + } + } + + + /** + * 修改cookie的value值 + */ + public static void updateCookie(HttpServletRequest request,HttpServletResponse response,String name,String value) { + Cookie[] cookies = request.getCookies(); + if(cookies != null){ + for(Cookie cookie : cookies) { + if(cookies != null && (name).equals(cookie.getName())) { + addCookie(response,name,value,cookie.getPath(),cookie.getMaxAge()); + return; + } + } + } + } + + + +} \ No newline at end of file diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/util/SpringMVCUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SpringMVCUtil.java new file mode 100644 index 00000000..124715ba --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SpringMVCUtil.java @@ -0,0 +1,34 @@ +package cn.dev33.satoken.util; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * SpringMVC相关操作 + * @author kong + * + */ +public class SpringMVCUtil { + + // 获取当前会话的 request + public static HttpServletRequest getRequest() { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装 + if(servletRequestAttributes == null) { + throw new RuntimeException("当前环境非JavaWeb"); + } + return servletRequestAttributes.getRequest(); + } + + // 获取当前会话的 + public static HttpServletResponse getResponse() { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装 + if(servletRequestAttributes == null) { + throw new RuntimeException("当前环境非JavaWeb"); + } + return servletRequestAttributes.getResponse(); + } + +} diff --git a/sa-token-dev/src/main/java/com/pj/SaTokenApplication.java b/sa-token-dev/src/main/java/com/pj/SaTokenApplication.java new file mode 100644 index 00000000..472af75a --- /dev/null +++ b/sa-token-dev/src/main/java/com/pj/SaTokenApplication.java @@ -0,0 +1,18 @@ +package com.pj; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import cn.dev33.satoken.SaTokenManager; +import cn.dev33.satoken.spring.SaTokenSetup; + +@SaTokenSetup // 标注启动 sa-token +@SpringBootApplication +public class SaTokenApplication { + + public static void main(String[] args) { + SpringApplication.run(SaTokenApplication.class, args); // run--> + System.out.println(SaTokenManager.getConfig()); + } + +} \ No newline at end of file diff --git a/sa-token-dev/src/main/resources/application.yml b/sa-token-dev/src/main/resources/application.yml new file mode 100644 index 00000000..36a3adec --- /dev/null +++ b/sa-token-dev/src/main/resources/application.yml @@ -0,0 +1,20 @@ +# 端口 +server: + port: 8081 + +spring: + # sa-token配置 + sa-token: + # token名称(同时也是cookie名称) + token-name: satoken + # token有效期,单位s 默认30天,-1为永不过期 + timeout: 2592000 + # 在多人登录同一账号时,是否共享会话(为true时共用一个,为false时新登录挤掉旧登录) + is-share: true + # 是否在cookie读取不到token时,继续从请求header里继续尝试读取 + is-read-head: true + # 是否在header读取不到token时,继续从请求题参数里继续尝试读取 + is-read-body: true + # 是否在初始化配置时打印版本字符画 + is-v: true + \ No newline at end of file diff --git a/sa-token-doc/index.html b/sa-token-doc/index.html new file mode 100644 index 00000000..0a170b28 --- /dev/null +++ b/sa-token-doc/index.html @@ -0,0 +1,11 @@ + + + + + + sa-token在线文档 + + + sa-token在线文档 + + \ No newline at end of file
+ * (stp = sa-token-permission 的缩写 ) + * + */ +public class StpLogic { + + + private String login_key = ""; // 持久化的key前缀,多账号体系时以此值区分,比如:login、user、admin + + public StpLogic(String login_key) { + this.login_key = login_key; + } + + // =================== 获取token 相关 =================== + + + /** 随机生成一个tokenValue */ + public String randomTokenValue() { + return UUID.randomUUID().toString(); + } + + /** 获取当前tokenValue */ + public String getTokenValue(){ + // 0、获取相应对象 + HttpServletRequest request = SpringMVCUtil.getRequest(); + SaTokenConfig config = SaTokenManager.getConfig(); + String key_tokenName = getKey_tokenName(); + + // 1、尝试从request里读取 + if(request.getAttribute(SaTokenUtil.just_created_save_key) != null) { + return String.valueOf(request.getAttribute(SaTokenUtil.just_created_save_key)); + } + + // 2、尝试从cookie里读取 + Cookie cookie = SaCookieUtil.getCookie(request, key_tokenName); + if(cookie != null){ + String tokenValue = cookie.getValue(); + if(tokenValue != null) { + return tokenValue; + } + } + + // 3、尝试从header力读取 + if(config.getIsReadHead() == true){ + String tokenValue = request.getHeader(key_tokenName); + if(tokenValue != null) { + return tokenValue; + } + } + + // 4、尝试从请求体里面读取 + if(config.getIsReadBody() == true){ + String tokenValue = request.getParameter(key_tokenName); + if(tokenValue != null) { + return tokenValue; + } + } + + // 5、都读取不到,那算了吧还是 + return null; + } + + /** 获取指定id的tokenValue */ + public String getTokenValueByLoginId(Object login_id) { + return SaTokenManager.getDao().getValue(getKey_LoginId(login_id)); + } + + /** 获取当前会话的token信息:tokenName与tokenValue */ + public Map getTokenInfo() { + Map map = new HashMap(); + map.put("tokenName", getKey_tokenName()); + map.put("tokenValue", getTokenValue()); + return map; + } + + + // =================== 登录相关操作 =================== + + /** 在当前会话上登录id ,建议的类型:(long | int | String) */ + public void setLoginId(Object login_id) { + + // 1、获取相应对象 + HttpServletRequest request = SpringMVCUtil.getRequest(); + SaTokenConfig config = SaTokenManager.getConfig(); + SaTokenDao dao = SaTokenManager.getDao(); + + // 2、获取tokenValue + String tokenValue = getTokenValueByLoginId(login_id); // 获取旧tokenValue + if(tokenValue == null){ // 为null则创建一个新的 + tokenValue = randomTokenValue(); + } else { + // 不为null, 并且配置不共享,则删掉原来,并且创建新的 + if(config.getIsShare() == false){ + dao.delKey(getKey_TokenValue(tokenValue)); + tokenValue = randomTokenValue(); + } + } + + // 3、持久化 + dao.setValue(getKey_TokenValue(tokenValue), String.valueOf(login_id), config.getTimeout()); // token -> uid + dao.setValue(getKey_LoginId(login_id), tokenValue, config.getTimeout()); // uid -> token + request.setAttribute(SaTokenUtil.just_created_save_key, tokenValue); // 保存到本次request里 + SaCookieUtil.addCookie(SpringMVCUtil.getResponse(), getKey_tokenName(), tokenValue, "/", (int)config.getTimeout()); // cookie注入 + } + + /** 当前会话注销登录 */ + public void logout() { + Object login_id = getLoginId_defaultNull(); + if(login_id != null) { + logoutByLoginId(login_id); + SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie + } + } + + /** 指定login_id的会话注销登录(踢人下线) */ + public void logoutByLoginId(Object login_id) { + + // 获取相应tokenValue + String tokenValue = getTokenValueByLoginId(login_id); + if(tokenValue == null) { + return; + } + + // 清除相关数据 + SaTokenManager.getDao().delKey(getKey_TokenValue(tokenValue)); // 清除token-id键值对 + SaTokenManager.getDao().delKey(getKey_LoginId(login_id)); // 清除id-token键值对 + SaTokenManager.getDao().delKey(getKey_session(login_id)); // 清除其session + // SaCookieUtil.delCookie(SpringMVCUtil.getRequest(), SpringMVCUtil.getResponse(), getKey_tokenName()); // 清除cookie + } + + // 查询相关 + + /** 获取当前会话是否已经登录 */ + public boolean isLogin() { + return getLoginId_defaultNull() != null; + } + + /** 获取当前会话登录id, 如果未登录,则抛出异常 */ + public Object getLoginId() { + Object login_id = getLoginId_defaultNull(); + if(login_id == null) { + throw new NotLoginException(); + } + return login_id; + } + + /** 获取当前会话登录id, 如果未登录,则返回默认值 */ + @SuppressWarnings("unchecked") + public T getLoginId(T default_value) { + Object login_id = getLoginId_defaultNull(); + if(login_id == null) { + return default_value; + } + return (T)login_id; + } + + /** 获取当前会话登录id, 如果未登录,则返回null */ + public Object getLoginId_defaultNull() { + String tokenValue = getTokenValue(); + if(tokenValue != null) { + Object login_id = SaTokenManager.getDao().getValue(getKey_TokenValue(tokenValue)); + if(login_id != null) { + return login_id; + } + } + return null; + } + + /** 获取当前会话登录id, 并转换为String */ + public String getLoginId_asString() { + return String.valueOf(getLoginId()); + } + + /** 获取当前会话登录id, 并转换为int */ + public int getLoginId_asInt() { + // Object login_id = getLoginId(); +// if(login_id instanceof Integer) { +// return (Integer)login_id; +// } + return Integer.valueOf(String.valueOf(getLoginId())); + } + + /** 获取当前会话登录id, 并转换为long */ + public long getLoginId_asLong() { +// Object login_id = getLoginId(); +// if(login_id instanceof Long) { +// return (Long)login_id; +// } + return Long.valueOf(String.valueOf(getLoginId())); + } + + + + // =================== session相关 =================== + + /** 获取指定key的session, 如果没有,is_create=是否新建并返回 */ + protected SaSession getSessionBySessionId(String sessionId, boolean is_create) { + SaSession session = SaTokenManager.getDao().getSaSession(sessionId); + if(session == null && is_create) { + session = new SaSession(sessionId); + SaTokenManager.getDao().saveSaSession(session, SaTokenManager.getConfig().getTimeout()); + } + return session; + } + + /** 获取指定login_id的session */ + public SaSession getSessionByLoginId(Object login_id) { + return getSessionBySessionId(getKey_session(login_id), false); + } + + /** 获取当前会话的session */ + public SaSession getSession() { + return getSessionBySessionId(getKey_session(getLoginId()), true); + } + + + + // =================== 权限验证操作 =================== + + /** 指定login_id是否含有指定权限 */ + public boolean hasPermission(Object login_id, Object pcode) { + List pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key); + return !(pcodeList == null || pcodeList.contains(pcode) == false); + } + + /** 当前会话是否含有指定权限 */ + public boolean hasPermission(Object pcode) { + return hasPermission(getLoginId(), pcode); + } + + /** 当前账号是否含有指定权限 , 没有就抛出异常 */ + public void checkPermission(Object pcode) { + if(hasPermission(pcode) == false) { + throw new NotPermissionException(pcode); + } + } + + /** 当前账号是否含有指定权限 , 【指定多个,必须全都有】 */ + public void checkPermissionAnd(Object... pcodeArray){ + Object login_id = getLoginId(); + List pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key); + for (Object pcode : pcodeArray) { + if(pcodeList.contains(pcode) == false) { + throw new NotPermissionException(pcode); // 没有权限抛出异常 + } + } + } + + /** 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】 */ + public void checkPermissionOr(Object... pcodeArray){ + Object login_id = getLoginId(); + List pcodeList = SaTokenManager.getStp().getPermissionCodeList(login_id, login_key); + for (Object pcode : pcodeArray) { + if(pcodeList.contains(pcode) == true) { + return; // 有的话提前退出 + } + } + if(pcodeArray.length > 0) { + throw new NotPermissionException(pcodeArray[0]); // 没有权限抛出异常 + } + } + + + // =================== 返回相应key =================== + + /** 获取key:客户端 tokenName */ + public String getKey_tokenName() { + return SaTokenManager.getConfig().getTokenName(); + } + /** 获取key: tokenValue 持久化 */ + public String getKey_TokenValue(String tokenValue) { + return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":token:" + tokenValue; + } + /** 获取key: id 持久化 */ + public String getKey_LoginId(Object login_id) { + return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":id:" + login_id; + } + /** 获取key: session 持久化 */ + public String getKey_session(Object login_id) { + return SaTokenManager.getConfig().getTokenName() + ":" + login_key + ":session:" + login_id; + } + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java new file mode 100644 index 00000000..40058e47 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/stp/StpUtil.java @@ -0,0 +1,131 @@ +package cn.dev33.satoken.stp; + +import java.util.Map; + +import org.springframework.stereotype.Service; + +import cn.dev33.satoken.session.SaSession; + +/** + * 一个默认的实现 + */ +@Service +public class StpUtil { + + // 底层的 StpLogic 对象 + public static StpLogic stpLogic = new StpLogic("login"); + + + // =================== 获取token 相关 =================== + + + /** 获取当前tokenValue */ + public static String getTokenValue() { + return stpLogic.getTokenValue(); + } + + /** 获取指定id的tokenValue */ + public static String getTokenValueByLoginId(Object login_id) { + return stpLogic.getTokenValueByLoginId(login_id); + } + + /** 获取当前会话的token信息:tokenName与tokenValue */ + public static Map getTokenInfo() { + return stpLogic.getTokenInfo(); + } + + // =================== 登录相关操作 =================== + + /** 在当前会话上设置登录id,建议的类型:(long | int | String) */ + public static void setLoginId(Object login_id) { + stpLogic.setLoginId(login_id); + } + + /** 当前会话注销登录 */ + public static void logout() { + stpLogic.logout(); + } + + /** 指定login_id的会话注销登录(踢人下线) */ + public static void logoutByLoginId(Object login_id) { + stpLogic.logoutByLoginId(login_id); + } + + // 查询相关 + + /** 获取当前会话是否已经登录 */ + public static boolean isLogin() { + return stpLogic.isLogin(); + } + + /** 获取当前会话登录id, 如果未登录,则抛出异常 */ + public static Object getLoginId() { + return stpLogic.getLoginId(); + } + + /** 获取当前会话登录id, 如果未登录,则返回默认值 */ + public static T getLoginId(T default_value) { + return stpLogic.getLoginId(default_value); + } + + /** 获取当前会话登录id, 如果未登录,则返回null */ + public static Object getLoginId_defaultNull() { + return stpLogic.getLoginId_defaultNull(); + } + + /** 获取当前会话登录id, 并转换为String */ + public static String getLoginId_asString() { + return stpLogic.getLoginId_asString(); + } + + /** 获取当前会话登录id, 并转换为int */ + public static int getLoginId_asInt() { + return stpLogic.getLoginId_asInt(); + } + + /** 获取当前会话登录id, 并转换为long */ + public static long getLoginId_asLong() { + return stpLogic.getLoginId_asLong(); + } + + // =================== session相关 =================== + + /** 获取指定login_id的session */ + public static SaSession getSessionByLoginId(Object login_id) { + return stpLogic.getSessionByLoginId(login_id); + } + + /** 获取当前会话的session */ + public static SaSession getSession() { + return stpLogic.getSession(); + } + + // =================== 权限验证操作 =================== + + /** 指定login_id是否含有指定权限 */ + public static boolean hasPermission(Object login_id, Object pcode) { + return stpLogic.hasPermission(login_id, pcode); + } + + /** 当前会话是否含有指定权限 */ + public static boolean hasPermission(Object pcode) { + return stpLogic.hasPermission(pcode); + } + + /** 当前账号是否含有指定权限 , 没有就抛出异常 */ + public static void checkPermission(Object pcode) { + stpLogic.checkPermission(pcode); + } + + /** 当前账号是否含有指定权限 , 【指定多个,必须全都有】 */ + public static void checkPermissionAnd(Object... pcodeArray) { + stpLogic.checkPermissionAnd(pcodeArray); + } + + /** 当前账号是否含有指定权限 , 【指定多个,有一个就可以了】 */ + public static void checkPermissionOr(Object... pcodeArray) { + stpLogic.checkPermissionOr(pcodeArray); + } + + +} diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/util/SaCookieUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SaCookieUtil.java new file mode 100644 index 00000000..1943d8c4 --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SaCookieUtil.java @@ -0,0 +1,80 @@ +package cn.dev33.satoken.util; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * cookie工具类 + * @author kong + * + */ +public class SaCookieUtil { + + + /** + * 获取指定cookie + */ + public static Cookie getCookie(HttpServletRequest request, String cookieName) { + Cookie[] cookies = request.getCookies(); + if(cookies != null) { + for(int i = 0; i < cookies.length; i++) { + Cookie cookie = cookies[i]; + if(cookie != null && cookieName.equals(cookie.getName())) { + return cookie; + } + } + } + return null; + } + + + /** + * 添加cookie + */ + public static void addCookie(HttpServletResponse response,String name,String value,String path,int timeout) { + Cookie cookie = new Cookie(name, value); + if(path == null) { + path = "/"; + } + cookie.setPath(path); + cookie.setMaxAge(timeout); + response.addCookie(cookie); + } + + + /** + * 删除cookie + */ + public static void delCookie(HttpServletRequest request,HttpServletResponse response,String name) { + Cookie[] cookies = request.getCookies(); + if(cookies != null){ + for(Cookie cookie : cookies) { + if(cookies != null && (name).equals(cookie.getName())) { + addCookie(response,name,null,null,0); + return; + } + } + } + } + + + /** + * 修改cookie的value值 + */ + public static void updateCookie(HttpServletRequest request,HttpServletResponse response,String name,String value) { + Cookie[] cookies = request.getCookies(); + if(cookies != null){ + for(Cookie cookie : cookies) { + if(cookies != null && (name).equals(cookie.getName())) { + addCookie(response,name,value,cookie.getPath(),cookie.getMaxAge()); + return; + } + } + } + } + + + +} \ No newline at end of file diff --git a/sa-token-dev/src/main/java/cn/dev33/satoken/util/SpringMVCUtil.java b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SpringMVCUtil.java new file mode 100644 index 00000000..124715ba --- /dev/null +++ b/sa-token-dev/src/main/java/cn/dev33/satoken/util/SpringMVCUtil.java @@ -0,0 +1,34 @@ +package cn.dev33.satoken.util; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * SpringMVC相关操作 + * @author kong + * + */ +public class SpringMVCUtil { + + // 获取当前会话的 request + public static HttpServletRequest getRequest() { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装 + if(servletRequestAttributes == null) { + throw new RuntimeException("当前环境非JavaWeb"); + } + return servletRequestAttributes.getRequest(); + } + + // 获取当前会话的 + public static HttpServletResponse getResponse() { + ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();// 大善人SpringMVC提供的封装 + if(servletRequestAttributes == null) { + throw new RuntimeException("当前环境非JavaWeb"); + } + return servletRequestAttributes.getResponse(); + } + +} diff --git a/sa-token-dev/src/main/java/com/pj/SaTokenApplication.java b/sa-token-dev/src/main/java/com/pj/SaTokenApplication.java new file mode 100644 index 00000000..472af75a --- /dev/null +++ b/sa-token-dev/src/main/java/com/pj/SaTokenApplication.java @@ -0,0 +1,18 @@ +package com.pj; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import cn.dev33.satoken.SaTokenManager; +import cn.dev33.satoken.spring.SaTokenSetup; + +@SaTokenSetup // 标注启动 sa-token +@SpringBootApplication +public class SaTokenApplication { + + public static void main(String[] args) { + SpringApplication.run(SaTokenApplication.class, args); // run--> + System.out.println(SaTokenManager.getConfig()); + } + +} \ No newline at end of file diff --git a/sa-token-dev/src/main/resources/application.yml b/sa-token-dev/src/main/resources/application.yml new file mode 100644 index 00000000..36a3adec --- /dev/null +++ b/sa-token-dev/src/main/resources/application.yml @@ -0,0 +1,20 @@ +# 端口 +server: + port: 8081 + +spring: + # sa-token配置 + sa-token: + # token名称(同时也是cookie名称) + token-name: satoken + # token有效期,单位s 默认30天,-1为永不过期 + timeout: 2592000 + # 在多人登录同一账号时,是否共享会话(为true时共用一个,为false时新登录挤掉旧登录) + is-share: true + # 是否在cookie读取不到token时,继续从请求header里继续尝试读取 + is-read-head: true + # 是否在header读取不到token时,继续从请求题参数里继续尝试读取 + is-read-body: true + # 是否在初始化配置时打印版本字符画 + is-v: true + \ No newline at end of file diff --git a/sa-token-doc/index.html b/sa-token-doc/index.html new file mode 100644 index 00000000..0a170b28 --- /dev/null +++ b/sa-token-doc/index.html @@ -0,0 +1,11 @@ + + + + + + sa-token在线文档 + + + sa-token在线文档 + + \ No newline at end of file