diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java index 76b195be..04251490 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/stp/StpLogic.java @@ -299,10 +299,16 @@ public class StpLogic { if(SaFoxUtil.isEmpty(tokenValue)) { return; } - // 如果打开了Cookie模式,第一步,先把cookie清除掉 + + // 从当前 [storage存储器] 里删除 + SaHolder.getStorage().delete(splicingKeyJustCreatedSave()); + + // 如果打开了Cookie模式,则把cookie清除掉 if(getConfig().getIsReadCookie()){ SaHolder.getResponse().deleteCookie(getTokenName()); } + + // 清除这个token的相关信息 logoutByTokenValue(tokenValue); } diff --git a/sa-token-demo/sa-token-demo-jwt/pom.xml b/sa-token-demo/sa-token-demo-jwt/pom.xml index b4b69519..5df94f6a 100644 --- a/sa-token-demo/sa-token-demo-jwt/pom.xml +++ b/sa-token-demo/sa-token-demo-jwt/pom.xml @@ -42,7 +42,7 @@ - + @@ -59,6 +59,13 @@ true + + + org.springframework.boot + spring-boot-starter-test + test + + diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStatelessTest.java b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStatelessTest.java new file mode 100644 index 00000000..36c02fc3 --- /dev/null +++ b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForStatelessTest.java @@ -0,0 +1,166 @@ +package com.pj.test; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.exception.SaTokenException; +import cn.dev33.satoken.jwt.SaJwtUtil; +import cn.dev33.satoken.jwt.StpLogicJwtForStateless; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaTokenConsts; +import cn.hutool.json.JSONObject; +import cn.hutool.jwt.JWT; + +/** + * Sa-Token 整合 jwt:stateless 模式 测试 + * + * + * @author kong + * + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = StartUpApplication.class) +public class JwtForStatelessTest { + + // 持久化Bean + static SaTokenDao dao; + + // 开始 + @BeforeClass + public static void beforeClass() { + System.out.println("\n\n------------------------ 基础测试 star ..."); + dao = SaManager.getSaTokenDao(); + StpUtil.setStpLogic(new StpLogicJwtForStateless()); + } + + // 结束 + @AfterClass + public static void afterClass() { + System.out.println("\n\n------------------------ 基础测试 end ... \n"); + } + + // 测试:登录 + @Test + public void doLogin() { + // 登录 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + + // API 验证 + Assert.assertTrue(StpUtil.isLogin()); + Assert.assertNotNull(token); // token不为null + Assert.assertEquals(StpUtil.getLoginIdAsLong(), 10001); // loginId=10001 + Assert.assertEquals(StpUtil.getLoginDevice(), SaTokenConsts.DEFAULT_LOGIN_DEVICE); // 登录设备 + + // token 验证 + JWT jwt = JWT.of(token); + JSONObject payloads = jwt.getPayloads(); + Assert.assertEquals(payloads.getStr(SaJwtUtil.LOGIN_ID), "10001"); // 账号 + Assert.assertEquals(payloads.getStr(SaJwtUtil.DEVICE), SaTokenConsts.DEFAULT_LOGIN_DEVICE); // 登录设备 + Assert.assertEquals(payloads.getStr(SaJwtUtil.LOGIN_TYPE), StpUtil.TYPE); // 账号类型 + + // 时间 + Assert.assertTrue(StpUtil.getTokenTimeout() <= SaManager.getConfig().getTimeout()); + Assert.assertTrue(StpUtil.getTokenTimeout() > SaManager.getConfig().getTimeout() - 10000); + + try { + // 尝试获取Session会抛出异常 + StpUtil.getSession(); + Assert.assertTrue(false); + } catch (Exception e) { + } + } + + // 测试:注销 + @Test + public void logout() { + // 登录 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + Assert.assertEquals(JWT.of(token).getPayloads().getStr("loginId"), "10001"); + + // 注销 + StpUtil.logout(); + + // token 应该被清除 + Assert.assertNull(StpUtil.getTokenValue()); + Assert.assertFalse(StpUtil.isLogin()); + } + + // 测试:Session会话 + @Test(expected = SaTokenException.class) + public void testSession() { + StpUtil.login(10001); + + // 会抛异常 + StpUtil.getSession(); + } + + // 测试:权限认证 + @Test + public void testCheckPermission() { + StpUtil.login(10001); + + // 权限认证 + Assert.assertTrue(StpUtil.hasPermission("user-add")); + Assert.assertTrue(StpUtil.hasPermission("user-list")); + Assert.assertTrue(StpUtil.hasPermission("user")); + Assert.assertTrue(StpUtil.hasPermission("art-add")); + Assert.assertFalse(StpUtil.hasPermission("get-user")); + // and + Assert.assertTrue(StpUtil.hasPermissionAnd("art-add", "art-get")); + Assert.assertFalse(StpUtil.hasPermissionAnd("art-add", "comment-add")); + // or + Assert.assertTrue(StpUtil.hasPermissionOr("art-add", "comment-add")); + Assert.assertFalse(StpUtil.hasPermissionOr("comment-add", "comment-delete")); + } + + // 测试:角色认证 + @Test + public void testCheckRole() { + StpUtil.login(10001); + + // 角色认证 + Assert.assertTrue(StpUtil.hasRole("admin")); + Assert.assertFalse(StpUtil.hasRole("teacher")); + // and + Assert.assertTrue(StpUtil.hasRoleAnd("admin", "super-admin")); + Assert.assertFalse(StpUtil.hasRoleAnd("admin", "ceo")); + // or + Assert.assertTrue(StpUtil.hasRoleOr("admin", "ceo")); + Assert.assertFalse(StpUtil.hasRoleOr("ceo", "cto")); + } + + // 测试:根据token强制注销 + @Test(expected = SaTokenException.class) + public void testLogoutByToken() { + + // 先登录上 + StpUtil.login(10001); + Assert.assertTrue(StpUtil.isLogin()); + String token = StpUtil.getTokenValue(); + + // 根据token注销 + StpUtil.logoutByTokenValue(token); + } + + // 测试:根据账号id强制注销 + @Test(expected = SaTokenException.class) + public void testLogoutByLoginId() { + + // 先登录上 + StpUtil.login(10001); + Assert.assertTrue(StpUtil.isLogin()); + + // 根据账号id注销 + StpUtil.logout(10001); + } + +} diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForTokenStyleTest.java b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForTokenStyleTest.java new file mode 100644 index 00000000..08c8f8fd --- /dev/null +++ b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/JwtForTokenStyleTest.java @@ -0,0 +1,75 @@ +package com.pj.test; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.jwt.StpLogicJwtForTokenStyle; +import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaTokenConsts; +import cn.hutool.json.JSONObject; +import cn.hutool.jwt.JWT; + +/** + * Sa-Token 整合 jwt:token-style 模式 测试 + * + * @author kong + * + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = StartUpApplication.class) +public class JwtForTokenStyleTest { + + // 持久化Bean + static SaTokenDao dao; + + // 开始 + @BeforeClass + public static void beforeClass() { + System.out.println("\n\n------------------------ TokenStyleTest star ..."); + dao = SaManager.getSaTokenDao(); + StpUtil.setStpLogic(new StpLogicJwtForTokenStyle()); + } + + // 结束 + @AfterClass + public static void afterClass() { + System.out.println("\n\n------------------------ TokenStyleTest end ... \n"); + } + + // 测试:登录 + @Test + public void doLogin() { + // 登录 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + + // API 验证 + Assert.assertTrue(StpUtil.isLogin()); + Assert.assertNotNull(token); // token不为null + Assert.assertEquals(StpUtil.getLoginIdAsLong(), 10001); // loginId=10001 + Assert.assertEquals(StpUtil.getLoginDevice(), SaTokenConsts.DEFAULT_LOGIN_DEVICE); // 登录设备 + + // token 验证 + JWT jwt = JWT.of(token); + JSONObject payloads = jwt.getPayloads(); + Assert.assertEquals(payloads.getStr("loginId"), "10001"); + + // db数据 验证 + // token存在 + Assert.assertEquals(dao.get("satoken:login:token:" + token), "10001"); + // Session 存在 + SaSession session = dao.getSession("satoken:login:session:" + 10001); + Assert.assertNotNull(session); + Assert.assertEquals(session.getId(), "satoken:login:session:" + 10001); + Assert.assertTrue(session.getTokenSignList().size() >= 1); + } + +} diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/StartUpApplication.java b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/StartUpApplication.java new file mode 100644 index 00000000..2a174efd --- /dev/null +++ b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/StartUpApplication.java @@ -0,0 +1,16 @@ +package com.pj.test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 启动类 + * @author Auster + * + */ +@SpringBootApplication +public class StartUpApplication { + public static void main(String[] args) { + SpringApplication.run(StartUpApplication.class, args); + } +} diff --git a/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java new file mode 100644 index 00000000..18130ce0 --- /dev/null +++ b/sa-token-demo/sa-token-demo-jwt/src/test/java/com/pj/test/satoken/StpInterfaceImpl.java @@ -0,0 +1,35 @@ +package com.pj.test.satoken; + +import java.util.Arrays; +import java.util.List; + +import org.springframework.stereotype.Component; + +import cn.dev33.satoken.stp.StpInterface; + +/** + * 自定义权限验证接口扩展 + * + * @author Auster + * + */ +@Component +public class StpInterfaceImpl implements StpInterface { + + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + return Arrays.asList("user*", "art-add", "art-delete", "art-update", "art-get"); + } + + /** + * 返回一个账号所拥有的角色标识集合 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + return Arrays.asList("admin", "super-admin"); + } + +} diff --git a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java index cb2b66ae..593e088e 100644 --- a/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java +++ b/sa-token-plugin/sa-token-jwt/src/main/java/cn/dev33/satoken/jwt/StpLogicJwtForStateless.java @@ -1,6 +1,7 @@ package cn.dev33.satoken.jwt; import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.SaTokenException; @@ -132,11 +133,14 @@ public class StpLogicJwtForStateless extends StpLogic { */ @Override public void logout() { - // stateless模式下清除Cookie即可 - - // 如果打开了cookie模式,把cookie清除掉 - if(getConfig().getIsReadCookie() == true){ - SaManager.getSaTokenContext().getResponse().deleteCookie(getTokenName()); + // ... + + // 从当前 [storage存储器] 里删除 + SaHolder.getStorage().delete(splicingKeyJustCreatedSave()); + + // 如果打开了Cookie模式,则把cookie清除掉 + if(getConfig().getIsReadCookie()){ + SaHolder.getResponse().deleteCookie(getTokenName()); } } diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/test/java/com/pj/test/BasicsTest.java b/sa-token-starter/sa-token-spring-boot-starter/src/test/java/com/pj/test/BasicsTest.java index 4258c241..ba9679ce 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/test/java/com/pj/test/BasicsTest.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/test/java/com/pj/test/BasicsTest.java @@ -81,6 +81,8 @@ public class BasicsTest { // 注销 StpUtil.logout(); // token 应该被清除 + Assert.assertNull(StpUtil.getTokenValue()); + Assert.assertFalse(StpUtil.isLogin()); Assert.assertNull(dao.get("satoken:login:token:" + token)); // Session 应该被清除 SaSession session = dao.getSession("satoken:login:session:" + 10001);