diff --git a/README.md b/README.md index 3aa3a0e..6695fab 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ There are some demo in every `integration-tests` module. - [x] quarkus-satoken-aop(in quarkus-satoken-resteasy) - [x] quarkus-satoken-dao-redis-jackson(based on quarkus-redisson) - [x] quarkus-satoken-context-dubbo -- [ ] quarkus-satoken-alone-redis +- [x] quarkus-satoken-alone-redis - [ ] quarkus-satoken-dao-redis - [ ] quarkus-satoken-dialect-thymeleaf - [ ] quarkus-satoken-jwt diff --git a/quarkus-satoken-plugins/pom.xml b/quarkus-satoken-plugins/pom.xml index 8fa03c2..a0e7b3d 100644 --- a/quarkus-satoken-plugins/pom.xml +++ b/quarkus-satoken-plugins/pom.xml @@ -10,6 +10,7 @@ quarkus-satoken-plugins pom + quarkus-satoken-dao-alone-redis quarkus-satoken-dao-redis-jackson quarkus-satoken-oauth2 quarkus-satoken-sso diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/pom.xml b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/pom.xml new file mode 100644 index 0000000..7ff073c --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + io.quarkiverse.satoken + quarkus-satoken-dao-alone-redis-parent + 1.30.0 + + quarkus-satoken-dao-alone-redis-deployment + Quarkus Satoken Dao Alone Redis - Deployment + + + io.quarkus + quarkus-arc-deployment + + + io.quarkiverse.satoken + quarkus-satoken-dao-alone-redis + ${project.version} + + + io.quarkiverse.satoken + quarkus-satoken-dao-redis-jackson-deployment + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/main/java/io/satoken/dao/alone/redis/deployment/QuarkusSatokenDaoAloneRedisProcessor.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/main/java/io/satoken/dao/alone/redis/deployment/QuarkusSatokenDaoAloneRedisProcessor.java new file mode 100644 index 0000000..25e5c95 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/main/java/io/satoken/dao/alone/redis/deployment/QuarkusSatokenDaoAloneRedisProcessor.java @@ -0,0 +1,26 @@ +package io.satoken.dao.alone.redis.deployment; + +import java.io.IOException; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.satoken.dao.alone.redis.runtime.AloneRedisRecorder; + +class QuarkusSatokenDaoAloneRedisProcessor { + + private static final String FEATURE = "satoken-dao-alone-redis"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + public SaTokenAloneRedisBuildItem configure(AloneRedisRecorder recorder) throws IOException { + recorder.initAloneRedis(); + return new SaTokenAloneRedisBuildItem(); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/main/java/io/satoken/dao/alone/redis/deployment/SaTokenAloneRedisBuildItem.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/main/java/io/satoken/dao/alone/redis/deployment/SaTokenAloneRedisBuildItem.java new file mode 100644 index 0000000..8ae74c9 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/main/java/io/satoken/dao/alone/redis/deployment/SaTokenAloneRedisBuildItem.java @@ -0,0 +1,12 @@ +package io.satoken.dao.alone.redis.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * SaTokenAloneRedisBuildItem + * + * @author nayan + * @date 2022/10/19 14:57 + */ +public final class SaTokenAloneRedisBuildItem extends SimpleBuildItem { +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/test/java/io/quarkus/satoken/dao/alone/redis/test/QuarkusSatokenDaoAloneRedisDevModeTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/test/java/io/quarkus/satoken/dao/alone/redis/test/QuarkusSatokenDaoAloneRedisDevModeTest.java new file mode 100644 index 0000000..0a3b8a6 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/test/java/io/quarkus/satoken/dao/alone/redis/test/QuarkusSatokenDaoAloneRedisDevModeTest.java @@ -0,0 +1,23 @@ +package io.quarkus.satoken.dao.alone.redis.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusDevModeTest; + +public class QuarkusSatokenDaoAloneRedisDevModeTest { + + // Start hot reload (DevMode) test with your extension loaded + @RegisterExtension + static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnDevModeTest() { + // Write your dev mode tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-hot-reload for more information + Assertions.assertTrue(true, "Add dev mode assertions to " + getClass().getName()); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/test/java/io/quarkus/satoken/dao/alone/redis/test/QuarkusSatokenDaoAloneRedisTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/test/java/io/quarkus/satoken/dao/alone/redis/test/QuarkusSatokenDaoAloneRedisTest.java new file mode 100644 index 0000000..9c08361 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/deployment/src/test/java/io/quarkus/satoken/dao/alone/redis/test/QuarkusSatokenDaoAloneRedisTest.java @@ -0,0 +1,23 @@ +package io.quarkus.satoken.dao.alone.redis.test; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; + +public class QuarkusSatokenDaoAloneRedisTest { + + // Start unit test with your extension loaded + @RegisterExtension + static final QuarkusUnitTest unitTest = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void writeYourOwnUnitTest() { + // Write your unit tests here - see the testing extension guide https://quarkus.io/guides/writing-extensions#testing-extensions for more information + Assertions.assertTrue(true, "Add some assertions to " + getClass().getName()); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/pom.xml b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/pom.xml new file mode 100644 index 0000000..51dde04 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/pom.xml @@ -0,0 +1,110 @@ + + + 4.0.0 + + io.quarkiverse.satoken + quarkus-satoken-dao-alone-redis-parent + 1.30.0 + + quarkus-satoken-dao-alone-redis-integration-tests + Quarkus Satoken Dao Alone Redis - Integration Tests + + true + + + + io.quarkiverse.satoken + quarkus-satoken-resteasy + ${project.version} + + + io.quarkiverse.satoken + quarkus-satoken-dao-alone-redis + ${project.version} + + + + io.quarkus + quarkus-redis-client + test + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.testcontainers + testcontainers + test + + + org.testcontainers + junit-jupiter + test + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-failsafe-plugin + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + + + native-image + + + native + + + + + + maven-surefire-plugin + + ${native.surefire.skip} + + + + + + false + native + + + + diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/RouterFilterConfiguration.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/RouterFilterConfiguration.java new file mode 100644 index 0000000..168f5ac --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/RouterFilterConfiguration.java @@ -0,0 +1,138 @@ +package io.quarkiverse.satoken.resteasy.it; + +import java.util.Arrays; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; + +import org.jboss.resteasy.reactive.server.core.CurrentRequestManager; +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.router.SaHttpMethod; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import io.quarkiverse.satoken.core.filter.SaRouteInterceptor; +import io.quarkus.arc.Priority; +import io.quarkus.arc.Unremovable; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * io.quarkiverse.satoken.resteasy.it.RouterFilterConfiguration + * + * @author nayan + * @date 2022/10/9 16:20 + */ +@Dependent +public class RouterFilterConfiguration { + + @Produces + @Unremovable + @Priority(1) + public SaRouteInterceptor configure2() { + return new SaRouteInterceptor("/**", handle -> { + + // 匹配 getInfo ,返回code=201 + SaRouter.match("/**") + .match(SaHttpMethod.POST) + .matchMethod("POST") + .match(SaHolder.getRequest().getMethod().equals("POST")) + .match(r -> SaHolder.getRequest().isPath("/rt/getInfo")) + .match(r -> SaHolder.getRequest().isParam("name", "zhang")) + .back(SaResult.code(201)); + + // 匹配 getInfo2 ,返回code=202 + SaRouter.match("/rt/getInfo2") + .match(Arrays.asList("/rt/getInfo2", "/rt/*")) + .notMatch("/rt/getInfo3") + .notMatch(false) + .notMatch(r -> false) + .notMatch(SaHttpMethod.GET) + .notMatchMethod("PUT") + .notMatch(Arrays.asList("/rt/getInfo4", "/rt/getInfo5")) + .back(SaResult.code(202)); + + // 匹配 getInfo3 ,返回code=203 + SaRouter.match("/rt/getInfo3", "/rt/getInfo4", () -> SaRouter.back(SaResult.code(203))); + SaRouter.match("/rt/getInfo4", "/rt/getInfo5", r -> SaRouter.back(SaResult.code(204))); + SaRouter.match("/rt/getInfo5", () -> SaRouter.back(SaResult.code(205))); + SaRouter.match("/rt/getInfo6", r -> SaRouter.back(SaResult.code(206))); + + // 通往 Controller + SaRouter.match(Arrays.asList("/rt/getInfo7")).stop(); + + // 通往 Controller + SaRouter.match("/rt/getInfo8", () -> SaRouter.stop()); + + SaRouter.matchMethod("POST").match("/rt/getInfo9").free(r -> SaRouter.back(SaResult.code(209))); + SaRouter.match(SaHttpMethod.POST).match("/rt/getInfo10").setHit(false).back(); + + // 11 + SaRouter.notMatch("/rt/getInfo11").reset().match("/rt/getInfo11").back(SaResult.code(211)); + SaRouter.notMatch(SaHttpMethod.GET).match("/rt/getInfo12").back(SaResult.code(212)); + SaRouter.notMatch(Arrays.asList("/rt/getInfo12", "/rt/getInfo14")).match("/rt/getInfo13").back(SaResult.code(213)); + SaRouter.notMatchMethod("GET", "PUT").match("/rt/getInfo14").back(SaResult.code(214)); + + // SaRouter.match(Arrays.asList("/rt/getInfo15", "/rt/getInfo16")) + ResteasyReactiveRequestContext resteasyReactiveRequestContext = CurrentRequestManager.get(); + if (SaRouter.isMatchCurrURI("/rt/getInfo15")) { + if (SaHolder.getRequest().getCookieValue("ddd") == null + && SaHolder.getStorage().getSource() == resteasyReactiveRequestContext + && SaHolder.getRequest().getSource() == resteasyReactiveRequestContext + && SaHolder.getResponse().getSource() == resteasyReactiveRequestContext.serverResponse()) { + SaRouter.newMatch().free(r -> SaRouter.back(SaResult.code(215))); + } + } + + SaRouter.match("/rt/getInfo16", () -> { + try { + SaHolder.getResponse().redirect(null); + } catch (Exception e) { + + } + SaHolder.getResponse().redirect("/rt/getInfo3"); + }); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(100) + public SaRouteInterceptor configure31() { + return new SaRouteInterceptor("/rt/getInfo_200", handle -> { + SaRouter.stop(); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(100) + public SaRouteInterceptor configure32() { + return new SaRouteInterceptor("/rt/getInfo_201", handle -> { + StpUtil.checkLogin(); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(101) + public SaRouteInterceptor configure33() { + return new SaRouteInterceptor("/rt/getInfo_201", handle -> { + SaRouter.back(SaResult.code(201)); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(100) + public SaRouteInterceptor configure34() { + return new SaRouteInterceptor("/rt/getInfo_202", handle -> { + StpUtil.checkLogin(); + }); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/StpInterfaceConfiguration.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/StpInterfaceConfiguration.java new file mode 100644 index 0000000..ef5b684 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/StpInterfaceConfiguration.java @@ -0,0 +1,90 @@ +package io.quarkiverse.satoken.resteasy.it; + +import java.util.Arrays; +import java.util.List; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; + +import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.util.SaFoxUtil; +import io.quarkus.arc.DefaultBean; +import io.quarkus.arc.Unremovable; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * io.quarkiverse.satoken.resteasy.it.StpInterfaceConfiguration + * + * @author nayan + * @date 2022/10/8 18:55 + */ + +@Dependent +public class StpInterfaceConfiguration { + + @Produces + @Unremovable + @IfBuildProfile("annotation") + public StpInterface annotation() { + return new StpInterface() { + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("user*", "art-add", "art-delete", "art-update", "art-get"); + } else { + return null; + } + } + + /** + * 返回一个账号所拥有的角色标识集合 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("admin", "super-admin"); + } else { + return null; + } + } + }; + } + + @Produces + @Unremovable + @DefaultBean + public StpInterface noopTracer() { + return new StpInterface() { + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("user*", "art-add", "art-delete", "art-update", "art-get"); + } else { + return null; + } + } + + /** + * 返回一个账号所拥有的角色标识集合 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("admin", "super-admin"); + } else { + return null; + } + } + }; + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/TestApplication.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/TestApplication.java new file mode 100644 index 0000000..0ba3b4d --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/TestApplication.java @@ -0,0 +1,11 @@ +package io.quarkiverse.satoken.resteasy.it; + +import io.quarkus.runtime.Quarkus; +import io.quarkus.runtime.annotations.QuarkusMain; + +@QuarkusMain +public class TestApplication { + public static void main(String... args) { + Quarkus.run(args); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/it/SatokenDaoRedisJacksonResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/LoginResource.java similarity index 93% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/it/SatokenDaoRedisJacksonResource.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/LoginResource.java index f86760a..ed4fbee 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/it/SatokenDaoRedisJacksonResource.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/LoginResource.java @@ -14,19 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.quarkiverse.satoken.dao.redis.jackson.it; +package io.quarkiverse.satoken.resteasy.it.integrate; import javax.enterprise.context.ApplicationScoped; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; +import cn.dev33.satoken.annotation.SaCheckLogin; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; @Path("/acc/") @ApplicationScoped -public class SatokenDaoRedisJacksonResource { +public class LoginResource { // 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456 @POST @@ -49,6 +50,7 @@ public SaResult isLogin() { // 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo @POST + @SaCheckLogin @Path("tokenInfo") public SaResult tokenInfo() { return SaResult.data(StpUtil.getTokenInfo()); diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/MoreResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/MoreResource.java new file mode 100644 index 0000000..a1b88dc --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/MoreResource.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import cn.dev33.satoken.basic.SaBasicUtil; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/more/") +@ApplicationScoped +public class MoreResource { + + // 一些基本的测试 + @POST + @Path("getInfo") + public SaResult getInfo() { + SaRequest req = SaHolder.getRequest(); + boolean flag = SaFoxUtil.equals(req.getParam("name"), "zhang") + && SaFoxUtil.equals(req.getParam("name2", "li"), "li") + && SaFoxUtil.equals(req.getParamNotNull("name"), "zhang") + && req.isParam("name", "zhang") + && req.isPath("/more/getInfo") + && req.hasParam("name") + && SaFoxUtil.equals(req.getHeader("div"), "val") + && SaFoxUtil.equals(req.getHeader("div", "zhang"), "val") + && SaFoxUtil.equals(req.getHeader("div2", "zhang"), "zhang"); + + SaHolder.getResponse().setServer("sa-server"); + return SaResult.data(flag); + } + + // Http Basic 认证 + @POST + @Path("basicAuth") + public SaResult basicAuth() { + SaBasicUtil.check("sa:123456"); + return SaResult.ok(); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/RouterResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/RouterResource.java new file mode 100644 index 0000000..71dc03a --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/RouterResource.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/rt/") +@ApplicationScoped +public class RouterResource { + + @POST + @Path("getInfo") + public SaResult getInfo() { + return SaResult.ok(); + } + + @POST + @Path("getInfo{var:.*}") + public SaResult getInfo2() { + return SaResult.ok(); + } + + // 读url + @POST + @Path("getInfo_101") + public SaResult getInfo_101() { + return SaResult.data(SaHolder.getRequest().getUrl()); + } + + // 读Cookie + @POST + @Path("getInfo_102") + public SaResult getInfo_102() { + return SaResult.data(SaHolder.getRequest().getCookieValue("x-token")); + } + + // 测试转发 + @POST + @Path("getInfo_103") + public SaResult getInfo_103() { + SaHolder.getRequest().forward("/rt/getInfo_102"); + return SaResult.ok(); + } + + // 空接口 + @POST + @Path("getInfo_200") + public SaResult getInfo_200() { + return SaResult.ok(); + } + + @POST + @Path("getInfo_201") + public SaResult getInfo_201() { + return SaResult.ok(); + } + + @POST + @Path("getInfo_202") + public SaResult getInfo_202() { + return SaResult.ok(); + } + + @POST + @Path("login") + public SaResult login(@QueryParam("id") long id) { + StpUtil.login(id); + return SaResult.ok().set("token", StpUtil.getTokenValue()); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationIgnoreResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationIgnoreResource.java new file mode 100644 index 0000000..8546839 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationIgnoreResource.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.util.SaResult; + +@SaCheckLogin +@Path("/ig/") +@ApplicationScoped +public class SaAnnotationIgnoreResource { + + // 需要登录后访问 + @POST + @Path("show1") + public SaResult show1() { + return SaResult.ok(); + } + + // 不登录也可访问 + @SaIgnore + @POST + @Path("show2") + public SaResult show2() { + return SaResult.ok(); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationResource.java new file mode 100644 index 0000000..f0e586d --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationResource.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import cn.dev33.satoken.annotation.SaCheckDisable; +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.annotation.SaCheckSafe; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/at/") +@ApplicationScoped +public class SaAnnotationResource { + + // 登录 + @POST + @Path("login") + public SaResult login(@QueryParam("id") long id) { + StpUtil.login(id); + return SaResult.ok().set("token", StpUtil.getTokenValue()); + } + + // 登录校验 + @SaCheckLogin + @POST + @Path("checkLogin") + public SaResult checkLogin() { + return SaResult.ok(); + } + + // 角色校验 + @SaCheckRole("admin") + @POST + @Path("checkRole") + public SaResult checkRole() { + return SaResult.ok(); + } + + // 权限校验 + @SaCheckPermission("art-add") + @POST + @Path("checkPermission") + public SaResult checkPermission() { + return SaResult.ok(); + } + + // 权限校验 or 角色校验 + @SaCheckPermission(value = "art-add2", orRole = "admin") + @POST + @Path("checkPermission2") + public SaResult checkPermission2() { + return SaResult.ok(); + } + + // 开启二级认证 + @POST + @Path("openSafe") + public SaResult openSafe() { + StpUtil.openSafe(120); + return SaResult.ok(); + } + + // 二级认证校验 + @SaCheckSafe + @POST + @Path("checkSafe") + public SaResult checkSafe() { + return SaResult.ok(); + } + + // 封禁账号 + @POST + @Path("disable") + public SaResult disable(@QueryParam("id") long id) { + StpUtil.disable(id, "comment", 200); + return SaResult.ok(); + } + + // 服务封禁校验 + @SaCheckDisable("comment") + @POST + @Path("checkDisable") + public SaResult checkDisable() { + return SaResult.ok(); + } + + // 解封账号 + @POST + @Path("untieDisable") + public SaResult untieDisable(@QueryParam("id") long id) { + StpUtil.untieDisable(id, "comment"); + return SaResult.ok(); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaIdTokenResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaIdTokenResource.java new file mode 100644 index 0000000..3da9b2b --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaIdTokenResource.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import cn.dev33.satoken.id.SaIdUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/id/") +@ApplicationScoped +public class SaIdTokenResource { + + // 获取信息 + @POST + @Path("getInfo") + public SaResult getInfo(@HeaderParam(SaIdUtil.ID_TOKEN) String token) { + // 获取并校验id-token + SaIdUtil.checkToken(token); + // 返回信息 + return SaResult.data("info=zhangsan"); + } + + // 获取信息2 + @POST + @Path("getInfo2") + public SaResult getInfo2() { + // 获取并校验id-token + SaIdUtil.checkCurrentRequestToken(); + // 返回信息 + return SaResult.data("info=zhangsan2"); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application-annotation.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application-annotation.properties new file mode 100644 index 0000000..6fe45ce --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application-annotation.properties @@ -0,0 +1 @@ +sa-token.annotation-intercepted-enabled=true diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application-route.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application-route.properties new file mode 100644 index 0000000..2ddcd42 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application-route.properties @@ -0,0 +1 @@ +sa-token.route.interceptor=true \ No newline at end of file diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application.properties new file mode 100644 index 0000000..1591c6a --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/main/resources/application.properties @@ -0,0 +1,10 @@ +sa-token.exception-mapper-enabled=true +quarkus.application.name=sa-token-dao-alone-redis +quarkus.redisson.single-server-config.address=redis://127.0.0.1:63790 +quarkus.redisson.single-server-config.database=0 +quarkus.redisson.codec=org.redisson.codec.JsonJacksonCodec +quarkus.redis.devservices.enabled=true +quarkus.redis.devservices.port=63790 +sa-token.alone-redis.redisson.single-server-config.address=redis://127.0.0.1:63791 +sa-token.alone-redis.redisson.single-server-config.database=0 +sa-token.alone-redis.redisson.codec=org.redisson.codec.JsonJacksonCodec diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/AbstractRequestTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/AbstractRequestTest.java new file mode 100644 index 0000000..ab14554 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/AbstractRequestTest.java @@ -0,0 +1,117 @@ +package io.quarkiverse.satoken.resteasy; + +import static io.restassured.RestAssured.given; + +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.util.SaResult; +import io.quarkiverse.satoken.resteasy.utils.SoMap; +import io.restassured.response.ValidatableResponse; + +/** + * AbstractRequestTest + * + * @author nayan + * @date 2022/10/8 14:12 + */ +public class AbstractRequestTest { + + protected ValidatableResponse request(String path, SaResultMatcher matcher) { + return given() + .when() + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + + protected ValidatableResponse request(String path, Map headers, SaResultMatcher matcher) { + return given() + .when() + .headers(headers) + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + + protected ValidatableResponse request(String path, Map headers, Map cookies, + SaResultMatcher matcher) { + return given() + .when() + .headers(headers) + .cookies(cookies) + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + + public interface SaMapMatcher extends Matcher { + + void match(SoMap result); + + @Override + default boolean matches(Object actual) { + SoMap so = SoMap.getSoMap().setJsonString( + actual.toString()); + match(so); + return true; + } + + @Override + default public void describeMismatch(Object actual, Description mismatchDescription) { + + } + + @Override + default public void _dont_implement_Matcher___instead_extend_BaseMatcher_() { + + } + + @Override + default public void describeTo(Description description) { + + } + } + + public interface SaResultMatcher extends Matcher { + + void match(SaResult result); + + @Override + default boolean matches(Object actual) { + // 转 Map + Map map = SaManager.getSaJsonTemplate().parseJsonToMap(actual.toString()); + + // 转 SaResult 对象 + match(new SaResult().setMap(map)); + return true; + } + + @Override + default public void describeMismatch(Object actual, Description mismatchDescription) { + + } + + @Override + default public void _dont_implement_Matcher___instead_extend_BaseMatcher_() { + + } + + @Override + default public void describeTo(Description description) { + + } + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/BasicTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/BasicTest.java new file mode 100644 index 0000000..21f07b5 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/BasicTest.java @@ -0,0 +1,742 @@ +package io.quarkiverse.satoken.resteasy; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.SaTokenContext; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.exception.DisableServiceException; +import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.exception.NotRoleException; +import cn.dev33.satoken.exception.NotSafeException; +import cn.dev33.satoken.exception.SaJsonConvertException; +import cn.dev33.satoken.json.SaJsonTemplate; +import cn.dev33.satoken.session.SaSession; +import cn.dev33.satoken.stp.SaLoginConfig; +import cn.dev33.satoken.stp.SaLoginModel; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaTokenConsts; +import io.quarkiverse.satoken.core.context.SaPathMatcherHolder; +import io.quarkiverse.satoken.core.context.SaTokenContextForQuarkus; +import io.quarkiverse.satoken.core.utils.AntPathMatcher; +import io.quarkiverse.satoken.resteasy.utils.SoMap; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(RedisResource.class) +public class BasicTest extends AbstractRequestTest { + + // 持久化Bean + SaTokenDao dao = SaManager.getSaTokenDao(); + + // 开始 + @BeforeAll + public static void beforeClass() { + System.out.println("\n\n------------------------ 基础测试 star ..."); + SaManager.getConfig().setActivityTimeout(180); + } + + // 结束 + @AfterAll + public static void afterClass() { + System.out.println("\n\n------------------------ 基础测试 end ... \n"); + } + + @AfterEach + public void clear() { + MockResteasyReactiveRequestContext.remove(); + } + + // 测试:基础API + @Test + public void testBasicsApi() { + // 基本API + Assertions.assertEquals(StpUtil.getLoginType(), "login"); + Assertions.assertEquals(StpUtil.getStpLogic(), SaManager.getStpLogic("login")); + Assertions.assertEquals(StpUtil.getTokenName(), "satoken"); + + // 安全的更新 StpUtil 的 StpLogic 对象 + StpLogic loginStpLogic = new StpLogic("login"); + StpUtil.setStpLogic(loginStpLogic); + Assertions.assertEquals(StpUtil.getStpLogic(), loginStpLogic); + Assertions.assertEquals(SaManager.getStpLogic("login"), loginStpLogic); + } + + // 测试:登录 + @Test + public void testDoLogin() { + // 登录 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + + // token 存在 + Assertions.assertNotNull(token); + Assertions.assertEquals(token, StpUtil.getTokenValueNotCut()); + Assertions.assertEquals(token, StpUtil.getTokenValueByLoginId(10001)); + Assertions.assertEquals(token, StpUtil.getTokenValueByLoginId(10001, SaTokenConsts.DEFAULT_LOGIN_DEVICE)); + + // token 队列 + List tokenList = StpUtil.getTokenValueListByLoginId(10001); + List tokenList2 = StpUtil.getTokenValueListByLoginId(10001, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + Assertions.assertEquals(token, tokenList.get(tokenList.size() - 1)); + Assertions.assertEquals(token, tokenList2.get(tokenList.size() - 1)); + + // API 验证 + Assertions.assertTrue(StpUtil.isLogin()); + Assertions.assertDoesNotThrow(() -> StpUtil.checkLogin()); + Assertions.assertNotNull(token); // token不为null + Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10001); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginIdAsInt(), 10001); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginIdAsString(), "10001"); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginId(), "10001"); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginIdDefaultNull(), "10001"); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginDevice(), SaTokenConsts.DEFAULT_LOGIN_DEVICE); // 登录设备类型 + + // db数据 验证 + // token存在 + Assertions.assertEquals(dao.get("satoken:login:token:" + token), "10001"); + // Session 存在 + SaSession session = dao.getSession("satoken:login:session:" + 10001); + Assertions.assertNotNull(session); + Assertions.assertEquals(session.getId(), "satoken:login:session:" + 10001); + Assertions.assertTrue(session.getTokenSignList().size() >= 1); + } + + // 测试:注销 + @Test + public void testLogout() { + // 登录 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + Assertions.assertEquals(dao.get("satoken:login:token:" + token), "10001"); + + // 注销 + StpUtil.logout(); + // token 应该被清除 + Assertions.assertNull(StpUtil.getTokenValue()); + Assertions.assertFalse(StpUtil.isLogin()); + Assertions.assertNull(dao.get("satoken:login:token:" + token)); + + // 全部客户端注销掉 + StpUtil.logout(10001); + // Session 应该被清除 + SaSession session = dao.getSession("satoken:login:session:" + 10001); + Assertions.assertNull(session); + + // 在调用 getLoginId() 就会抛出异常 + Assertions.assertEquals(StpUtil.getLoginId("无值"), "无值"); + Assertions.assertThrows(NotLoginException.class, () -> StpUtil.getLoginId()); + } + + // 测试:Session会话 + @Test + public void testSession() { + StpUtil.login(10001); + + // API 应该可以获取 Session + Assertions.assertNotNull(StpUtil.getSession()); + Assertions.assertNotNull(StpUtil.getSession(false)); + + // db中应该存在 Session + SaSession session = dao.getSession("satoken:login:session:" + 10001); + Assertions.assertNotNull(session); + + // 存取值 + session.set("name", "zhang"); + session.set("age", "18"); + Assertions.assertEquals(session.get("name"), "zhang"); + Assertions.assertEquals(session.getInt("age"), 18); + Assertions.assertEquals((int) session.getModel("age", int.class), 18); + Assertions.assertEquals((int) session.get("age", 20), 18); + Assertions.assertEquals((int) session.get("name2", 20), 20); + Assertions.assertEquals((int) session.get("name2", () -> 30), 30); + session.clear(); + Assertions.assertEquals(session.get("name"), null); + } + + // 测试:权限认证 + @Test + public void testCheckPermission() { + StpUtil.login(10001); + + // 获取权限 + List permissionList = StpUtil.getPermissionList(); + List permissionList2 = StpUtil.getPermissionList(10001); + Assertions.assertEquals(permissionList.size(), permissionList2.size()); + + // 权限校验 + Assertions.assertTrue(StpUtil.hasPermission("user-add")); + Assertions.assertTrue(StpUtil.hasPermission("user-list")); + Assertions.assertTrue(StpUtil.hasPermission("user")); + Assertions.assertTrue(StpUtil.hasPermission("art-add")); + Assertions.assertFalse(StpUtil.hasPermission("get-user")); + // and + Assertions.assertTrue(StpUtil.hasPermissionAnd("art-add", "art-get")); + Assertions.assertFalse(StpUtil.hasPermissionAnd("art-add", "comment-add")); + // or + Assertions.assertTrue(StpUtil.hasPermissionOr("art-add", "comment-add")); + Assertions.assertFalse(StpUtil.hasPermissionOr("comment-add", "comment-delete")); + // more + Assertions.assertTrue(StpUtil.hasPermission(10001, "user-add")); + Assertions.assertFalse(StpUtil.hasPermission(10002, "user-add")); + + // 抛异常 + Assertions.assertThrows(NotPermissionException.class, () -> StpUtil.checkPermission("goods-add")); + Assertions.assertThrows(NotPermissionException.class, () -> StpUtil.checkPermissionAnd("goods-add", "art-add")); + // 不抛异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkPermission("user-add")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkPermissionAnd("art-get", "art-add")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkPermissionOr("goods-add", "art-add")); + } + + // 测试:角色认证 + @Test + public void testCheckRole() { + StpUtil.login(10001); + + // 获取角色 + List roleList = StpUtil.getRoleList(); + List roleList2 = StpUtil.getRoleList(10001); + Assertions.assertEquals(roleList.size(), roleList2.size()); + + // 角色校验 + Assertions.assertTrue(StpUtil.hasRole("admin")); + Assertions.assertFalse(StpUtil.hasRole("teacher")); + // and + Assertions.assertTrue(StpUtil.hasRoleAnd("admin", "super-admin")); + Assertions.assertFalse(StpUtil.hasRoleAnd("admin", "ceo")); + // or + Assertions.assertTrue(StpUtil.hasRoleOr("admin", "ceo")); + Assertions.assertFalse(StpUtil.hasRoleOr("ceo", "cto")); + // more + Assertions.assertTrue(StpUtil.hasRole(10001, "admin")); + Assertions.assertFalse(StpUtil.hasRole(10002, "admin2")); + + // 抛异常 + Assertions.assertThrows(NotRoleException.class, () -> StpUtil.checkRole("ceo")); + Assertions.assertThrows(NotRoleException.class, () -> StpUtil.checkRoleAnd("ceo", "admin")); + // 不抛异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkRole("admin")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkRoleAnd("admin", "super-admin")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkRoleOr("ceo", "admin")); + } + + // 测试:根据token强制注销 + @Test + public void testLogoutByToken() { + + // 先登录上 + StpUtil.login(10001); + Assertions.assertTrue(StpUtil.isLogin()); + String token = StpUtil.getTokenValue(); + + // 根据token注销 + StpUtil.logoutByTokenValue(token); + Assertions.assertFalse(StpUtil.isLogin()); + + // token 应该被清除 + Assertions.assertNull(dao.get("satoken:login:token:" + token)); + // Session 应该被清除 + SaSession session = dao.getSession("satoken:login:session:" + 10001); + Assertions.assertNull(session); + + // 场景值应该是token无效 + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + Assertions.assertEquals(e.getType(), NotLoginException.INVALID_TOKEN); + } + + // 根据token踢下线 + StpUtil.login(10001); + StpUtil.kickoutByTokenValue(StpUtil.getTokenValue()); + + // 场景值应该是被踢下线 + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + Assertions.assertEquals(e.getType(), NotLoginException.KICK_OUT); + } + } + + // 测试:根据账号id强制注销 + @Test + public void testLogoutByLoginId() { + + // 先登录上 + StpUtil.login(10001); + Assertions.assertTrue(StpUtil.isLogin()); + String token = StpUtil.getTokenValue(); + + // 根据账号id注销 + StpUtil.logout(10001); + Assertions.assertFalse(StpUtil.isLogin()); + + // token 应该被清除 + Assertions.assertNull(dao.get("satoken:login:token:" + token)); + // Session 应该被清除 + SaSession session = dao.getSession("satoken:login:session:" + 10001); + Assertions.assertNull(session); + + // 场景值应该是token无效 + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + Assertions.assertEquals(e.getType(), NotLoginException.INVALID_TOKEN); + } + } + + // 测试Token-Session + @Test + public void testTokenSession() { + + // 先登录上 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + + // 刚开始不存在 + Assertions.assertNull(StpUtil.stpLogic.getTokenSession(false)); + SaSession session = dao.getSession("satoken:login:token-session:" + token); + Assertions.assertNull(session); + + // 调用一次就存在了 + StpUtil.getTokenSession(); + Assertions.assertNotNull(StpUtil.stpLogic.getTokenSession(false)); + SaSession session2 = dao.getSession("satoken:login:token-session:" + token); + Assertions.assertNotNull(session2); + + // + SaSession tokenSession = StpUtil.getTokenSession(); + SaSession tokenSession2 = StpUtil.getTokenSessionByToken(token); + Assertions.assertEquals(tokenSession.getId(), tokenSession2.getId()); + } + + // 测试:根据账号id踢人 + @Test + public void kickoutByLoginId() { + + // 踢人下线 + StpUtil.login(10001); + String token = StpUtil.getTokenValue(); + StpUtil.kickout(10001); + + // token 应该被打标记 + Assertions.assertEquals(dao.get("satoken:login:token:" + token), NotLoginException.KICK_OUT); + + // 场景值应该是token已被踢下线 + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + Assertions.assertEquals(e.getType(), NotLoginException.KICK_OUT); + } + } + + // 测试:账号封禁 + @Test + public void testDisable() { + // 封号 + StpUtil.disable(10007, 200); + Assertions.assertTrue(StpUtil.isDisable(10007)); + Assertions.assertEquals(dao.get("satoken:login:disable:login:" + 10007), + String.valueOf(SaTokenConsts.DEFAULT_DISABLE_LEVEL)); + + // 封号后检测一下 (会抛出 DisableLoginException 异常) + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisable(10007)); + + // 封号时间 + long disableTime = StpUtil.getDisableTime(10007); + Assertions.assertTrue(disableTime <= 200 && disableTime >= 199); + + // 解封 + StpUtil.untieDisable(10007); + Assertions.assertFalse(StpUtil.isDisable(10007)); + Assertions.assertEquals(dao.get("satoken:login:disable:login:" + 10007), null); + Assertions.assertDoesNotThrow(() -> StpUtil.checkDisable(10007)); + } + + // 测试:分类封禁 + @Test + public void testDisableService() { + // 封掉评论功能 + StpUtil.disable(10008, "comment", 200); + Assertions.assertTrue(StpUtil.isDisable(10008, "comment")); + Assertions.assertEquals(dao.get("satoken:login:disable:comment:" + 10008), + String.valueOf(SaTokenConsts.DEFAULT_DISABLE_LEVEL)); + Assertions.assertNull(dao.get("satoken:login:disable:login:" + 10008)); + + // 封号后检测一下 + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisable(10008, "comment")); + // 检查多个,有一个不通过就报异常 + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisable(10008, "comment", "login")); + + // 封号时间 + long disableTime = StpUtil.getDisableTime(10008, "comment"); + Assertions.assertTrue(disableTime <= 200 && disableTime >= 199); + + // 解封 (不加服务名不会成功) + StpUtil.untieDisable(10008); + Assertions.assertTrue(StpUtil.isDisable(10008, "comment")); + Assertions.assertNotNull(dao.get("satoken:login:disable:comment:" + 10008)); + + // 解封 (加服务名才会成功) + StpUtil.untieDisable(10008, "comment"); + Assertions.assertFalse(StpUtil.isDisable(10008, "comment")); + Assertions.assertEquals(dao.get("satoken:login:disable:comment:" + 10008), null); + Assertions.assertDoesNotThrow(() -> StpUtil.checkDisable(10007, "comment")); + } + + // 测试:阶梯封禁 + @Test + public void testDisableLevel() { + // 封禁等级5 + StpUtil.disableLevel(10009, 5, 200); + Assertions.assertTrue(StpUtil.isDisableLevel(10009, 3)); + Assertions.assertTrue(StpUtil.isDisableLevel(10009, 5)); + // 未达到7级 + Assertions.assertFalse(StpUtil.isDisableLevel(10009, 7)); + // 账号未封禁 + Assertions.assertFalse(StpUtil.isDisableLevel(20009, 3)); + + // dao中应该有值 + Assertions.assertEquals(dao.get("satoken:login:disable:login:" + 10009), String.valueOf(5)); + + // 封号后检测一下 + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisableLevel(10009, 3)); + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisableLevel(10009, 5)); + // 未达到等级,不抛出异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkDisableLevel(10009, 7)); + // 账号未被封禁,不抛出异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkDisableLevel(20009, 3)); + + // 封号等级 + Assertions.assertEquals(StpUtil.getDisableLevel(10009), 5); + Assertions.assertEquals(StpUtil.getDisableLevel(20009), -2); + + // 解封 + StpUtil.untieDisable(10009); + Assertions.assertFalse(StpUtil.isDisable(10009)); + Assertions.assertFalse(StpUtil.isDisableLevel(10009, 5)); + Assertions.assertNull(dao.get("satoken:login:disable:login:" + 10009)); + } + + // 测试:分类封禁 + 阶梯封禁 + @Test + public void testDisableServiceLevel() { + // 封禁服务 shop,等级5 + StpUtil.disableLevel(10010, "shop", 5, 200); + Assertions.assertTrue(StpUtil.isDisableLevel(10010, "shop", 3)); + Assertions.assertTrue(StpUtil.isDisableLevel(10010, "shop", 5)); + // 未达到7级 + Assertions.assertFalse(StpUtil.isDisableLevel(10010, "shop", 7)); + // 账号未封禁 + Assertions.assertFalse(StpUtil.isDisableLevel(20010, "shop", 3)); + // 服务名不对 + Assertions.assertFalse(StpUtil.isDisableLevel(10010, "shop2", 5)); + + // dao中应该有值 + Assertions.assertEquals(dao.get("satoken:login:disable:shop:" + 10010), String.valueOf(5)); + + // 封号后检测一下 + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisableLevel(10010, "shop", 3)); + Assertions.assertThrows(DisableServiceException.class, () -> StpUtil.checkDisableLevel(10010, "shop", 5)); + // 未达到等级,不抛出异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkDisableLevel(10010, "shop", 7)); + // 账号未被封禁,不抛出异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkDisableLevel(20010, "shop", 3)); + + // 封号等级 + Assertions.assertEquals(StpUtil.getDisableLevel(10010, "shop"), 5); + Assertions.assertEquals(StpUtil.getDisableLevel(10010, "shop2"), -2); + Assertions.assertEquals(StpUtil.getDisableLevel(20010, "shop"), -2); + + // 解封 + StpUtil.untieDisable(10010, "shop"); + Assertions.assertFalse(StpUtil.isDisable(10010, "shop")); + Assertions.assertFalse(StpUtil.isDisableLevel(10010, "shop", 5)); + Assertions.assertNull(dao.get("satoken:login:disable:shop:" + 10010)); + } + + // 测试:身份切换 + @Test + public void testSwitch() { + // 登录 + StpUtil.login(10001); + Assertions.assertFalse(StpUtil.isSwitch()); + Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10001); + + // 开始身份切换 + StpUtil.switchTo(10044); + Assertions.assertTrue(StpUtil.isSwitch()); + Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10044); + + // 开始身份切换 Lambda 方式 + StpUtil.switchTo(10045, () -> { + Assertions.assertTrue(StpUtil.isSwitch()); + Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10045); + }); + + // 结束切换 + StpUtil.endSwitch(); + Assertions.assertFalse(StpUtil.isSwitch()); + Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10001); + } + + // 测试:会话管理 + @Test + public void testSearchTokenValue() { + // 登录 + StpUtil.login(10001); + StpUtil.login(10002); + StpUtil.login(10003); + StpUtil.login(10004); + StpUtil.login(10005); + + // 查询 Token 列表 + List list = StpUtil.searchTokenValue("", 0, 10, true); + Assertions.assertTrue(list.size() >= 5); + + // 查询 Session 列表 + List list2 = StpUtil.searchSessionId("", 0, 10, true); + Assertions.assertTrue(list2.size() >= 5); + list2.stream().forEach(sessionId -> { + Assertions.assertNotNull(StpUtil.getSessionBySessionId(sessionId)); + }); + } + + // 测试:会话管理(Token-Session) + @Test + public void testSearchTokenSession() { + // 登录 + StpUtil.login(10001); + StpUtil.getTokenSession(); + StpUtil.login(10002); + StpUtil.getTokenSession(); + StpUtil.login(10003); + StpUtil.getTokenSession(); + StpUtil.login(10004); + StpUtil.getTokenSession(); + StpUtil.login(10005); + StpUtil.getTokenSession(); + + // 查询 Token-Session 列表 + List list2 = StpUtil.searchTokenSessionId("", 0, 10, true); + Assertions.assertTrue(list2.size() >= 5); + list2.stream().forEach(sessionId -> { + Assertions.assertNotNull(StpUtil.getSessionBySessionId(sessionId)); + }); + } + + // 测试:二级认证 + @Test + public void testSafe() { + // 登录 + StpUtil.login(10001); + Assertions.assertFalse(StpUtil.isSafe()); + + // 开启二级认证 + StpUtil.openSafe(2); + Assertions.assertTrue(StpUtil.isSafe()); + Assertions.assertTrue(StpUtil.getSafeTime() > 0); + StpUtil.checkSafe(); + + // 自然结束 + // Thread.sleep(2500); + // Assertions.assertFalse(StpUtil.isSafe()); + + // 手动结束 + // StpUtil.openSafe(2); + StpUtil.closeSafe(); + Assertions.assertFalse(StpUtil.isSafe()); + + // 抛异常 + Assertions.assertThrows(NotSafeException.class, () -> StpUtil.checkSafe()); + } + + // ------------- 复杂点的 + + // 测试:指定设备登录 + @Test + public void testDoLoginByDevice() { + StpUtil.login(10001, "PC"); + Assertions.assertEquals(StpUtil.getLoginDevice(), "PC"); + + // 指定一个其它的设备注销,应该注销不掉 + StpUtil.logout(10001, "APP"); + Assertions.assertTrue(StpUtil.isLogin()); + + // 指定当前设备踢掉,则能够踢掉 + StpUtil.kickout(10001, "PC"); + Assertions.assertFalse(StpUtil.isLogin()); + + // 顶掉 + StpUtil.login(10001, "PC"); + StpUtil.replaced(10001, "PC"); + Assertions.assertFalse(StpUtil.isLogin()); + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + // 场景值应该为-4 + Assertions.assertEquals(e.getType(), NotLoginException.BE_REPLACED); + } + } + + // 测试:指定 timeout 登录 + @Test + public void testDoLoginByTimeout() { + + // 指定timeout 登录 + StpUtil.login(10001, 100); + long timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout <= 100 && timeout >= 99); + + // 续期一下 + StpUtil.renewTimeout(200); + timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout <= 200 && timeout >= 199); + + // 续期一下 + StpUtil.renewTimeout(StpUtil.getTokenValue(), 300); + timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout <= 300 && timeout >= 299); + + // Session 也会续期 + timeout = StpUtil.getSessionTimeout(); + Assertions.assertTrue(timeout >= 299); + + StpUtil.getTokenSession(); + timeout = StpUtil.getTokenSessionTimeout(); + Assertions.assertTrue(timeout >= 299); + + // 注销后,就是-2 + StpUtil.logout(); + timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout == SaTokenDao.NOT_VALUE_EXPIRE); + } + + // 测试:预定 Token 登录 + @Test + public void testDoLoginBySetToken() { + // 预定 Token 登录 + StpUtil.login(10001, new SaLoginModel().setToken("qwer-qwer-qwer-qwer")); + Assertions.assertEquals(StpUtil.getTokenValue(), "qwer-qwer-qwer-qwer"); + + // 注销后,应该清除Token + StpUtil.logout(); + Assertions.assertNull(StpUtil.getTokenValue()); + } + + // 测试:无上下文注入的登录 + @Test + public void testCreateLoginSession() { + + // 无上下文注入的登录 + StpUtil.createLoginSession(10001); + Assertions.assertNull(StpUtil.getTokenValue()); + + // 无上下文注入的登录 + String token = StpUtil.createLoginSession(10001, new SaLoginModel()); + Assertions.assertNull(StpUtil.getTokenValue()); + + // 手动写入 + StpUtil.setTokenValue(token); + Assertions.assertNotNull(StpUtil.getTokenValue()); + + // 手动写入到Cookie + StpUtil.setTokenValue(token, 10); + Assertions.assertNotNull(StpUtil.getTokenValue()); + } + + // 测试,匿名 Token-Session + @Test + public void testAnonTokenSession() { + // token 不存在 + StpUtil.logout(); + Assertions.assertNull(StpUtil.getTokenValue()); + + // token 存在 + SaSession anonTokenSession = StpUtil.getAnonTokenSession(); + String token = StpUtil.getTokenValue(); + Assertions.assertNotNull(token); + // 写个值 + anonTokenSession.set("code", "123456"); + + // 登录时,预定上 + StpUtil.login(10001, SaLoginConfig.setToken(token)); + // token不变 + Assertions.assertEquals(token, StpUtil.getTokenValue()); + + // Token-Session 存在,且不变 + SaSession tokenSession = StpUtil.getTokenSession(); + Assertions.assertEquals(anonTokenSession.getId(), tokenSession.getId()); + + // 刚才写的值,仍然在 + Assertions.assertEquals(tokenSession.get("code"), "123456"); + } + + // 测试,临时过期 + @Test + public void testActivityTimeout() { + // 登录 + StpUtil.login(10001); + Assertions.assertNotNull(StpUtil.getTokenValue()); + + // 默认跟随全局 timeout + StpUtil.updateLastActivityToNow(); + long activityTimeout = StpUtil.getTokenActivityTimeout(); + Assertions.assertTrue(activityTimeout <= 180 || activityTimeout >= 179); + + // 不会抛出异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkActivityTimeout()); + } + + // 测试,上下文 API + @Test + public void testSaTokenContext() { + SaTokenContext context = SaHolder.getContext(); + // path 匹配 + Assertions.assertTrue(context.matchPath("/user/**", "/user/add")); + // context 是否有效 + Assertions.assertTrue(context.isValid()); + // 是否为web环境 + Assertions.assertFalse(((SaTokenContextForQuarkus) context).isWeb()); + // // pathMatcher + // // Assertions.assertEquals(pathMatcher, SaPathMatcherHolder.getPathMatcher()); + // 自创建 + SaPathMatcherHolder.pathMatcher = null; + Assertions.assertNotNull(SaPathMatcherHolder.getPathMatcher()); + SaPathMatcherHolder.pathMatcher = new AntPathMatcher(); + } + + // 测试json转换 + @Test + public void testSaJsonTemplate() { + SaJsonTemplate saJsonTemplate = SaManager.getSaJsonTemplate(); + + // map 转 json + SoMap map = SoMap.getSoMap("name", "zhangsan"); + String jsonString = saJsonTemplate.toJsonString(map); + Assertions.assertEquals(jsonString, "{\"name\":\"zhangsan\"}"); + + // 抛异常 + Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.toJsonString(new Object())); + + // json 转 map + Map map2 = saJsonTemplate.parseJsonToMap("{\"name\":\"zhangsan\"}"); + Assertions.assertEquals(map2.get("name"), "zhangsan"); + + // 抛异常 + Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.parseJsonToMap("")); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/ManyLoginTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/ManyLoginTest.java new file mode 100644 index 0000000..7172360 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/ManyLoginTest.java @@ -0,0 +1,176 @@ +package io.quarkiverse.satoken.resteasy; + +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.config.SaTokenConfig; +import cn.dev33.satoken.dao.SaTokenDao; +import cn.dev33.satoken.session.TokenSign; +import cn.dev33.satoken.stp.StpLogic; +import cn.dev33.satoken.stp.StpUtil; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +/** + * ManyLoginTest + * + * @author nayan + * @date 2022/4/12 2:11 PM + */ +@QuarkusTest +@QuarkusTestResource(RedisResource.class) +public class ManyLoginTest extends AbstractRequestTest { + // 持久化Bean + SaTokenDao dao = SaManager.getSaTokenDao(); + + // 开始 + @BeforeAll + public static void beforeClass() { + System.out.println("\n------------ 多端登录测试 star ..."); + } + + // 结束 + @AfterAll + public static void afterClass() { + // System.out.println("\n---------- 多端登录测试 end ... \n"); + } + + // 测试:并发登录、共享token、同端 + @Test + public void login() { + SaManager.setConfig(new SaTokenConfig()); + + StpUtil.login(10001); + String token1 = StpUtil.getTokenValue(); + + StpUtil.login(10001); + String token2 = StpUtil.getTokenValue(); + + Assertions.assertEquals(token1, token2); + } + + // 测试:并发登录、共享token、不同端 + @Test + public void login2() { + SaManager.setConfig(new SaTokenConfig()); + + StpUtil.login(10001, "APP"); + String token1 = StpUtil.getTokenValue(); + + StpUtil.login(10001, "PC"); + String token2 = StpUtil.getTokenValue(); + + Assertions.assertNotEquals(token1, token2); + } + + // 测试:并发登录、不共享token + @Test + public void login3() { + SaManager.setConfig(new SaTokenConfig().setIsShare(false)); + + StpUtil.login(10001); + String token1 = StpUtil.getTokenValue(); + + StpUtil.login(10001); + String token2 = StpUtil.getTokenValue(); + + Assertions.assertNotEquals(token1, token2); + } + + // 测试:禁并发登录,后者顶出前者 + @Test + public void login4() { + SaManager.setConfig(new SaTokenConfig().setIsConcurrent(false)); + + StpUtil.login(10001); + String token1 = StpUtil.getTokenValue(); + + StpUtil.login(10001); + String token2 = StpUtil.getTokenValue(); + + // token不同 + Assertions.assertNotEquals(token1, token2); + + // token1会被标记为:已被顶下线 + Assertions.assertEquals(dao.get("satoken:login:token:" + token1), "-4"); + + // User-Session里的 token1 签名会被移除 + List tokenSignList = StpUtil.getSessionByLoginId(10001).getTokenSignList(); + for (TokenSign tokenSign : tokenSignList) { + Assertions.assertNotEquals(tokenSign.getValue(), token1); + } + } + + // 测试:多端登录,一起强制注销 + @Test + public void login5() { + SaManager.setConfig(new SaTokenConfig()); + + StpUtil.login(10001, "APP"); + String token1 = StpUtil.getTokenValue(); + + StpUtil.login(10001, "PC"); + String token2 = StpUtil.getTokenValue(); + + StpUtil.login(10001, "h5"); + String token3 = StpUtil.getTokenValue(); + + // 注销 + StpUtil.logout(10001); + + // 三个Token应该全部无效 + Assertions.assertNull(dao.get("satoken:login:token:" + token1)); + Assertions.assertNull(dao.get("satoken:login:token:" + token2)); + Assertions.assertNull(dao.get("satoken:login:token:" + token3)); + + // User-Session也应该被清除掉 + Assertions.assertNull(StpUtil.getSessionByLoginId(10001, false)); + Assertions.assertNull(dao.getSession("satoken:login:session:" + 10001)); + } + + // 测试:多端登录,一起强制踢下线 + @Test + public void login6() { + SaManager.setConfig(new SaTokenConfig()); + + StpUtil.login(10001, "APP"); + String token1 = StpUtil.getTokenValue(); + + StpUtil.login(10001, "PC"); + String token2 = StpUtil.getTokenValue(); + + StpUtil.login(10001, "h5"); + String token3 = StpUtil.getTokenValue(); + + // 注销 + StpUtil.kickout(10001); + + // 三个Token应该全部无效 + Assertions.assertEquals(dao.get("satoken:login:token:" + token1), "-5"); + Assertions.assertEquals(dao.get("satoken:login:token:" + token2), "-5"); + Assertions.assertEquals(dao.get("satoken:login:token:" + token3), "-5"); + + // User-Session也应该被清除掉 + Assertions.assertNull(StpUtil.getSessionByLoginId(10001, false)); + Assertions.assertNull(dao.getSession("satoken:login:session:" + 10001)); + } + + // 测试:多账号模式,在一个账号体系里登录成功,在另一个账号体系不会校验通过 + @Test + public void login7() { + SaManager.setConfig(new SaTokenConfig()); + + StpUtil.login(10001); + String token1 = StpUtil.getTokenValue(); + + StpLogic stp = new StpLogic("user"); + + Assertions.assertNotNull(StpUtil.getLoginIdByToken(token1)); + Assertions.assertNull(stp.getLoginIdByToken(token1)); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/RedisResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/RedisResource.java new file mode 100644 index 0000000..78ac94d --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/RedisResource.java @@ -0,0 +1,37 @@ +package io.quarkiverse.satoken.resteasy; + +import java.util.Collections; +import java.util.Map; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +/** + * RedisResource + * + * @author nayan + * @date 2022/10/20 10:42 + */ +public class RedisResource implements QuarkusTestResourceLifecycleManager { + + //准备Redis容器,使用withExposedPorts方法暴露端口 + public GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:6-alpine")) + .withExposedPorts(6379); + + @Override + public Map start() { + // redis.setPortBindings(Arrays.asList("63791:6379")); + redis.start(); + Integer port = redis.getMappedPort(6379); + return Collections.singletonMap( + "sa-token.alone-redis.redisson.single-server-config.address", + "redis://127.0.0.1:" + port); + } + + @Override + public void stop() { + redis.stop(); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/LoginResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/LoginResourceTest.java new file mode 100644 index 0000000..1ac54c1 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/LoginResourceTest.java @@ -0,0 +1,80 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkiverse.satoken.resteasy.RedisResource; +import io.quarkiverse.satoken.resteasy.utils.SoMap; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.response.ValidatableResponse; + +@QuarkusTest +@QuarkusTestResource(RedisResource.class) +public class LoginResourceTest extends AbstractRequestTest { + + @Test + void testLogin() { + requestSoMap( + "/acc/doLogin?name=zhang&pwd=123456", + so -> { + String token = so.getString("token"); + assertEquals(so.getInt("code"), 200); + assertNotNull(token); + }).header("Set-Cookie", notNullValue()); + } + + @Test + void testLogin2() { + requestSoMap( + "/acc/doLogin?name=zhang&pwd=123456", + so -> { + Assertions.assertNotNull(so.getString("token")); + String token = so.getString("token"); + + // 是否登录 + requestSoMap("/acc/isLogin?satoken=" + token, so2 -> { + Assertions.assertTrue(so2.getBoolean("data")); + }); + + // tokenInfo + requestSoMap("/acc/tokenInfo?satoken=" + token, so3 -> { + SoMap so4 = SoMap.getSoMap((Map) so3.get("data")); + Assertions.assertEquals(so4.getString("tokenName"), "satoken"); + Assertions.assertEquals(so4.getString("tokenValue"), token); + }); + + // 注销 + requestSoMap("/acc/logout?satoken=" + token, so4 -> { + }); + + // 是否登录 + requestSoMap("/acc/isLogin?satoken=" + token, so5 -> { + Assertions.assertFalse(so5.getBoolean("data")); + }); + + }); + } + + protected ValidatableResponse requestSoMap(String path, SaMapMatcher matcher) { + return given() + .when() + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .statusCode(200) + .body(matcher); + + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/MoreResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/MoreResourceTest.java new file mode 100644 index 0000000..356cb2a --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/MoreResourceTest.java @@ -0,0 +1,41 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkiverse.satoken.resteasy.RedisResource; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +@QuarkusTestResource(RedisResource.class) +public class MoreResourceTest extends AbstractRequestTest { + + // 基础API测试 + @Test + public void testApi() { + request("/more/getInfo?name=zhang", Map.of("div", "val"), res -> { + Assertions.assertEquals(res.getData(), true); + }).statusCode(200); + + } + + // Http Basic 认证 + @Test + public void testBasic() throws Exception { + + // ---------------- 认证不通过 + request("/more/basicAuth", res -> { + Assertions.assertEquals(res.getCode(), 903); + }).statusCode(401).header("WWW-Authenticate", "Basic Realm=Sa-Token"); + + // ---------------- 认证通过 + request("/more/basicAuth", Map.of("Authorization", "Basic c2E6MTIzNDU2"), res -> { + Assertions.assertEquals(res.getCode(), 200); + }).statusCode(200); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/RouteResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/RouteResourceTest.java new file mode 100644 index 0000000..d42f5b1 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/RouteResourceTest.java @@ -0,0 +1,169 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import java.util.Arrays; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.router.SaRouterStaff; +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkiverse.satoken.resteasy.RedisResource; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +/** + * RouteResourceTest + * + * @author nayan + * @date 2022/10/9 16:26 + */ +@QuarkusTest +@TestProfile(RouteResourceTest.RouteTestProfile.class) +@QuarkusTestResource(RedisResource.class) +public class RouteResourceTest extends AbstractRequestTest { + + // 基础API测试 + @Test + public void testApi() { + // 是否命中 + SaRouterStaff staff = SaRouter.match(false); + Assertions.assertFalse(staff.isHit()); + + // 重置 + staff.reset(); + Assertions.assertTrue(staff.isHit()); + + // lambda 形式 + SaRouterStaff staff2 = SaRouter.match(r -> false); + Assertions.assertFalse(staff2.isHit()); + + // 匹配 + Assertions.assertTrue(SaRouter.isMatch("/user/**", "/user/add")); + Assertions.assertTrue(SaRouter.isMatch(new String[] { "/user/**", "/art/**", "/goods/**" }, "/art/delete")); + Assertions.assertTrue(SaRouter.isMatch(Arrays.asList("/user/**", "/art/**", "/goods/**"), "/art/delete")); + Assertions.assertTrue(SaRouter.isMatch(new String[] { "POST", "GET", "PUT" }, "GET")); + + // 不匹配的 + Assertions.assertTrue(SaRouter.notMatch(false).isHit()); + Assertions.assertTrue(SaRouter.notMatch(r -> false).isHit()); + } + + // 各种路由测试 + @Test + public void testRouter() { + // getInfo + request("/rt/getInfo?name=zhang", res -> Assertions.assertEquals(res.getCode(), 201)); + + // getInfo2 + request("/rt/getInfo2", res2 -> Assertions.assertEquals(res2.getCode(), 202)); + + // getInfo3 + request("/rt/getInfo3", res3 -> Assertions.assertEquals(res3.getCode(), 203)); + + // getInfo4 + request("/rt/getInfo4", res4 -> Assertions.assertEquals(res4.getCode(), 204)); + + // getInfo5 + request("/rt/getInfo5", res5 -> Assertions.assertEquals(res5.getCode(), 205)); + + // getInfo6 + request("/rt/getInfo6", res6 -> Assertions.assertEquals(res6.getCode(), 206)); + + // getInfo7 + request("/rt/getInfo7", res7 -> Assertions.assertEquals(res7.getCode(), 200)); + + // getInfo8 + request("/rt/getInfo8", res8 -> Assertions.assertEquals(res8.getCode(), 200)); + + // getInfo9 + request("/rt/getInfo9", res9 -> Assertions.assertEquals(res9.getCode(), 209)); + + // getInfo10 + request("/rt/getInfo10", res10 -> Assertions.assertEquals(res10.getCode(), 200)); + + // getInfo11 + request("/rt/getInfo11", res11 -> Assertions.assertEquals(res11.getCode(), 211)); + + // getInfo12 + request("/rt/getInfo12", res12 -> Assertions.assertEquals(res12.getCode(), 212)); + + // getInfo13 + request("/rt/getInfo13", res13 -> Assertions.assertEquals(res13.getCode(), 213)); + + // getInfo14 + request("/rt/getInfo14", res14 -> Assertions.assertEquals(res14.getCode(), 214)); + + // getInfo15 + request("/rt/getInfo15", res15 -> Assertions.assertEquals(res15.getCode(), 215)); + + } + + // 测试 getUrl() + @Test + public void testGetUrl() { + // getInfo_101 + request("/rt/getInfo_101", res -> Assertions.assertTrue(res.getData().toString().endsWith("/rt/getInfo_101"))); + + // getInfo_101,不包括后面的参数 + request("/rt/getInfo_101?id=1", res2 -> Assertions.assertTrue(res2.getData().toString().endsWith("/rt/getInfo_101"))); + + // 自定义当前域名 + SaManager.getConfig().setCurrDomain("http://xxx.com"); + request("/rt/getInfo_101?id=1", + res3 -> Assertions.assertEquals(res3.getData().toString(), "http://xxx.com/rt/getInfo_101")); + SaManager.getConfig().setCurrDomain(null); + } + + // 测试读取Cookie + @Test + public void testGetCookie() throws Exception { + request("/rt/getInfo_102", Map.of(), Map.of("x-token", "token-111"), res -> { + Assertions.assertEquals(res.getData(), "token-111"); + }).statusCode(200); + + } + + // 测试重定向 + @Test + public void testRedirect() throws Exception { + request("/rt/getInfo16", res -> { + }).statusCode(302).header("Location", "/rt/getInfo3"); + } + + // 空接口 + @Test + public void testGetInfo200() { + request("/rt/getInfo_200", res -> Assertions.assertEquals(res.getCode(), 200)); + request("/rt/getInfo_201", res1 -> Assertions.assertEquals(res1.getCode(), 201)); + request("/rt/getInfo_202", res2 -> Assertions.assertEquals(res2.getCode(), 401)); + + // 登录拿到Token + request("/rt/login?id=10001", resLogin -> { + String satoken = resLogin.get("token", String.class); + request("/rt/getInfo_202?satoken=" + satoken, res3 -> Assertions.assertEquals(res3.getCode(), 200)); + ; + }); + + } + + // 测试转发 + @Test + public void testForward() { + request("/rt/getInfo_103", res -> Assertions.assertEquals(res.getCode(), 200)); + + } + + public static class RouteTestProfile implements QuarkusTestProfile { + public RouteTestProfile() { + } + + public String getConfigProfile() { + return "route"; + } + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaAnnotationResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaAnnotationResourceTest.java new file mode 100644 index 0000000..3ed8bf9 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaAnnotationResourceTest.java @@ -0,0 +1,121 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkiverse.satoken.resteasy.RedisResource; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +/** + * SaAnnotationResteasyResourceTest + * + * @author nayan + * @date 2022/10/8 14:11 + */ +@QuarkusTest +@TestProfile(SaAnnotationResourceTest.AnnotationTestProfile.class) +@QuarkusTestResource(RedisResource.class) +public class SaAnnotationResourceTest extends AbstractRequestTest { + + // 校验通过的情况 + @Test + public void testPassing() { + // 登录拿到Token + request("/at/login?id=10001", res -> { + String satoken = res.get("token", String.class); + Assertions.assertNotNull(satoken); + + // 登录校验,通过 + request("/at/checkLogin?satoken=" + satoken, res2 -> Assertions.assertEquals(res2.getCode(), 200)); + + // 角色校验,通过 + request("/at/checkRole?satoken=" + satoken, res3 -> Assertions.assertEquals(res3.getCode(), 200)); + + // 权限校验,通过 + request("/at/checkPermission?satoken=" + satoken, res4 -> Assertions.assertEquals(res4.getCode(), 200)); + + // 权限校验or角色校验,通过 + request("/at/checkPermission2?satoken=" + satoken, res5 -> Assertions.assertEquals(res5.getCode(), 200)); + + // 开启二级认证 + request("/at/openSafe?satoken=" + satoken, res6 -> Assertions.assertEquals(res6.getCode(), 200)); + + // 校验二级认证,通过 + request("/at/checkSafe?satoken=" + satoken, res7 -> Assertions.assertEquals(res7.getCode(), 200)); + + // 访问校验封禁的接口 ,通过 + request("/at/checkDisable?satoken=" + satoken, res9 -> Assertions.assertEquals(res9.getCode(), 200)); + }); + + } + + // 校验不通过的情况 + @Test + public void testNotPassing() { + // 登录拿到Token + + request("/at/login?id=10002", res -> { + String satoken = res.get("token", String.class); + Assertions.assertNotNull(satoken); + + // 登录校验,不通过 + request("/at/checkLogin", res2 -> Assertions.assertEquals(res2.getCode(), 401)); + + // 角色校验,不通过 + request("/at/checkRole?satoken=" + satoken, res3 -> Assertions.assertEquals(res3.getCode(), 402)); + + // 权限校验,不通过 + request("/at/checkPermission?satoken=" + satoken, res4 -> Assertions.assertEquals(res4.getCode(), 403)); + + // 权限校验or角色校验,不通过 + request("/at/checkPermission2?satoken=" + satoken, res5 -> Assertions.assertEquals(res5.getCode(), 403)); + + // 校验二级认证,不通过 + request("/at/checkSafe?satoken=" + satoken, res7 -> Assertions.assertEquals(res7.getCode(), 901)); + + }); + + // -------- 登录拿到Token + request("/at/login?id=10042", res -> { + String satoken10042 = res.get("token", String.class); + Assertions.assertNotNull(satoken10042); + + // 校验账号封禁 ,通过 + request("/at/disable?id=10042", res8 -> Assertions.assertEquals(res8.getCode(), 200)); + + // 访问校验封禁的接口 ,不通过 + request("/at/checkDisable?satoken=" + satoken10042, res9 -> Assertions.assertEquals(res9.getCode(), 904)); + + // 解封后就能访问了 + request("/at/untieDisable?id=10042", result -> { + }); + request("/at/checkDisable?satoken=" + satoken10042, res10 -> Assertions.assertEquals(res10.getCode(), 200)); + + }); + + } + + // 测试忽略认证 + @Test + public void testIgnore() { + // 必须登录才能访问的 + request("/ig/show1", res1 -> Assertions.assertEquals(res1.getCode(), 401)); + + // 不登录也可以访问的 + request("/ig/show2", res2 -> Assertions.assertEquals(res2.getCode(), 200)); + } + + public static class AnnotationTestProfile implements QuarkusTestProfile { + public AnnotationTestProfile() { + } + + public String getConfigProfile() { + return "annotation"; + } + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaIdTokenResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaIdTokenResourceTest.java new file mode 100644 index 0000000..ff9e774 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaIdTokenResourceTest.java @@ -0,0 +1,104 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import static io.restassured.RestAssured.given; + +import javax.ws.rs.core.MediaType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.exception.IdTokenInvalidException; +import cn.dev33.satoken.id.SaIdUtil; +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkiverse.satoken.resteasy.RedisResource; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.Header; +import io.restassured.response.ValidatableResponse; + +/** + * SaIdTokenResourceTest + * + * @author nayan + * @date 2022/10/8 14:29 + */ +@QuarkusTest +@QuarkusTestResource(RedisResource.class) +public class SaIdTokenResourceTest extends AbstractRequestTest { + + // 获取信息 + @Test + public void testGetInfo() { + String token = SaIdUtil.getToken(); + // 加token,能调通 + request("/id/getInfo", token, res -> { + Assertions.assertEquals(200, res.getCode()); + }); + // 不加token,不能调通 + request("/id/getInfo", "xxx", res -> { + Assertions.assertEquals(902, res.getCode()); + }); + + // 获取信息2 + token = SaIdUtil.getTokenNh(); + // 加token,能调通 + request("/id/getInfo2", token, res -> { + Assertions.assertEquals(200, res.getCode()); + }); + // 不加token,不能调通 + request("/id/getInfo2", "xxx", res -> { + Assertions.assertEquals(902, res.getCode()); + }); + } + + // 基础测试 + @Test + public void testApi() { + String token = SaIdUtil.getToken(); + + // 刷新一下,会有变化 + SaIdUtil.refreshToken(); + String token2 = SaIdUtil.getToken(); + Assertions.assertNotEquals(token, token2); + + // 旧token,变为次级token + String pastToken = SaIdUtil.getPastTokenNh(); + Assertions.assertEquals(token, pastToken); + + // dao中应该有值 + String daoToken = SaManager.getSaTokenDao().get("satoken:var:id-token"); + String daoToken2 = SaManager.getSaTokenDao().get("satoken:var:past-id-token"); + Assertions.assertEquals(token2, daoToken); + Assertions.assertEquals(token, daoToken2); + + // 新旧都有效 + Assertions.assertTrue(SaIdUtil.isValid(token)); + Assertions.assertTrue(SaIdUtil.isValid(token2)); + + // 空的不行 + Assertions.assertFalse(SaIdUtil.isValid(null)); + Assertions.assertFalse(SaIdUtil.isValid("")); + + // 不抛出异常 + Assertions.assertDoesNotThrow(() -> SaIdUtil.checkToken(token)); + Assertions.assertDoesNotThrow(() -> SaIdUtil.checkToken(token2)); + + // 抛出异常 + Assertions.assertThrows(IdTokenInvalidException.class, () -> SaIdUtil.checkToken(null)); + Assertions.assertThrows(IdTokenInvalidException.class, () -> SaIdUtil.checkToken("")); + Assertions.assertThrows(IdTokenInvalidException.class, () -> SaIdUtil.checkToken("aaa")); + } + + protected ValidatableResponse request(String path, String token, SaResultMatcher matcher) { + return given() + .when() + .header(new Header(SaIdUtil.ID_TOKEN, token)) + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/SoMap.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/utils/SoMap.java similarity index 98% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/SoMap.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/utils/SoMap.java index 00d2a2f..6286644 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/SoMap.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/utils/SoMap.java @@ -1,9 +1,15 @@ -package io.quarkiverse.satoken.dao.redis.jackson.it.utils; +package io.quarkiverse.satoken.resteasy.utils; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.text.SimpleDateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; import com.fasterxml.jackson.databind.ObjectMapper; @@ -217,7 +223,7 @@ public List getListByComma(String key, Class cs) { */ public T getModel(Class cs) { try { - return getModelByObject(cs.newInstance()); + return getModelByObject(cs.getConstructor().newInstance()); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/pom.xml b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/pom.xml new file mode 100644 index 0000000..e8e1cba --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + quarkus-satoken-plugins + io.quarkiverse.satoken + 1.30.0 + + quarkus-satoken-dao-alone-redis-parent + pom + Quarkus Satoken Dao Alone Redis - Parent + + deployment + integration-tests + runtime + + + 3.8.1 + ${surefire-plugin.version} + 11 + UTF-8 + UTF-8 + 2.10.1.Final + 3.0.0-M7 + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + + diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/pom.xml b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/pom.xml new file mode 100644 index 0000000..6204d85 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + + io.quarkiverse.satoken + quarkus-satoken-dao-alone-redis-parent + 1.30.0 + + quarkus-satoken-dao-alone-redis + Quarkus Satoken Dao Alone Redis - Runtime + + + io.quarkus + quarkus-arc + + + io.quarkiverse.satoken + quarkus-satoken-dao-redis-jackson + ${project.version} + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/src/main/java/io/quarkus/satoken/dao/alone/redis/runtime/AloneRedisRecorder.java b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/src/main/java/io/quarkus/satoken/dao/alone/redis/runtime/AloneRedisRecorder.java new file mode 100644 index 0000000..2bd221b --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/src/main/java/io/quarkus/satoken/dao/alone/redis/runtime/AloneRedisRecorder.java @@ -0,0 +1,169 @@ +package io.quarkus.satoken.dao.alone.redis.runtime; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; +import org.redisson.config.ConfigSupport; + +import com.fasterxml.jackson.databind.MapperFeature; + +import io.quarkiverse.satoken.dao.redis.jackson.core.SaTokenDaoRedisJackson; +import io.quarkus.arc.Arc; +import io.quarkus.runtime.annotations.Recorder; + +/** + * AloneRedisRecorder + * + * @author nayan + * @date 2022/10/19 14:38 + */ +@Recorder +public class AloneRedisRecorder { + + /** + * 配置信息的前缀 + */ + public static final String ALONE_PREFIX = "sa-token.alone-redis.redisson."; + + public void initAloneRedis() throws IOException { + RedissonClient redissonClient = getSaTokenAloneRedissonClient(); + Arc.container().instance(SaTokenDaoRedisJackson.class).get().init(redissonClient); + + } + + private RedissonClient getSaTokenAloneRedissonClient() throws IOException { + Optional configFile = ConfigProvider.getConfig().getOptionalValue(ALONE_PREFIX + "file", String.class); + InputStream configStream = null; + if (configFile.isPresent()) { + configStream = this.getClass().getResourceAsStream((String) configFile.get()); + } + + String configStr; + if (configStream != null) { + byte[] array = new byte[configStream.available()]; + configStream.read(array); + configStr = new String(array, StandardCharsets.UTF_8); + try { + configStream.close(); + } catch (Exception e) { + + } + } else { + Stream s = StreamSupport.stream(ConfigProvider.getConfig().getPropertyNames().spliterator(), false); + String yaml = toYaml(ALONE_PREFIX, s.sorted().collect(Collectors.toList()), prop -> { + return ConfigProvider.getConfig().getValue(prop, String.class); + }, false); + configStr = yaml; + } + + ConfigSupport support = new ConfigSupport() { + { + this.yamlMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true); + } + }; + Config config = support.fromYAML(configStr, Config.class); + RedissonClient redissonClient = Redisson.create(config); + return redissonClient; + } + + public static String toYaml(String suffix, Iterable propertyNames, Function resolver, + boolean caseSensitive) { + Map map = new HashMap<>(); + + for (String propertyName : propertyNames) { + if (!propertyName.startsWith(suffix)) { + continue; + } + + List pps = Arrays.asList(propertyName.replace(suffix, "").split("\\.")); + String value = resolver.apply(propertyName); + String name = convertKey(pps.get(0), caseSensitive); + if (pps.size() == 2) { + Map m = (Map) map.computeIfAbsent(name, k -> new HashMap()); + String subName = convertKey(pps.get(1), caseSensitive); + m.put(subName, value); + } else { + map.put(name, value); + } + } + + StringBuilder yaml = new StringBuilder(); + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() instanceof Map) { + yaml.append(entry.getKey()).append(":").append("\n"); + + Map m = (Map) entry.getValue(); + for (Map.Entry subEntry : m.entrySet()) { + yaml.append(" ").append(subEntry.getKey()).append(": "); + addValue(yaml, subEntry); + yaml.append("\n"); + } + } else { + yaml.append(entry.getKey()).append(": "); + addValue(yaml, entry); + yaml.append("\n"); + } + } + return yaml.toString(); + } + + private static String convertKey(String key, boolean caseSensitive) { + if (!caseSensitive) { + return key.replace("-", ""); + } + + String[] parts = key.split("-"); + StringBuilder builder = new StringBuilder(); + builder.append(parts[0]); + for (int i = 1; i < parts.length; i++) { + builder.append(parts[i].substring(0, 1).toUpperCase()) + .append(parts[i].substring(1)); + } + return builder.toString(); + } + + private static final Set LIST_NODES = new HashSet<>( + Arrays.asList("node-addresses", "nodeaddresses", "slave-addresses", "slaveaddresses", "addresses")); + + private static void addValue(StringBuilder yaml, Map.Entry subEntry) { + String value = (String) subEntry.getValue(); + if (value.contains(",") || LIST_NODES.contains(subEntry.getKey())) { + for (String part : value.split(",")) { + yaml.append("\n ").append("- \"").append(part.trim()).append("\""); + } + return; + } + + if ("codec".equals(subEntry.getKey()) + || "load-balancer".equals(subEntry.getKey())) { + value = "!<" + value + "> {}"; + } else { + try { + Long.parseLong(value); + } catch (NumberFormatException e) { + if (!Boolean.parseBoolean(value) + && !"null".equals(value)) { + value = "\"" + value + "\""; + } + } + } + + yaml.append(value); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000..a148e7c --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-alone-redis/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Quarkus Satoken Dao Alone Redis +#description: Do something useful. +metadata: +# keywords: +# - quarkus-satoken-dao-alone-redis +# guide: ... +# categories: +# - "miscellaneous" +# status: "preview" \ No newline at end of file diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/deployment/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/deployment/SatokenDaoRedisJacksonProcessor.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/deployment/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/deployment/SatokenDaoRedisJacksonProcessor.java index 26f21a8..646e15f 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/deployment/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/deployment/SatokenDaoRedisJacksonProcessor.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/deployment/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/deployment/SatokenDaoRedisJacksonProcessor.java @@ -1,7 +1,7 @@ package io.quarkiverse.satoken.dao.redis.jackson.deployment; -import io.quarkiverse.satoken.dao.redis.jackson.SaTokenDaoRedisJackson; -import io.quarkiverse.satoken.dao.redis.jackson.SaTokenJacksonModuleCustomizer; +import io.quarkiverse.satoken.dao.redis.jackson.core.SaTokenDaoRedisJackson; +import io.quarkiverse.satoken.dao.redis.jackson.core.SaTokenJacksonModuleCustomizer; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; @@ -18,7 +18,7 @@ FeatureBuildItem feature() { @BuildStep void additionalBeans(BuildProducer additionalBeans) { - additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(SaTokenJacksonModuleCustomizer.class)); additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(SaTokenDaoRedisJackson.class)); + additionalBeans.produce(AdditionalBeanBuildItem.unremovableOf(SaTokenJacksonModuleCustomizer.class)); } } diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/pom.xml b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/pom.xml index 1ade69f..6ee2453 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/pom.xml +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/pom.xml @@ -13,10 +13,7 @@ io.quarkus quarkus-resteasy-reactive-jackson - - io.quarkus - quarkus-resteasy-reactive-jackson - + io.quarkiverse.satoken quarkus-satoken-resteasy diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/it/StpInterfaceImpl.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/it/StpInterfaceImpl.java deleted file mode 100644 index adcecd4..0000000 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/it/StpInterfaceImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.quarkiverse.satoken.dao.redis.jackson.it; - -import java.util.Arrays; -import java.util.List; - -import javax.enterprise.context.ApplicationScoped; - -import cn.dev33.satoken.stp.StpInterface; -import io.quarkus.arc.DefaultBean; -import io.quarkus.arc.Unremovable; - -/** - * StpInterfaceImpl - * - * @author nayan - * @date 2022/4/8 5:54 PM - */ - -@DefaultBean -@Unremovable -@ApplicationScoped -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/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/RouterFilterConfiguration.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/RouterFilterConfiguration.java new file mode 100644 index 0000000..168f5ac --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/RouterFilterConfiguration.java @@ -0,0 +1,138 @@ +package io.quarkiverse.satoken.resteasy.it; + +import java.util.Arrays; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; + +import org.jboss.resteasy.reactive.server.core.CurrentRequestManager; +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.router.SaHttpMethod; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; +import io.quarkiverse.satoken.core.filter.SaRouteInterceptor; +import io.quarkus.arc.Priority; +import io.quarkus.arc.Unremovable; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * io.quarkiverse.satoken.resteasy.it.RouterFilterConfiguration + * + * @author nayan + * @date 2022/10/9 16:20 + */ +@Dependent +public class RouterFilterConfiguration { + + @Produces + @Unremovable + @Priority(1) + public SaRouteInterceptor configure2() { + return new SaRouteInterceptor("/**", handle -> { + + // 匹配 getInfo ,返回code=201 + SaRouter.match("/**") + .match(SaHttpMethod.POST) + .matchMethod("POST") + .match(SaHolder.getRequest().getMethod().equals("POST")) + .match(r -> SaHolder.getRequest().isPath("/rt/getInfo")) + .match(r -> SaHolder.getRequest().isParam("name", "zhang")) + .back(SaResult.code(201)); + + // 匹配 getInfo2 ,返回code=202 + SaRouter.match("/rt/getInfo2") + .match(Arrays.asList("/rt/getInfo2", "/rt/*")) + .notMatch("/rt/getInfo3") + .notMatch(false) + .notMatch(r -> false) + .notMatch(SaHttpMethod.GET) + .notMatchMethod("PUT") + .notMatch(Arrays.asList("/rt/getInfo4", "/rt/getInfo5")) + .back(SaResult.code(202)); + + // 匹配 getInfo3 ,返回code=203 + SaRouter.match("/rt/getInfo3", "/rt/getInfo4", () -> SaRouter.back(SaResult.code(203))); + SaRouter.match("/rt/getInfo4", "/rt/getInfo5", r -> SaRouter.back(SaResult.code(204))); + SaRouter.match("/rt/getInfo5", () -> SaRouter.back(SaResult.code(205))); + SaRouter.match("/rt/getInfo6", r -> SaRouter.back(SaResult.code(206))); + + // 通往 Controller + SaRouter.match(Arrays.asList("/rt/getInfo7")).stop(); + + // 通往 Controller + SaRouter.match("/rt/getInfo8", () -> SaRouter.stop()); + + SaRouter.matchMethod("POST").match("/rt/getInfo9").free(r -> SaRouter.back(SaResult.code(209))); + SaRouter.match(SaHttpMethod.POST).match("/rt/getInfo10").setHit(false).back(); + + // 11 + SaRouter.notMatch("/rt/getInfo11").reset().match("/rt/getInfo11").back(SaResult.code(211)); + SaRouter.notMatch(SaHttpMethod.GET).match("/rt/getInfo12").back(SaResult.code(212)); + SaRouter.notMatch(Arrays.asList("/rt/getInfo12", "/rt/getInfo14")).match("/rt/getInfo13").back(SaResult.code(213)); + SaRouter.notMatchMethod("GET", "PUT").match("/rt/getInfo14").back(SaResult.code(214)); + + // SaRouter.match(Arrays.asList("/rt/getInfo15", "/rt/getInfo16")) + ResteasyReactiveRequestContext resteasyReactiveRequestContext = CurrentRequestManager.get(); + if (SaRouter.isMatchCurrURI("/rt/getInfo15")) { + if (SaHolder.getRequest().getCookieValue("ddd") == null + && SaHolder.getStorage().getSource() == resteasyReactiveRequestContext + && SaHolder.getRequest().getSource() == resteasyReactiveRequestContext + && SaHolder.getResponse().getSource() == resteasyReactiveRequestContext.serverResponse()) { + SaRouter.newMatch().free(r -> SaRouter.back(SaResult.code(215))); + } + } + + SaRouter.match("/rt/getInfo16", () -> { + try { + SaHolder.getResponse().redirect(null); + } catch (Exception e) { + + } + SaHolder.getResponse().redirect("/rt/getInfo3"); + }); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(100) + public SaRouteInterceptor configure31() { + return new SaRouteInterceptor("/rt/getInfo_200", handle -> { + SaRouter.stop(); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(100) + public SaRouteInterceptor configure32() { + return new SaRouteInterceptor("/rt/getInfo_201", handle -> { + StpUtil.checkLogin(); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(101) + public SaRouteInterceptor configure33() { + return new SaRouteInterceptor("/rt/getInfo_201", handle -> { + SaRouter.back(SaResult.code(201)); + }); + } + + @Produces + @Unremovable + @IfBuildProfile("route") + @Priority(100) + public SaRouteInterceptor configure34() { + return new SaRouteInterceptor("/rt/getInfo_202", handle -> { + StpUtil.checkLogin(); + }); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/StpInterfaceConfiguration.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/StpInterfaceConfiguration.java new file mode 100644 index 0000000..ef5b684 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/StpInterfaceConfiguration.java @@ -0,0 +1,90 @@ +package io.quarkiverse.satoken.resteasy.it; + +import java.util.Arrays; +import java.util.List; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Produces; + +import cn.dev33.satoken.stp.StpInterface; +import cn.dev33.satoken.util.SaFoxUtil; +import io.quarkus.arc.DefaultBean; +import io.quarkus.arc.Unremovable; +import io.quarkus.arc.profile.IfBuildProfile; + +/** + * io.quarkiverse.satoken.resteasy.it.StpInterfaceConfiguration + * + * @author nayan + * @date 2022/10/8 18:55 + */ + +@Dependent +public class StpInterfaceConfiguration { + + @Produces + @Unremovable + @IfBuildProfile("annotation") + public StpInterface annotation() { + return new StpInterface() { + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("user*", "art-add", "art-delete", "art-update", "art-get"); + } else { + return null; + } + } + + /** + * 返回一个账号所拥有的角色标识集合 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("admin", "super-admin"); + } else { + return null; + } + } + }; + } + + @Produces + @Unremovable + @DefaultBean + public StpInterface noopTracer() { + return new StpInterface() { + /** + * 返回一个账号所拥有的权限码集合 + */ + @Override + public List getPermissionList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("user*", "art-add", "art-delete", "art-update", "art-get"); + } else { + return null; + } + } + + /** + * 返回一个账号所拥有的角色标识集合 + */ + @Override + public List getRoleList(Object loginId, String loginType) { + int id = SaFoxUtil.getValueByType(loginId, int.class); + if (id == 10001) { + return Arrays.asList("admin", "super-admin"); + } else { + return null; + } + } + }; + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/TestApplication.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/TestApplication.java new file mode 100644 index 0000000..0ba3b4d --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/TestApplication.java @@ -0,0 +1,11 @@ +package io.quarkiverse.satoken.resteasy.it; + +import io.quarkus.runtime.Quarkus; +import io.quarkus.runtime.annotations.QuarkusMain; + +@QuarkusMain +public class TestApplication { + public static void main(String... args) { + Quarkus.run(args); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/LoginResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/LoginResource.java new file mode 100644 index 0000000..ed4fbee --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/LoginResource.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/acc/") +@ApplicationScoped +public class LoginResource { + + // 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456 + @POST + @Path("doLogin") + public SaResult doLogin(@QueryParam("name") String name, @QueryParam("pwd") String pwd) { + // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对 + if ("zhang".equals(name) && "123456".equals(pwd)) { + StpUtil.login(10001); + return SaResult.ok("登录成功").set("token", StpUtil.getTokenValue()); + } + return SaResult.error("登录失败"); + } + + // 查询登录状态 ---- http://localhost:8081/acc/isLogin + @POST + @Path("isLogin") + public SaResult isLogin() { + return SaResult.data(StpUtil.isLogin()); + } + + // 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo + @POST + @SaCheckLogin + @Path("tokenInfo") + public SaResult tokenInfo() { + return SaResult.data(StpUtil.getTokenInfo()); + } + + // 测试注销 ---- http://localhost:8081/acc/logout + @POST + @Path("logout") + public SaResult logout() { + StpUtil.logout(); + return SaResult.ok(); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/MoreResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/MoreResource.java new file mode 100644 index 0000000..a1b88dc --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/MoreResource.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import cn.dev33.satoken.basic.SaBasicUtil; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.model.SaRequest; +import cn.dev33.satoken.util.SaFoxUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/more/") +@ApplicationScoped +public class MoreResource { + + // 一些基本的测试 + @POST + @Path("getInfo") + public SaResult getInfo() { + SaRequest req = SaHolder.getRequest(); + boolean flag = SaFoxUtil.equals(req.getParam("name"), "zhang") + && SaFoxUtil.equals(req.getParam("name2", "li"), "li") + && SaFoxUtil.equals(req.getParamNotNull("name"), "zhang") + && req.isParam("name", "zhang") + && req.isPath("/more/getInfo") + && req.hasParam("name") + && SaFoxUtil.equals(req.getHeader("div"), "val") + && SaFoxUtil.equals(req.getHeader("div", "zhang"), "val") + && SaFoxUtil.equals(req.getHeader("div2", "zhang"), "zhang"); + + SaHolder.getResponse().setServer("sa-server"); + return SaResult.data(flag); + } + + // Http Basic 认证 + @POST + @Path("basicAuth") + public SaResult basicAuth() { + SaBasicUtil.check("sa:123456"); + return SaResult.ok(); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/RouterResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/RouterResource.java new file mode 100644 index 0000000..71dc03a --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/RouterResource.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/rt/") +@ApplicationScoped +public class RouterResource { + + @POST + @Path("getInfo") + public SaResult getInfo() { + return SaResult.ok(); + } + + @POST + @Path("getInfo{var:.*}") + public SaResult getInfo2() { + return SaResult.ok(); + } + + // 读url + @POST + @Path("getInfo_101") + public SaResult getInfo_101() { + return SaResult.data(SaHolder.getRequest().getUrl()); + } + + // 读Cookie + @POST + @Path("getInfo_102") + public SaResult getInfo_102() { + return SaResult.data(SaHolder.getRequest().getCookieValue("x-token")); + } + + // 测试转发 + @POST + @Path("getInfo_103") + public SaResult getInfo_103() { + SaHolder.getRequest().forward("/rt/getInfo_102"); + return SaResult.ok(); + } + + // 空接口 + @POST + @Path("getInfo_200") + public SaResult getInfo_200() { + return SaResult.ok(); + } + + @POST + @Path("getInfo_201") + public SaResult getInfo_201() { + return SaResult.ok(); + } + + @POST + @Path("getInfo_202") + public SaResult getInfo_202() { + return SaResult.ok(); + } + + @POST + @Path("login") + public SaResult login(@QueryParam("id") long id) { + StpUtil.login(id); + return SaResult.ok().set("token", StpUtil.getTokenValue()); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationIgnoreResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationIgnoreResource.java new file mode 100644 index 0000000..8546839 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationIgnoreResource.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.annotation.SaIgnore; +import cn.dev33.satoken.util.SaResult; + +@SaCheckLogin +@Path("/ig/") +@ApplicationScoped +public class SaAnnotationIgnoreResource { + + // 需要登录后访问 + @POST + @Path("show1") + public SaResult show1() { + return SaResult.ok(); + } + + // 不登录也可访问 + @SaIgnore + @POST + @Path("show2") + public SaResult show2() { + return SaResult.ok(); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationResource.java new file mode 100644 index 0000000..f0e586d --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaAnnotationResource.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import cn.dev33.satoken.annotation.SaCheckDisable; +import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaCheckRole; +import cn.dev33.satoken.annotation.SaCheckSafe; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/at/") +@ApplicationScoped +public class SaAnnotationResource { + + // 登录 + @POST + @Path("login") + public SaResult login(@QueryParam("id") long id) { + StpUtil.login(id); + return SaResult.ok().set("token", StpUtil.getTokenValue()); + } + + // 登录校验 + @SaCheckLogin + @POST + @Path("checkLogin") + public SaResult checkLogin() { + return SaResult.ok(); + } + + // 角色校验 + @SaCheckRole("admin") + @POST + @Path("checkRole") + public SaResult checkRole() { + return SaResult.ok(); + } + + // 权限校验 + @SaCheckPermission("art-add") + @POST + @Path("checkPermission") + public SaResult checkPermission() { + return SaResult.ok(); + } + + // 权限校验 or 角色校验 + @SaCheckPermission(value = "art-add2", orRole = "admin") + @POST + @Path("checkPermission2") + public SaResult checkPermission2() { + return SaResult.ok(); + } + + // 开启二级认证 + @POST + @Path("openSafe") + public SaResult openSafe() { + StpUtil.openSafe(120); + return SaResult.ok(); + } + + // 二级认证校验 + @SaCheckSafe + @POST + @Path("checkSafe") + public SaResult checkSafe() { + return SaResult.ok(); + } + + // 封禁账号 + @POST + @Path("disable") + public SaResult disable(@QueryParam("id") long id) { + StpUtil.disable(id, "comment", 200); + return SaResult.ok(); + } + + // 服务封禁校验 + @SaCheckDisable("comment") + @POST + @Path("checkDisable") + public SaResult checkDisable() { + return SaResult.ok(); + } + + // 解封账号 + @POST + @Path("untieDisable") + public SaResult untieDisable(@QueryParam("id") long id) { + StpUtil.untieDisable(id, "comment"); + return SaResult.ok(); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaIdTokenResource.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaIdTokenResource.java new file mode 100644 index 0000000..3da9b2b --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/java/io/quarkiverse/satoken/resteasy/it/integrate/SaIdTokenResource.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.quarkiverse.satoken.resteasy.it.integrate; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import cn.dev33.satoken.id.SaIdUtil; +import cn.dev33.satoken.util.SaResult; + +@Path("/id/") +@ApplicationScoped +public class SaIdTokenResource { + + // 获取信息 + @POST + @Path("getInfo") + public SaResult getInfo(@HeaderParam(SaIdUtil.ID_TOKEN) String token) { + // 获取并校验id-token + SaIdUtil.checkToken(token); + // 返回信息 + return SaResult.data("info=zhangsan"); + } + + // 获取信息2 + @POST + @Path("getInfo2") + public SaResult getInfo2() { + // 获取并校验id-token + SaIdUtil.checkCurrentRequestToken(); + // 返回信息 + return SaResult.data("info=zhangsan2"); + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application-annotation.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application-annotation.properties new file mode 100644 index 0000000..6fe45ce --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application-annotation.properties @@ -0,0 +1 @@ +sa-token.annotation-intercepted-enabled=true diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application-route.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application-route.properties new file mode 100644 index 0000000..2ddcd42 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application-route.properties @@ -0,0 +1 @@ +sa-token.route.interceptor=true \ No newline at end of file diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application.properties index e69de29..3a907b8 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application.properties +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/main/resources/application.properties @@ -0,0 +1,7 @@ +sa-token.exception-mapper-enabled=true +quarkus.application.name=sa-token-redis-jackson-test +quarkus.redisson.single-server-config.address=redis://127.0.0.1:63790 +quarkus.redisson.single-server-config.database=0 +quarkus.redisson.codec=org.redisson.codec.JsonJacksonCodec +quarkus.redis.devservices.enabled=true +quarkus.redis.devservices.port=63790 diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/SaTokenResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/SaTokenResourceTest.java deleted file mode 100644 index 0374141..0000000 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/SaTokenResourceTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package io.quarkiverse.satoken.dao.redis.jackson.it.utils; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.jupiter.api.Assertions.*; - -import java.util.Map; - -import javax.ws.rs.core.MediaType; - -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.junit.jupiter.api.Test; - -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.response.ValidatableResponse; - -/** - * SaTokenResourceTest - * - * @author nayan - * @date 2022/4/12 2:43 PM - */ -@QuarkusTest -class SaTokenResourceTest { - - @Test - void doLogin() { - request( - "/acc/doLogin", - Map.of("name", "zhang", "pwd", "123456"), - actual -> { - SoMap so = SoMap.getSoMap().setJsonString( - actual.toString()); - String token = so.getString("token"); - assertEquals(so.getInt("code"), 200); - assertNotNull(token); - return true; - }).header("Set-Cookie", notNullValue()); - } - - @Test - void isLogin() { - - // 获取token - request( - "/acc/doLogin", - Map.of("name", "zhang", "pwd", "123456"), - actual -> { - SoMap so = SoMap.getSoMap().setJsonString( - actual.toString()); - String token = so.getString("token"); - assertEquals(so.getInt("code"), 200); - assertNotNull(token); - - // 是否登录 - isLogin(token, - actual2 -> { - SoMap so2 = SoMap.getSoMap().setJsonString( - actual2.toString()); - assertTrue(so2.getBoolean("data")); - return true; - }); - - // tokenInfo - request( - "/acc/tokenInfo", - Map.of("satoken", token), - actual3 -> { - SoMap so3 = SoMap.getSoMap().setJsonString( - actual3.toString()); - SoMap so4 = SoMap.getSoMap((Map) so3.get("data")); - assertEquals(so4.getString("tokenName"), "satoken"); - assertEquals(so4.getString("tokenValue"), token); - return true; - }); - - // 注销 - request( - "/acc/logout", - Map.of("satoken", token), - actual4 -> true); - // 是否登录 - isLogin(token, - actual5 -> { - SoMap so5 = SoMap.getSoMap().setJsonString( - actual5.toString()); - assertFalse(so5.getBoolean("data")); - return true; - }); - - return true; - }); - - } - - private void isLogin(String token, SaTokenMatcher matcher) { - request( - "/acc/isLogin", - Map.of("satoken", token), - matcher); - } - - private ValidatableResponse request(String path, Map params, SaTokenMatcher matcher) { - return given() - .when() - .queryParams(params) - .accept(MediaType.APPLICATION_JSON) - .post(path) - .then() - .statusCode(200) - .body(matcher); - - } - - interface SaTokenMatcher extends Matcher { - - @Override - default public void describeMismatch(Object actual, Description mismatchDescription) { - - } - - @Override - default public void _dont_implement_Matcher___instead_extend_BaseMatcher_() { - - } - - @Override - default public void describeTo(Description description) { - - } - } -} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/AbstractRequestTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/AbstractRequestTest.java new file mode 100644 index 0000000..ab14554 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/AbstractRequestTest.java @@ -0,0 +1,117 @@ +package io.quarkiverse.satoken.resteasy; + +import static io.restassured.RestAssured.given; + +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.util.SaResult; +import io.quarkiverse.satoken.resteasy.utils.SoMap; +import io.restassured.response.ValidatableResponse; + +/** + * AbstractRequestTest + * + * @author nayan + * @date 2022/10/8 14:12 + */ +public class AbstractRequestTest { + + protected ValidatableResponse request(String path, SaResultMatcher matcher) { + return given() + .when() + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + + protected ValidatableResponse request(String path, Map headers, SaResultMatcher matcher) { + return given() + .when() + .headers(headers) + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + + protected ValidatableResponse request(String path, Map headers, Map cookies, + SaResultMatcher matcher) { + return given() + .when() + .headers(headers) + .cookies(cookies) + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + + public interface SaMapMatcher extends Matcher { + + void match(SoMap result); + + @Override + default boolean matches(Object actual) { + SoMap so = SoMap.getSoMap().setJsonString( + actual.toString()); + match(so); + return true; + } + + @Override + default public void describeMismatch(Object actual, Description mismatchDescription) { + + } + + @Override + default public void _dont_implement_Matcher___instead_extend_BaseMatcher_() { + + } + + @Override + default public void describeTo(Description description) { + + } + } + + public interface SaResultMatcher extends Matcher { + + void match(SaResult result); + + @Override + default boolean matches(Object actual) { + // 转 Map + Map map = SaManager.getSaJsonTemplate().parseJsonToMap(actual.toString()); + + // 转 SaResult 对象 + match(new SaResult().setMap(map)); + return true; + } + + @Override + default public void describeMismatch(Object actual, Description mismatchDescription) { + + } + + @Override + default public void _dont_implement_Matcher___instead_extend_BaseMatcher_() { + + } + + @Override + default public void describeTo(Description description) { + + } + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/BasicTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/BasicTest.java similarity index 53% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/BasicTest.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/BasicTest.java index e5c5888..4090afd 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/BasicTest.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/BasicTest.java @@ -1,21 +1,35 @@ -package io.quarkiverse.satoken.dao.redis.jackson.it.utils; +package io.quarkiverse.satoken.resteasy; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.context.SaTokenContext; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.exception.DisableServiceException; import cn.dev33.satoken.exception.NotLoginException; +import cn.dev33.satoken.exception.NotPermissionException; +import cn.dev33.satoken.exception.NotRoleException; +import cn.dev33.satoken.exception.NotSafeException; +import cn.dev33.satoken.exception.SaJsonConvertException; +import cn.dev33.satoken.json.SaJsonTemplate; import cn.dev33.satoken.session.SaSession; -import cn.dev33.satoken.session.SaSessionCustomUtil; +import cn.dev33.satoken.stp.SaLoginConfig; +import cn.dev33.satoken.stp.SaLoginModel; +import cn.dev33.satoken.stp.StpLogic; import cn.dev33.satoken.stp.StpUtil; -import cn.dev33.satoken.temp.SaTempUtil; import cn.dev33.satoken.util.SaTokenConsts; +import io.quarkiverse.satoken.core.context.SaPathMatcherHolder; +import io.quarkiverse.satoken.core.context.SaTokenContextForQuarkus; +import io.quarkiverse.satoken.core.utils.AntPathMatcher; +import io.quarkiverse.satoken.resteasy.utils.SoMap; import io.quarkus.test.junit.QuarkusTest; @QuarkusTest @@ -28,6 +42,7 @@ public class BasicTest { @BeforeAll public static void beforeClass() { System.out.println("\n\n------------------------ 基础测试 star ..."); + SaManager.getConfig().setActivityTimeout(180); } // 结束 @@ -36,18 +51,55 @@ public static void afterClass() { System.out.println("\n\n------------------------ 基础测试 end ... \n"); } + @AfterEach + public void clear() { + MockResteasyReactiveRequestContext.remove(); + } + + // 测试:基础API + @Test + public void testBasicsApi() { + // 基本API + Assertions.assertEquals(StpUtil.getLoginType(), "login"); + Assertions.assertEquals(StpUtil.getStpLogic(), SaManager.getStpLogic("login")); + Assertions.assertEquals(StpUtil.getTokenName(), "satoken"); + + // 安全的更新 StpUtil 的 StpLogic 对象 + StpLogic loginStpLogic = new StpLogic("login"); + StpUtil.setStpLogic(loginStpLogic); + Assertions.assertEquals(StpUtil.getStpLogic(), loginStpLogic); + Assertions.assertEquals(SaManager.getStpLogic("login"), loginStpLogic); + } + // 测试:登录 @Test - public void doLogin() { + public void testDoLogin() { // 登录 StpUtil.login(10001); String token = StpUtil.getTokenValue(); + // token 存在 + Assertions.assertNotNull(token); + Assertions.assertEquals(token, StpUtil.getTokenValueNotCut()); + Assertions.assertEquals(token, StpUtil.getTokenValueByLoginId(10001)); + Assertions.assertEquals(token, StpUtil.getTokenValueByLoginId(10001, SaTokenConsts.DEFAULT_LOGIN_DEVICE)); + + // token 队列 + List tokenList = StpUtil.getTokenValueListByLoginId(10001); + List tokenList2 = StpUtil.getTokenValueListByLoginId(10001, SaTokenConsts.DEFAULT_LOGIN_DEVICE); + Assertions.assertEquals(token, tokenList.get(tokenList.size() - 1)); + Assertions.assertEquals(token, tokenList2.get(tokenList.size() - 1)); + // API 验证 Assertions.assertTrue(StpUtil.isLogin()); + Assertions.assertDoesNotThrow(() -> StpUtil.checkLogin()); Assertions.assertNotNull(token); // token不为null Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10001); // loginId=10001 - Assertions.assertEquals(StpUtil.getLoginDevice(), SaTokenConsts.DEFAULT_LOGIN_DEVICE); // 登录设备 + Assertions.assertEquals(StpUtil.getLoginIdAsInt(), 10001); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginIdAsString(), "10001"); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginId(), "10001"); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginIdDefaultNull(), "10001"); // loginId=10001 + Assertions.assertEquals(StpUtil.getLoginDevice(), SaTokenConsts.DEFAULT_LOGIN_DEVICE); // 登录设备类型 // db数据 验证 // token存在 @@ -57,12 +109,11 @@ public void doLogin() { Assertions.assertNotNull(session); Assertions.assertEquals(session.getId(), "satoken:login:session:" + 10001); Assertions.assertTrue(session.getTokenSignList().size() >= 1); - } // 测试:注销 @Test - public void logout() { + public void testLogout() { // 登录 StpUtil.login(10001); String token = StpUtil.getTokenValue(); @@ -74,9 +125,16 @@ public void logout() { Assertions.assertNull(StpUtil.getTokenValue()); Assertions.assertFalse(StpUtil.isLogin()); Assertions.assertNull(dao.get("satoken:login:token:" + token)); + + // 全部客户端注销掉 + StpUtil.logout(10001); // Session 应该被清除 SaSession session = dao.getSession("satoken:login:session:" + 10001); Assertions.assertNull(session); + + // 在调用 getLoginId() 就会抛出异常 + Assertions.assertEquals(StpUtil.getLoginId("无值"), "无值"); + Assertions.assertThrows(NotLoginException.class, () -> StpUtil.getLoginId()); } // 测试:Session会话 @@ -85,6 +143,7 @@ public void testSession() { StpUtil.login(10001); // API 应该可以获取 Session + Assertions.assertNotNull(StpUtil.getSession()); Assertions.assertNotNull(StpUtil.getSession(false)); // db中应该存在 Session @@ -109,7 +168,12 @@ public void testSession() { public void testCheckPermission() { StpUtil.login(10001); - // 权限认证 + // 获取权限 + List permissionList = StpUtil.getPermissionList(); + List permissionList2 = StpUtil.getPermissionList(10001); + Assertions.assertEquals(permissionList.size(), permissionList2.size()); + + // 权限校验 Assertions.assertTrue(StpUtil.hasPermission("user-add")); Assertions.assertTrue(StpUtil.hasPermission("user-list")); Assertions.assertTrue(StpUtil.hasPermission("user")); @@ -121,6 +185,17 @@ public void testCheckPermission() { // or Assertions.assertTrue(StpUtil.hasPermissionOr("art-add", "comment-add")); Assertions.assertFalse(StpUtil.hasPermissionOr("comment-add", "comment-delete")); + // more + Assertions.assertTrue(StpUtil.hasPermission(10001, "user-add")); + Assertions.assertFalse(StpUtil.hasPermission(10002, "user-add")); + + // 抛异常 + Assertions.assertThrows(NotPermissionException.class, () -> StpUtil.checkPermission("goods-add")); + Assertions.assertThrows(NotPermissionException.class, () -> StpUtil.checkPermissionAnd("goods-add", "art-add")); + // 不抛异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkPermission("user-add")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkPermissionAnd("art-get", "art-add")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkPermissionOr("goods-add", "art-add")); } // 测试:角色认证 @@ -128,7 +203,12 @@ public void testCheckPermission() { public void testCheckRole() { StpUtil.login(10001); - // 角色认证 + // 获取角色 + List roleList = StpUtil.getRoleList(); + List roleList2 = StpUtil.getRoleList(10001); + Assertions.assertEquals(roleList.size(), roleList2.size()); + + // 角色校验 Assertions.assertTrue(StpUtil.hasRole("admin")); Assertions.assertFalse(StpUtil.hasRole("teacher")); // and @@ -137,6 +217,17 @@ public void testCheckRole() { // or Assertions.assertTrue(StpUtil.hasRoleOr("admin", "ceo")); Assertions.assertFalse(StpUtil.hasRoleOr("ceo", "cto")); + // more + Assertions.assertTrue(StpUtil.hasRole(10001, "admin")); + Assertions.assertFalse(StpUtil.hasRole(10002, "admin2")); + + // 抛异常 + Assertions.assertThrows(NotRoleException.class, () -> StpUtil.checkRole("ceo")); + Assertions.assertThrows(NotRoleException.class, () -> StpUtil.checkRoleAnd("ceo", "admin")); + // 不抛异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkRole("admin")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkRoleAnd("admin", "super-admin")); + Assertions.assertDoesNotThrow(() -> StpUtil.checkRoleOr("ceo", "admin")); } // 测试:根据token强制注销 @@ -164,6 +255,17 @@ public void testLogoutByToken() { } catch (NotLoginException e) { Assertions.assertEquals(e.getType(), NotLoginException.INVALID_TOKEN); } + + // 根据token踢下线 + StpUtil.login(10001); + StpUtil.kickoutByTokenValue(StpUtil.getTokenValue()); + + // 场景值应该是被踢下线 + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + Assertions.assertEquals(e.getType(), NotLoginException.KICK_OUT); + } } // 测试:根据账号id强制注销 @@ -211,31 +313,11 @@ public void testTokenSession() { Assertions.assertNotNull(StpUtil.stpLogic.getTokenSession(false)); SaSession session2 = dao.getSession("satoken:login:token-session:" + token); Assertions.assertNotNull(session2); - } - - // 测试自定义Session - @Test - public void testCustomSession() { - // 刚开始不存在 - Assertions.assertFalse(SaSessionCustomUtil.isExists("art-1")); - SaSession session = dao.getSession("satoken:custom:session:" + "art-1"); - Assertions.assertNull(session); - - // 调用一下 - SaSessionCustomUtil.getSessionById("art-1"); - - // 就存在了 - Assertions.assertTrue(SaSessionCustomUtil.isExists("art-1")); - SaSession session2 = dao.getSession("satoken:custom:session:" + "art-1"); - Assertions.assertNotNull(session2); - // 给删除掉 - SaSessionCustomUtil.deleteSessionById("art-1"); - - // 就又不存在了 - Assertions.assertFalse(SaSessionCustomUtil.isExists("art-1")); - SaSession session3 = dao.getSession("satoken:custom:session:" + "art-1"); - Assertions.assertNull(session3); + // + SaSession tokenSession = StpUtil.getTokenSession(); + SaSession tokenSession2 = StpUtil.getTokenSessionByToken(token); + Assertions.assertEquals(tokenSession.getId(), tokenSession2.getId()); } // 测试:根据账号id踢人 @@ -259,7 +341,7 @@ public void kickoutByLoginId() { } // 测试:账号封禁 - @Test() + @Test public void testDisable() { // 封号 StpUtil.disable(10007, 200); @@ -396,6 +478,12 @@ public void testSwitch() { Assertions.assertTrue(StpUtil.isSwitch()); Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10044); + // 开始身份切换 Lambda 方式 + StpUtil.switchTo(10045, () -> { + Assertions.assertTrue(StpUtil.isSwitch()); + Assertions.assertEquals(StpUtil.getLoginIdAsLong(), 10045); + }); + // 结束切换 StpUtil.endSwitch(); Assertions.assertFalse(StpUtil.isSwitch()); @@ -412,32 +500,39 @@ public void testSearchTokenValue() { StpUtil.login(10004); StpUtil.login(10005); - // 查询 + // 查询 Token 列表 List list = StpUtil.searchTokenValue("", 0, 10, true); Assertions.assertTrue(list.size() >= 5); + + // 查询 Session 列表 + List list2 = StpUtil.searchSessionId("", 0, 10, true); + Assertions.assertTrue(list2.size() >= 5); + list2.stream().forEach(sessionId -> { + Assertions.assertNotNull(StpUtil.getSessionBySessionId(sessionId)); + }); } - // 测试:临时Token认证模块 + // 测试:会话管理(Token-Session) @Test - public void testSaTemp() { - // 生成token - String token = SaTempUtil.createToken("group-1014", 200); - Assertions.assertNotNull(token); - - // 解析token - String value = SaTempUtil.parseToken(token, String.class); - Assertions.assertEquals(value, "group-1014"); - Assertions.assertEquals(dao.getObject("satoken:temp-token:" + token), "group-1014"); - - // 过期时间 - long timeout = SaTempUtil.getTimeout(token); - Assertions.assertTrue(timeout > 195); + public void testSearchTokenSession() { + // 登录 + StpUtil.login(10001); + StpUtil.getTokenSession(); + StpUtil.login(10002); + StpUtil.getTokenSession(); + StpUtil.login(10003); + StpUtil.getTokenSession(); + StpUtil.login(10004); + StpUtil.getTokenSession(); + StpUtil.login(10005); + StpUtil.getTokenSession(); - // 回收token - SaTempUtil.deleteToken(token); - String value2 = SaTempUtil.parseToken(token, String.class); - Assertions.assertEquals(value2, null); - Assertions.assertEquals(dao.getObject("satoken:temp-token:" + token), null); + // 查询 Token-Session 列表 + List list2 = StpUtil.searchTokenSessionId("", 0, 10, true); + Assertions.assertTrue(list2.size() >= 5); + list2.stream().forEach(sessionId -> { + Assertions.assertNotNull(StpUtil.getSessionBySessionId(sessionId)); + }); } // 测试:二级认证 @@ -451,6 +546,7 @@ public void testSafe() { StpUtil.openSafe(2); Assertions.assertTrue(StpUtil.isSafe()); Assertions.assertTrue(StpUtil.getSafeTime() > 0); + StpUtil.checkSafe(); // 自然结束 // Thread.sleep(2500); @@ -460,5 +556,185 @@ public void testSafe() { // StpUtil.openSafe(2); StpUtil.closeSafe(); Assertions.assertFalse(StpUtil.isSafe()); + + // 抛异常 + Assertions.assertThrows(NotSafeException.class, () -> StpUtil.checkSafe()); + } + + // ------------- 复杂点的 + + // 测试:指定设备登录 + @Test + public void testDoLoginByDevice() { + StpUtil.login(10001, "PC"); + Assertions.assertEquals(StpUtil.getLoginDevice(), "PC"); + + // 指定一个其它的设备注销,应该注销不掉 + StpUtil.logout(10001, "APP"); + Assertions.assertTrue(StpUtil.isLogin()); + + // 指定当前设备踢掉,则能够踢掉 + StpUtil.kickout(10001, "PC"); + Assertions.assertFalse(StpUtil.isLogin()); + + // 顶掉 + StpUtil.login(10001, "PC"); + StpUtil.replaced(10001, "PC"); + Assertions.assertFalse(StpUtil.isLogin()); + try { + StpUtil.checkLogin(); + } catch (NotLoginException e) { + // 场景值应该为-4 + Assertions.assertEquals(e.getType(), NotLoginException.BE_REPLACED); + } + } + + // 测试:指定 timeout 登录 + @Test + public void testDoLoginByTimeout() { + + // 指定timeout 登录 + StpUtil.login(10001, 100); + long timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout <= 100 && timeout >= 99); + + // 续期一下 + StpUtil.renewTimeout(200); + timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout <= 200 && timeout >= 199); + + // 续期一下 + StpUtil.renewTimeout(StpUtil.getTokenValue(), 300); + timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout <= 300 && timeout >= 299); + + // Session 也会续期 + timeout = StpUtil.getSessionTimeout(); + Assertions.assertTrue(timeout >= 299); + + StpUtil.getTokenSession(); + timeout = StpUtil.getTokenSessionTimeout(); + Assertions.assertTrue(timeout >= 299); + + // 注销后,就是-2 + StpUtil.logout(); + timeout = StpUtil.getTokenTimeout(); + Assertions.assertTrue(timeout == SaTokenDao.NOT_VALUE_EXPIRE); + } + + // 测试:预定 Token 登录 + @Test + public void testDoLoginBySetToken() { + // 预定 Token 登录 + StpUtil.login(10001, new SaLoginModel().setToken("qwer-qwer-qwer-qwer")); + Assertions.assertEquals(StpUtil.getTokenValue(), "qwer-qwer-qwer-qwer"); + + // 注销后,应该清除Token + StpUtil.logout(); + Assertions.assertNull(StpUtil.getTokenValue()); + } + + // 测试:无上下文注入的登录 + @Test + public void testCreateLoginSession() { + + // 无上下文注入的登录 + StpUtil.createLoginSession(10001); + Assertions.assertNull(StpUtil.getTokenValue()); + + // 无上下文注入的登录 + String token = StpUtil.createLoginSession(10001, new SaLoginModel()); + Assertions.assertNull(StpUtil.getTokenValue()); + + // 手动写入 + StpUtil.setTokenValue(token); + Assertions.assertNotNull(StpUtil.getTokenValue()); + + // 手动写入到Cookie + StpUtil.setTokenValue(token, 10); + Assertions.assertNotNull(StpUtil.getTokenValue()); } + + // 测试,匿名 Token-Session + @Test + public void testAnonTokenSession() { + // token 不存在 + StpUtil.logout(); + Assertions.assertNull(StpUtil.getTokenValue()); + + // token 存在 + SaSession anonTokenSession = StpUtil.getAnonTokenSession(); + String token = StpUtil.getTokenValue(); + Assertions.assertNotNull(token); + // 写个值 + anonTokenSession.set("code", "123456"); + + // 登录时,预定上 + StpUtil.login(10001, SaLoginConfig.setToken(token)); + // token不变 + Assertions.assertEquals(token, StpUtil.getTokenValue()); + + // Token-Session 存在,且不变 + SaSession tokenSession = StpUtil.getTokenSession(); + Assertions.assertEquals(anonTokenSession.getId(), tokenSession.getId()); + + // 刚才写的值,仍然在 + Assertions.assertEquals(tokenSession.get("code"), "123456"); + } + + // 测试,临时过期 + @Test + public void testActivityTimeout() { + // 登录 + StpUtil.login(10001); + Assertions.assertNotNull(StpUtil.getTokenValue()); + + // 默认跟随全局 timeout + StpUtil.updateLastActivityToNow(); + long activityTimeout = StpUtil.getTokenActivityTimeout(); + Assertions.assertTrue(activityTimeout <= 180 || activityTimeout >= 179); + + // 不会抛出异常 + Assertions.assertDoesNotThrow(() -> StpUtil.checkActivityTimeout()); + } + + // 测试,上下文 API + @Test + public void testSaTokenContext() { + SaTokenContext context = SaHolder.getContext(); + // path 匹配 + Assertions.assertTrue(context.matchPath("/user/**", "/user/add")); + // context 是否有效 + Assertions.assertTrue(context.isValid()); + // 是否为web环境 + Assertions.assertFalse(((SaTokenContextForQuarkus) context).isWeb()); + // // pathMatcher + // // Assertions.assertEquals(pathMatcher, SaPathMatcherHolder.getPathMatcher()); + // 自创建 + SaPathMatcherHolder.pathMatcher = null; + Assertions.assertNotNull(SaPathMatcherHolder.getPathMatcher()); + SaPathMatcherHolder.pathMatcher = new AntPathMatcher(); + } + + // 测试json转换 + @Test + public void testSaJsonTemplate() { + SaJsonTemplate saJsonTemplate = SaManager.getSaJsonTemplate(); + + // map 转 json + SoMap map = SoMap.getSoMap("name", "zhangsan"); + String jsonString = saJsonTemplate.toJsonString(map); + Assertions.assertEquals(jsonString, "{\"name\":\"zhangsan\"}"); + + // 抛异常 + Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.toJsonString(new Object())); + + // json 转 map + Map map2 = saJsonTemplate.parseJsonToMap("{\"name\":\"zhangsan\"}"); + Assertions.assertEquals(map2.get("name"), "zhangsan"); + + // 抛异常 + Assertions.assertThrows(SaJsonConvertException.class, () -> saJsonTemplate.parseJsonToMap("")); + } + } diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/ManyLoginTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/ManyLoginTest.java similarity index 85% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/ManyLoginTest.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/ManyLoginTest.java index c890dde..9f79d5a 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/dao/redis/jackson/it/utils/ManyLoginTest.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/ManyLoginTest.java @@ -1,4 +1,4 @@ -package io.quarkiverse.satoken.dao.redis.jackson.it.utils; +package io.quarkiverse.satoken.resteasy; import java.util.List; @@ -57,10 +57,10 @@ public void login() { public void login2() { SaManager.setConfig(new SaTokenConfig()); - StpUtil.login(10002, "APP"); + StpUtil.login(10001, "APP"); String token1 = StpUtil.getTokenValue(); - StpUtil.login(10002, "PC"); + StpUtil.login(10001, "PC"); String token2 = StpUtil.getTokenValue(); Assertions.assertNotEquals(token1, token2); @@ -71,10 +71,10 @@ public void login2() { public void login3() { SaManager.setConfig(new SaTokenConfig().setIsShare(false)); - StpUtil.login(10003); + StpUtil.login(10001); String token1 = StpUtil.getTokenValue(); - StpUtil.login(10003); + StpUtil.login(10001); String token2 = StpUtil.getTokenValue(); Assertions.assertNotEquals(token1, token2); @@ -85,10 +85,10 @@ public void login3() { public void login4() { SaManager.setConfig(new SaTokenConfig().setIsConcurrent(false)); - StpUtil.login(10004); + StpUtil.login(10001); String token1 = StpUtil.getTokenValue(); - StpUtil.login(10004); + StpUtil.login(10001); String token2 = StpUtil.getTokenValue(); // token不同 @@ -98,7 +98,7 @@ public void login4() { Assertions.assertEquals(dao.get("satoken:login:token:" + token1), "-4"); // User-Session里的 token1 签名会被移除 - List tokenSignList = StpUtil.getSessionByLoginId(10004).getTokenSignList(); + List tokenSignList = StpUtil.getSessionByLoginId(10001).getTokenSignList(); for (TokenSign tokenSign : tokenSignList) { Assertions.assertNotEquals(tokenSign.getValue(), token1); } @@ -109,17 +109,17 @@ public void login4() { public void login5() { SaManager.setConfig(new SaTokenConfig()); - StpUtil.login(10005, "APP"); + StpUtil.login(10001, "APP"); String token1 = StpUtil.getTokenValue(); - StpUtil.login(10005, "PC"); + StpUtil.login(10001, "PC"); String token2 = StpUtil.getTokenValue(); - StpUtil.login(10005, "h5"); + StpUtil.login(10001, "h5"); String token3 = StpUtil.getTokenValue(); // 注销 - StpUtil.logout(10005); + StpUtil.logout(10001); // 三个Token应该全部无效 Assertions.assertNull(dao.get("satoken:login:token:" + token1)); @@ -127,8 +127,8 @@ public void login5() { Assertions.assertNull(dao.get("satoken:login:token:" + token3)); // User-Session也应该被清除掉 - Assertions.assertNull(StpUtil.getSessionByLoginId(10005, false)); - Assertions.assertNull(dao.getSession("satoken:login:session:" + 10005)); + Assertions.assertNull(StpUtil.getSessionByLoginId(10001, false)); + Assertions.assertNull(dao.getSession("satoken:login:session:" + 10001)); } // 测试:多端登录,一起强制踢下线 @@ -136,17 +136,17 @@ public void login5() { public void login6() { SaManager.setConfig(new SaTokenConfig()); - StpUtil.login(10006, "APP"); + StpUtil.login(10001, "APP"); String token1 = StpUtil.getTokenValue(); - StpUtil.login(10006, "PC"); + StpUtil.login(10001, "PC"); String token2 = StpUtil.getTokenValue(); - StpUtil.login(10006, "h5"); + StpUtil.login(10001, "h5"); String token3 = StpUtil.getTokenValue(); // 注销 - StpUtil.kickout(10006); + StpUtil.kickout(10001); // 三个Token应该全部无效 Assertions.assertEquals(dao.get("satoken:login:token:" + token1), "-5"); @@ -154,8 +154,8 @@ public void login6() { Assertions.assertEquals(dao.get("satoken:login:token:" + token3), "-5"); // User-Session也应该被清除掉 - Assertions.assertNull(StpUtil.getSessionByLoginId(10006, false)); - Assertions.assertNull(dao.getSession("satoken:login:session:" + 10006)); + Assertions.assertNull(StpUtil.getSessionByLoginId(10001, false)); + Assertions.assertNull(dao.getSession("satoken:login:session:" + 10001)); } // 测试:多账号模式,在一个账号体系里登录成功,在另一个账号体系不会校验通过 @@ -163,7 +163,7 @@ public void login6() { public void login7() { SaManager.setConfig(new SaTokenConfig()); - StpUtil.login(10008); + StpUtil.login(10001); String token1 = StpUtil.getTokenValue(); StpLogic stp = new StpLogic("user"); diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/LoginResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/LoginResourceTest.java new file mode 100644 index 0000000..0e882c2 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/LoginResourceTest.java @@ -0,0 +1,77 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkiverse.satoken.resteasy.utils.SoMap; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.response.ValidatableResponse; + +@QuarkusTest +public class LoginResourceTest extends AbstractRequestTest { + + @Test + void testLogin() { + requestSoMap( + "/acc/doLogin?name=zhang&pwd=123456", + so -> { + String token = so.getString("token"); + assertEquals(so.getInt("code"), 200); + assertNotNull(token); + }).header("Set-Cookie", notNullValue()); + } + + @Test + void testLogin2() { + requestSoMap( + "/acc/doLogin?name=zhang&pwd=123456", + so -> { + Assertions.assertNotNull(so.getString("token")); + String token = so.getString("token"); + + // 是否登录 + requestSoMap("/acc/isLogin?satoken=" + token, so2 -> { + Assertions.assertTrue(so2.getBoolean("data")); + }); + + // tokenInfo + requestSoMap("/acc/tokenInfo?satoken=" + token, so3 -> { + SoMap so4 = SoMap.getSoMap((Map) so3.get("data")); + Assertions.assertEquals(so4.getString("tokenName"), "satoken"); + Assertions.assertEquals(so4.getString("tokenValue"), token); + }); + + // 注销 + requestSoMap("/acc/logout?satoken=" + token, so4 -> { + }); + + // 是否登录 + requestSoMap("/acc/isLogin?satoken=" + token, so5 -> { + Assertions.assertFalse(so5.getBoolean("data")); + }); + + }); + } + + protected ValidatableResponse requestSoMap(String path, SaMapMatcher matcher) { + return given() + .when() + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .statusCode(200) + .body(matcher); + + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/MoreResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/MoreResourceTest.java new file mode 100644 index 0000000..52203c4 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/MoreResourceTest.java @@ -0,0 +1,38 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkus.test.junit.QuarkusTest; + +@QuarkusTest +public class MoreResourceTest extends AbstractRequestTest { + + // 基础API测试 + @Test + public void testApi() { + request("/more/getInfo?name=zhang", Map.of("div", "val"), res -> { + Assertions.assertEquals(res.getData(), true); + }).statusCode(200); + + } + + // Http Basic 认证 + @Test + public void testBasic() throws Exception { + + // ---------------- 认证不通过 + request("/more/basicAuth", res -> { + Assertions.assertEquals(res.getCode(), 903); + }).statusCode(401).header("WWW-Authenticate", "Basic Realm=Sa-Token"); + + // ---------------- 认证通过 + request("/more/basicAuth", Map.of("Authorization", "Basic c2E6MTIzNDU2"), res -> { + Assertions.assertEquals(res.getCode(), 200); + }).statusCode(200); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/RouteResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/RouteResourceTest.java new file mode 100644 index 0000000..894e0d0 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/RouteResourceTest.java @@ -0,0 +1,166 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import java.util.Arrays; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.router.SaRouter; +import cn.dev33.satoken.router.SaRouterStaff; +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +/** + * RouteResourceTest + * + * @author nayan + * @date 2022/10/9 16:26 + */ +@QuarkusTest +@TestProfile(RouteResourceTest.RouteTestProfile.class) +public class RouteResourceTest extends AbstractRequestTest { + + // 基础API测试 + @Test + public void testApi() { + // 是否命中 + SaRouterStaff staff = SaRouter.match(false); + Assertions.assertFalse(staff.isHit()); + + // 重置 + staff.reset(); + Assertions.assertTrue(staff.isHit()); + + // lambda 形式 + SaRouterStaff staff2 = SaRouter.match(r -> false); + Assertions.assertFalse(staff2.isHit()); + + // 匹配 + Assertions.assertTrue(SaRouter.isMatch("/user/**", "/user/add")); + Assertions.assertTrue(SaRouter.isMatch(new String[] { "/user/**", "/art/**", "/goods/**" }, "/art/delete")); + Assertions.assertTrue(SaRouter.isMatch(Arrays.asList("/user/**", "/art/**", "/goods/**"), "/art/delete")); + Assertions.assertTrue(SaRouter.isMatch(new String[] { "POST", "GET", "PUT" }, "GET")); + + // 不匹配的 + Assertions.assertTrue(SaRouter.notMatch(false).isHit()); + Assertions.assertTrue(SaRouter.notMatch(r -> false).isHit()); + } + + // 各种路由测试 + @Test + public void testRouter() { + // getInfo + request("/rt/getInfo?name=zhang", res -> Assertions.assertEquals(res.getCode(), 201)); + + // getInfo2 + request("/rt/getInfo2", res2 -> Assertions.assertEquals(res2.getCode(), 202)); + + // getInfo3 + request("/rt/getInfo3", res3 -> Assertions.assertEquals(res3.getCode(), 203)); + + // getInfo4 + request("/rt/getInfo4", res4 -> Assertions.assertEquals(res4.getCode(), 204)); + + // getInfo5 + request("/rt/getInfo5", res5 -> Assertions.assertEquals(res5.getCode(), 205)); + + // getInfo6 + request("/rt/getInfo6", res6 -> Assertions.assertEquals(res6.getCode(), 206)); + + // getInfo7 + request("/rt/getInfo7", res7 -> Assertions.assertEquals(res7.getCode(), 200)); + + // getInfo8 + request("/rt/getInfo8", res8 -> Assertions.assertEquals(res8.getCode(), 200)); + + // getInfo9 + request("/rt/getInfo9", res9 -> Assertions.assertEquals(res9.getCode(), 209)); + + // getInfo10 + request("/rt/getInfo10", res10 -> Assertions.assertEquals(res10.getCode(), 200)); + + // getInfo11 + request("/rt/getInfo11", res11 -> Assertions.assertEquals(res11.getCode(), 211)); + + // getInfo12 + request("/rt/getInfo12", res12 -> Assertions.assertEquals(res12.getCode(), 212)); + + // getInfo13 + request("/rt/getInfo13", res13 -> Assertions.assertEquals(res13.getCode(), 213)); + + // getInfo14 + request("/rt/getInfo14", res14 -> Assertions.assertEquals(res14.getCode(), 214)); + + // getInfo15 + request("/rt/getInfo15", res15 -> Assertions.assertEquals(res15.getCode(), 215)); + + } + + // 测试 getUrl() + @Test + public void testGetUrl() { + // getInfo_101 + request("/rt/getInfo_101", res -> Assertions.assertTrue(res.getData().toString().endsWith("/rt/getInfo_101"))); + + // getInfo_101,不包括后面的参数 + request("/rt/getInfo_101?id=1", res2 -> Assertions.assertTrue(res2.getData().toString().endsWith("/rt/getInfo_101"))); + + // 自定义当前域名 + SaManager.getConfig().setCurrDomain("http://xxx.com"); + request("/rt/getInfo_101?id=1", + res3 -> Assertions.assertEquals(res3.getData().toString(), "http://xxx.com/rt/getInfo_101")); + SaManager.getConfig().setCurrDomain(null); + } + + // 测试读取Cookie + @Test + public void testGetCookie() throws Exception { + request("/rt/getInfo_102", Map.of(), Map.of("x-token", "token-111"), res -> { + Assertions.assertEquals(res.getData(), "token-111"); + }).statusCode(200); + + } + + // 测试重定向 + @Test + public void testRedirect() throws Exception { + request("/rt/getInfo16", res -> { + }).statusCode(302).header("Location", "/rt/getInfo3"); + } + + // 空接口 + @Test + public void testGetInfo200() { + request("/rt/getInfo_200", res -> Assertions.assertEquals(res.getCode(), 200)); + request("/rt/getInfo_201", res1 -> Assertions.assertEquals(res1.getCode(), 201)); + request("/rt/getInfo_202", res2 -> Assertions.assertEquals(res2.getCode(), 401)); + + // 登录拿到Token + request("/rt/login?id=10001", resLogin -> { + String satoken = resLogin.get("token", String.class); + request("/rt/getInfo_202?satoken=" + satoken, res3 -> Assertions.assertEquals(res3.getCode(), 200)); + ; + }); + + } + + // 测试转发 + @Test + public void testForward() { + request("/rt/getInfo_103", res -> Assertions.assertEquals(res.getCode(), 200)); + + } + + public static class RouteTestProfile implements QuarkusTestProfile { + public RouteTestProfile() { + } + + public String getConfigProfile() { + return "route"; + } + } +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaAnnotationResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaAnnotationResourceTest.java new file mode 100644 index 0000000..60a5a80 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaAnnotationResourceTest.java @@ -0,0 +1,118 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +/** + * SaAnnotationResteasyResourceTest + * + * @author nayan + * @date 2022/10/8 14:11 + */ +@QuarkusTest +@TestProfile(SaAnnotationResourceTest.AnnotationTestProfile.class) +public class SaAnnotationResourceTest extends AbstractRequestTest { + + // 校验通过的情况 + @Test + public void testPassing() { + // 登录拿到Token + request("/at/login?id=10001", res -> { + String satoken = res.get("token", String.class); + Assertions.assertNotNull(satoken); + + // 登录校验,通过 + request("/at/checkLogin?satoken=" + satoken, res2 -> Assertions.assertEquals(res2.getCode(), 200)); + + // 角色校验,通过 + request("/at/checkRole?satoken=" + satoken, res3 -> Assertions.assertEquals(res3.getCode(), 200)); + + // 权限校验,通过 + request("/at/checkPermission?satoken=" + satoken, res4 -> Assertions.assertEquals(res4.getCode(), 200)); + + // 权限校验or角色校验,通过 + request("/at/checkPermission2?satoken=" + satoken, res5 -> Assertions.assertEquals(res5.getCode(), 200)); + + // 开启二级认证 + request("/at/openSafe?satoken=" + satoken, res6 -> Assertions.assertEquals(res6.getCode(), 200)); + + // 校验二级认证,通过 + request("/at/checkSafe?satoken=" + satoken, res7 -> Assertions.assertEquals(res7.getCode(), 200)); + + // 访问校验封禁的接口 ,通过 + request("/at/checkDisable?satoken=" + satoken, res9 -> Assertions.assertEquals(res9.getCode(), 200)); + }); + + } + + // 校验不通过的情况 + @Test + public void testNotPassing() { + // 登录拿到Token + + request("/at/login?id=10002", res -> { + String satoken = res.get("token", String.class); + Assertions.assertNotNull(satoken); + + // 登录校验,不通过 + request("/at/checkLogin", res2 -> Assertions.assertEquals(res2.getCode(), 401)); + + // 角色校验,不通过 + request("/at/checkRole?satoken=" + satoken, res3 -> Assertions.assertEquals(res3.getCode(), 402)); + + // 权限校验,不通过 + request("/at/checkPermission?satoken=" + satoken, res4 -> Assertions.assertEquals(res4.getCode(), 403)); + + // 权限校验or角色校验,不通过 + request("/at/checkPermission2?satoken=" + satoken, res5 -> Assertions.assertEquals(res5.getCode(), 403)); + + // 校验二级认证,不通过 + request("/at/checkSafe?satoken=" + satoken, res7 -> Assertions.assertEquals(res7.getCode(), 901)); + + }); + + // -------- 登录拿到Token + request("/at/login?id=10042", res -> { + String satoken10042 = res.get("token", String.class); + Assertions.assertNotNull(satoken10042); + + // 校验账号封禁 ,通过 + request("/at/disable?id=10042", res8 -> Assertions.assertEquals(res8.getCode(), 200)); + + // 访问校验封禁的接口 ,不通过 + request("/at/checkDisable?satoken=" + satoken10042, res9 -> Assertions.assertEquals(res9.getCode(), 904)); + + // 解封后就能访问了 + request("/at/untieDisable?id=10042", result -> { + }); + request("/at/checkDisable?satoken=" + satoken10042, res10 -> Assertions.assertEquals(res10.getCode(), 200)); + + }); + + } + + // 测试忽略认证 + @Test + public void testIgnore() { + // 必须登录才能访问的 + request("/ig/show1", res1 -> Assertions.assertEquals(res1.getCode(), 401)); + + // 不登录也可以访问的 + request("/ig/show2", res2 -> Assertions.assertEquals(res2.getCode(), 200)); + } + + public static class AnnotationTestProfile implements QuarkusTestProfile { + public AnnotationTestProfile() { + } + + public String getConfigProfile() { + return "annotation"; + } + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaIdTokenResourceTest.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaIdTokenResourceTest.java new file mode 100644 index 0000000..6d64b04 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/integrate/SaIdTokenResourceTest.java @@ -0,0 +1,101 @@ +package io.quarkiverse.satoken.resteasy.integrate; + +import static io.restassured.RestAssured.given; + +import javax.ws.rs.core.MediaType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cn.dev33.satoken.SaManager; +import cn.dev33.satoken.exception.IdTokenInvalidException; +import cn.dev33.satoken.id.SaIdUtil; +import io.quarkiverse.satoken.resteasy.AbstractRequestTest; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.Header; +import io.restassured.response.ValidatableResponse; + +/** + * SaIdTokenResourceTest + * + * @author nayan + * @date 2022/10/8 14:29 + */ +@QuarkusTest +public class SaIdTokenResourceTest extends AbstractRequestTest { + + // 获取信息 + @Test + public void testGetInfo() { + String token = SaIdUtil.getToken(); + // 加token,能调通 + request("/id/getInfo", token, res -> { + Assertions.assertEquals(200, res.getCode()); + }); + // 不加token,不能调通 + request("/id/getInfo", "xxx", res -> { + Assertions.assertEquals(902, res.getCode()); + }); + + // 获取信息2 + token = SaIdUtil.getTokenNh(); + // 加token,能调通 + request("/id/getInfo2", token, res -> { + Assertions.assertEquals(200, res.getCode()); + }); + // 不加token,不能调通 + request("/id/getInfo2", "xxx", res -> { + Assertions.assertEquals(902, res.getCode()); + }); + } + + // 基础测试 + @Test + public void testApi() { + String token = SaIdUtil.getToken(); + + // 刷新一下,会有变化 + SaIdUtil.refreshToken(); + String token2 = SaIdUtil.getToken(); + Assertions.assertNotEquals(token, token2); + + // 旧token,变为次级token + String pastToken = SaIdUtil.getPastTokenNh(); + Assertions.assertEquals(token, pastToken); + + // dao中应该有值 + String daoToken = SaManager.getSaTokenDao().get("satoken:var:id-token"); + String daoToken2 = SaManager.getSaTokenDao().get("satoken:var:past-id-token"); + Assertions.assertEquals(token2, daoToken); + Assertions.assertEquals(token, daoToken2); + + // 新旧都有效 + Assertions.assertTrue(SaIdUtil.isValid(token)); + Assertions.assertTrue(SaIdUtil.isValid(token2)); + + // 空的不行 + Assertions.assertFalse(SaIdUtil.isValid(null)); + Assertions.assertFalse(SaIdUtil.isValid("")); + + // 不抛出异常 + Assertions.assertDoesNotThrow(() -> SaIdUtil.checkToken(token)); + Assertions.assertDoesNotThrow(() -> SaIdUtil.checkToken(token2)); + + // 抛出异常 + Assertions.assertThrows(IdTokenInvalidException.class, () -> SaIdUtil.checkToken(null)); + Assertions.assertThrows(IdTokenInvalidException.class, () -> SaIdUtil.checkToken("")); + Assertions.assertThrows(IdTokenInvalidException.class, () -> SaIdUtil.checkToken("aaa")); + } + + protected ValidatableResponse request(String path, String token, SaResultMatcher matcher) { + return given() + .when() + .header(new Header(SaIdUtil.ID_TOKEN, token)) + .accept(MediaType.APPLICATION_JSON) + .post(path) + .then() + .body(matcher); + + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/utils/SoMap.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/utils/SoMap.java new file mode 100644 index 0000000..6286644 --- /dev/null +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/java/io/quarkiverse/satoken/resteasy/utils/SoMap.java @@ -0,0 +1,788 @@ +package io.quarkiverse.satoken.resteasy.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Map< String, Object> 是最常用的一种Map类型,但是它写着麻烦 + *

+ * 所以特封装此类,继承Map,进行一些扩展,可以让Map更灵活使用 + *

+ * 最新:2020-12-10 新增部分构造方法 + * + * @author kong + */ +public class SoMap extends LinkedHashMap { + + private static final long serialVersionUID = 1L; + + public SoMap() { + } + + /** + * 以下元素会在isNull函数中被判定为Null, + */ + public static final Object[] NULL_ELEMENT_ARRAY = { null, "" }; + public static final List NULL_ELEMENT_LIST; + + static { + NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY); + } + + // ============================= 读值 ============================= + + /** + * 获取一个值 + */ + @Override + public Object get(Object key) { + if ("this".equals(key)) { + return this; + } + return super.get(key); + } + + /** + * 如果为空,则返回默认值 + */ + public Object get(Object key, Object defaultValue) { + Object value = get(key); + if (valueIsNull(value)) { + return defaultValue; + } + return value; + } + + /** + * 转为String并返回 + */ + public String getString(String key) { + Object value = get(key); + if (value == null) { + return null; + } + return String.valueOf(value); + } + + /** + * 如果为空,则返回默认值 + */ + public String getString(String key, String defaultValue) { + Object value = get(key); + if (valueIsNull(value)) { + return defaultValue; + } + return String.valueOf(value); + } + + /** + * 转为int并返回 + */ + public int getInt(String key) { + Object value = get(key); + if (valueIsNull(value)) { + return 0; + } + return Integer.valueOf(String.valueOf(value)); + } + + /** + * 转为int并返回,同时指定默认值 + */ + public int getInt(String key, int defaultValue) { + Object value = get(key); + if (valueIsNull(value)) { + return defaultValue; + } + return Integer.valueOf(String.valueOf(value)); + } + + /** + * 转为long并返回 + */ + public long getLong(String key) { + Object value = get(key); + if (valueIsNull(value)) { + return 0; + } + return Long.valueOf(String.valueOf(value)); + } + + /** + * 转为double并返回 + */ + public double getDouble(String key) { + Object value = get(key); + if (valueIsNull(value)) { + return 0.0; + } + return Double.valueOf(String.valueOf(value)); + } + + /** + * 转为boolean并返回 + */ + public boolean getBoolean(String key) { + Object value = get(key); + if (valueIsNull(value)) { + return false; + } + return Boolean.valueOf(String.valueOf(value)); + } + + /** + * 转为Date并返回,根据自定义格式 + */ + public Date getDateByFormat(String key, String format) { + try { + return new SimpleDateFormat(format).parse(getString(key)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 转为Date并返回,根据格式: yyyy-MM-dd + */ + public Date getDate(String key) { + return getDateByFormat(key, "yyyy-MM-dd"); + } + + /** + * 转为Date并返回,根据格式: yyyy-MM-dd HH:mm:ss + */ + public Date getDateTime(String key) { + return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss"); + } + + /** + * 获取集合(必须原先就是个集合,否则会创建个新集合并返回) + */ + @SuppressWarnings("unchecked") + public List getList(String key) { + Object value = get(key); + List list = null; + if (value == null || value.equals("")) { + list = new ArrayList(); + } else if (value instanceof List) { + list = (List) value; + } else { + list = new ArrayList(); + list.add(value); + } + return list; + } + + /** + * 获取集合 (指定泛型类型) + */ + public List getList(String key, Class cs) { + List list = getList(key); + List list2 = new ArrayList(); + for (Object obj : list) { + T objC = getValueByClass(obj, cs); + list2.add(objC); + } + return list2; + } + + /** + * 获取集合(逗号分隔式),(指定类型) + */ + public List getListByComma(String key, Class cs) { + String listStr = getString(key); + if (listStr == null || listStr.equals("")) { + return new ArrayList<>(); + } + // 开始转化 + String[] arr = listStr.split(","); + List list = new ArrayList(); + for (String str : arr) { + if (cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class) { + str = str.trim(); + } + T objC = getValueByClass(str, cs); + list.add(objC); + } + return list; + } + + /** + * 根据指定类型从map中取值,返回实体对象 + */ + public T getModel(Class cs) { + try { + return getModelByObject(cs.getConstructor().newInstance()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 从map中取值,塞到一个对象中 + */ + public T getModelByObject(T obj) { + // 获取类型 + Class cs = obj.getClass(); + // 循环复制 + for (Field field : cs.getDeclaredFields()) { + try { + // 获取对象 + Object value = this.get(field.getName()); + if (value == null) { + continue; + } + field.setAccessible(true); + Object valueConvert = getValueByClass(value, field.getType()); + field.set(obj, valueConvert); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException("属性取值出错:" + field.getName(), e); + } + } + return obj; + } + + /** + * 将指定值转化为指定类型并返回 + * + * @param obj + * @param cs + * @param + * @return + */ + @SuppressWarnings("unchecked") + public static T getValueByClass(Object obj, Class cs) { + String obj2 = String.valueOf(obj); + Object obj3 = null; + if (cs.equals(String.class)) { + obj3 = obj2; + } else if (cs.equals(int.class) || cs.equals(Integer.class)) { + obj3 = Integer.valueOf(obj2); + } else if (cs.equals(long.class) || cs.equals(Long.class)) { + obj3 = Long.valueOf(obj2); + } else if (cs.equals(short.class) || cs.equals(Short.class)) { + obj3 = Short.valueOf(obj2); + } else if (cs.equals(byte.class) || cs.equals(Byte.class)) { + obj3 = Byte.valueOf(obj2); + } else if (cs.equals(float.class) || cs.equals(Float.class)) { + obj3 = Float.valueOf(obj2); + } else if (cs.equals(double.class) || cs.equals(Double.class)) { + obj3 = Double.valueOf(obj2); + } else if (cs.equals(boolean.class) || cs.equals(Boolean.class)) { + obj3 = Boolean.valueOf(obj2); + } else { + obj3 = (T) obj; + } + return (T) obj3; + } + + // ============================= 写值 ============================= + + /** + * 给指定key添加一个默认值(只有在这个key原来无值的情况先才会set进去) + */ + public void setDefaultValue(String key, Object defaultValue) { + if (isNull(key)) { + set(key, defaultValue); + } + } + + /** + * set一个值,连缀风格 + */ + public SoMap set(String key, Object value) { + // 防止敏感key + if (key.toLowerCase().equals("this")) { + return this; + } + put(key, value); + return this; + } + + /** + * 将一个Map塞进SoMap + */ + public SoMap setMap(Map map) { + if (map != null) { + for (String key : map.keySet()) { + this.set(key, map.get(key)); + } + } + return this; + } + + /** + * 将一个对象解析塞进SoMap + */ + public SoMap setModel(Object model) { + if (model == null) { + return this; + } + Field[] fields = model.getClass().getDeclaredFields(); + for (Field field : fields) { + try { + field.setAccessible(true); + boolean isStatic = Modifier.isStatic(field.getModifiers()); + if (!isStatic) { + this.set(field.getName(), field.get(model)); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return this; + } + + /** + * 将json字符串解析后塞进SoMap + */ + public SoMap setJsonString(String jsonString) { + try { + @SuppressWarnings("unchecked") + Map map = new ObjectMapper().readValue(jsonString, Map.class); + return this.setMap(map); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // ============================= 删值 ============================= + + /** + * delete一个值,连缀风格 + */ + public SoMap delete(String key) { + remove(key); + return this; + } + + /** + * 清理所有value为null的字段 + */ + public SoMap clearNull() { + Iterator iterator = this.keySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next(); + if (this.isNull(key)) { + iterator.remove(); + this.remove(key); + } + + } + return this; + } + + /** + * 清理指定key + */ + public SoMap clearIn(String... keys) { + List keys2 = Arrays.asList(keys); + Iterator iterator = this.keySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next(); + if (keys2.contains(key) == true) { + iterator.remove(); + this.remove(key); + } + } + return this; + } + + /** + * 清理掉不在列表中的key + */ + public SoMap clearNotIn(String... keys) { + List keys2 = Arrays.asList(keys); + Iterator iterator = this.keySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next(); + if (keys2.contains(key) == false) { + iterator.remove(); + this.remove(key); + } + + } + return this; + } + + /** + * 清理掉所有key + */ + public SoMap clearAll() { + clear(); + return this; + } + + // ============================= 快速构建 ============================= + + /** + * 构建一个SoMap并返回 + */ + public static SoMap getSoMap() { + return new SoMap(); + } + + /** + * 构建一个SoMap并返回 + */ + public static SoMap getSoMap(String key, Object value) { + return new SoMap().set(key, value); + } + + /** + * 构建一个SoMap并返回 + */ + public static SoMap getSoMap(Map map) { + return new SoMap().setMap(map); + } + + /** + * 将一个对象集合解析成为SoMap + */ + public static SoMap getSoMapByModel(Object model) { + return SoMap.getSoMap().setModel(model); + } + + /** + * 将一个对象集合解析成为SoMap集合 + */ + public static List getSoMapByList(List list) { + List listMap = new ArrayList(); + for (Object model : list) { + listMap.add(getSoMapByModel(model)); + } + return listMap; + } + + /** + * 克隆指定key,返回一个新的SoMap + */ + public SoMap cloneKeys(String... keys) { + SoMap so = new SoMap(); + for (String key : keys) { + so.set(key, this.get(key)); + } + return so; + } + + /** + * 克隆所有key,返回一个新的SoMap + */ + public SoMap cloneSoMap() { + SoMap so = new SoMap(); + for (String key : this.keySet()) { + so.set(key, this.get(key)); + } + return so; + } + + /** + * 将所有key转为大写 + */ + public SoMap toUpperCase() { + SoMap so = new SoMap(); + for (String key : this.keySet()) { + so.set(key.toUpperCase(), this.get(key)); + } + this.clearAll().setMap(so); + return this; + } + + /** + * 将所有key转为小写 + */ + public SoMap toLowerCase() { + SoMap so = new SoMap(); + for (String key : this.keySet()) { + so.set(key.toLowerCase(), this.get(key)); + } + this.clearAll().setMap(so); + return this; + } + + /** + * 将所有key中下划线转为中划线模式 (kebab-case风格) + */ + public SoMap toKebabCase() { + SoMap so = new SoMap(); + for (String key : this.keySet()) { + so.set(wordEachKebabCase(key), this.get(key)); + } + this.clearAll().setMap(so); + return this; + } + + /** + * 将所有key中下划线转为小驼峰模式 + */ + public SoMap toHumpCase() { + SoMap so = new SoMap(); + for (String key : this.keySet()) { + so.set(wordEachBigFs(key), this.get(key)); + } + this.clearAll().setMap(so); + return this; + } + + /** + * 将所有key中小驼峰转为下划线模式 + */ + public SoMap humpToLineCase() { + SoMap so = new SoMap(); + for (String key : this.keySet()) { + so.set(wordHumpToLine(key), this.get(key)); + } + this.clearAll().setMap(so); + return this; + } + + // ============================= 辅助方法 ============================= + + /** + * 指定key是否为null,判定标准为 NULL_ELEMENT_ARRAY 中的元素 + */ + public boolean isNull(String key) { + return valueIsNull(get(key)); + } + + /** + * 指定key列表中是否包含value为null的元素,只要有一个为null,就会返回true + */ + public boolean isContainNull(String... keys) { + for (String key : keys) { + if (this.isNull(key)) { + return true; + } + } + return false; + } + + /** + * 与isNull()相反 + */ + public boolean isNotNull(String key) { + return !isNull(key); + } + + /** + * 指定key的value是否为null,作用同isNotNull() + */ + public boolean has(String key) { + return !isNull(key); + } + + /** + * 指定value在此SoMap的判断标准中是否为null + */ + public boolean valueIsNull(Object value) { + return NULL_ELEMENT_LIST.contains(value); + } + + /** + * 验证指定key不为空,为空则抛出异常 + */ + public SoMap checkNull(String... keys) { + for (String key : keys) { + if (this.isNull(key)) { + throw new RuntimeException("参数" + key + "不能为空"); + } + } + return this; + } + + static Pattern patternNumber = Pattern.compile("[0-9]*"); + + /** + * 指定key是否为数字 + */ + public boolean isNumber(String key) { + String value = getString(key); + if (value == null) { + return false; + } + return patternNumber.matcher(value).matches(); + } + + /** + * 转为JSON字符串 + */ + public String toJsonString() { + try { + // SoMap so = SoMap.getSoMap(this); + return new ObjectMapper().writeValueAsString(this); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + // + // /** + // * 转为JSON字符串, 带格式的 + // */ + // public String toJsonFormatString() { + // try { + // return JSON.toJSONString(this, true); + // } catch (Exception e) { + // throw new RuntimeException(e); + // } + // } + + // ============================= web辅助 ============================= + + // ============================= 常见key (以下key经常用,所以封装以下,方便写代码) ============================= + + /** + * get 当前页 + */ + public int getKeyPageNo() { + int pageNo = getInt("pageNo", 1); + if (pageNo <= 0) { + pageNo = 1; + } + return pageNo; + } + + /** + * get 页大小 + */ + public int getKeyPageSize() { + int pageSize = getInt("pageSize", 10); + if (pageSize <= 0 || pageSize > 1000) { + pageSize = 10; + } + return pageSize; + } + + /** + * get 排序方式 + */ + public int getKeySortType() { + return getInt("sortType"); + } + + // ============================= 分页相关(封装mybatis的page-help插件 ) ============================= + + // /** 分页插件 */ + // private com.github.pagehelper.Page pagePlug; + // /** 分页插件 - 开始分页 */ + // public SoMap startPage() { + // this.pagePlug= com.github.pagehelper.PageHelper.startPage(getKeyPageNo(), getKeyPageSize()); + // return this; + // } + // /** 获取上次分页的记录总数 */ + // public long getDataCount() { + // if(pagePlug == null) { + // return -1; + // } + // return pagePlug.getTotal(); + // } + // /** 分页插件 - 结束分页, 返回总条数 (该方法已过时,请调用更加符合语义化的getDataCount() ) */ + // @Deprecated + // public long endPage() { + // return getDataCount(); + // } + + // ============================= 工具方法 ============================= + + /** + * 将一个一维集合转换为树形集合 + * + * @param list 集合 + * @param idKey id标识key + * @param parentIdKey 父id标识key + * @param childListKey 子节点标识key + * @return 转换后的tree集合 + */ + public static List listToTree(List list, String idKey, String parentIdKey, String childListKey) { + // 声明新的集合,存储tree形数据 + List newTreeList = new ArrayList(); + // 声明hash-Map,方便查找数据 + SoMap hash = new SoMap(); + // 将数组转为Object的形式,key为数组中的id + for (int i = 0; i < list.size(); i++) { + SoMap json = (SoMap) list.get(i); + hash.put(json.getString(idKey), json); + } + // 遍历结果集 + for (int j = 0; j < list.size(); j++) { + // 单条记录 + SoMap aVal = (SoMap) list.get(j); + // 在hash中取出key为单条记录中pid的值 + SoMap hashVp = (SoMap) hash.get(aVal.get(parentIdKey, "").toString()); + // 如果记录的pid存在,则说明它有父节点,将她添加到孩子节点的集合中 + if (hashVp != null) { + // 检查是否有child属性,有则添加,没有则新建 + if (hashVp.get(childListKey) != null) { + @SuppressWarnings("unchecked") + List ch = (List) hashVp.get(childListKey); + ch.add(aVal); + hashVp.put(childListKey, ch); + } else { + List ch = new ArrayList(); + ch.add(aVal); + hashVp.put(childListKey, ch); + } + } else { + newTreeList.add(aVal); + } + } + return newTreeList; + } + + /** + * 指定字符串的字符串下划线转大写模式 + */ + private static String wordEachBig(String str) { + String newStr = ""; + for (String s : str.split("_")) { + newStr += wordFirstBig(s); + } + return newStr; + } + + /** + * 返回下划线转小驼峰形式 + */ + private static String wordEachBigFs(String str) { + return wordFirstSmall(wordEachBig(str)); + } + + /** + * 将指定单词首字母大写 + */ + private static String wordFirstBig(String str) { + return str.substring(0, 1).toUpperCase() + str.substring(1, str.length()); + } + + /** + * 将指定单词首字母小写 + */ + private static String wordFirstSmall(String str) { + return str.substring(0, 1).toLowerCase() + str.substring(1, str.length()); + } + + /** + * 下划线转中划线 + */ + private static String wordEachKebabCase(String str) { + return str.replaceAll("_", "-"); + } + + /** + * 驼峰转下划线 + */ + private static String wordHumpToLine(String str) { + return str.replaceAll("[A-Z]", "_$0").toLowerCase(); + } + +} diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/resources/application.properties b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/resources/application.properties deleted file mode 100644 index feb2b60..0000000 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/integration-tests/src/test/resources/application.properties +++ /dev/null @@ -1,6 +0,0 @@ -quarkus.application.name=sa-token-redis-jackson-test -quarkus.redisson.single-server-config.address=redis://127.0.0.1:63790 -quarkus.redisson.single-server-config.database=0 -quarkus.redisson.codec=org.redisson.codec.JsonJacksonCodec -quarkus.redis.devservices.enabled=true -quarkus.redis.devservices.port=63790 diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaSessionForJacksonCustomized.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaSessionForJacksonCustomized.java similarity index 91% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaSessionForJacksonCustomized.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaSessionForJacksonCustomized.java index c577c6a..c4345d9 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaSessionForJacksonCustomized.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaSessionForJacksonCustomized.java @@ -1,4 +1,4 @@ -package io.quarkiverse.satoken.dao.redis.jackson; +package io.quarkiverse.satoken.dao.redis.jackson.core; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaTokenDaoRedisJackson.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaTokenDaoRedisJackson.java similarity index 88% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaTokenDaoRedisJackson.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaTokenDaoRedisJackson.java index ef8ade4..d3cae4e 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaTokenDaoRedisJackson.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaTokenDaoRedisJackson.java @@ -1,7 +1,8 @@ -package io.quarkiverse.satoken.dao.redis.jackson; +package io.quarkiverse.satoken.dao.redis.jackson.core; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; @@ -10,51 +11,47 @@ import org.redisson.api.RBucket; import org.redisson.api.RedissonClient; - -import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import cn.dev33.satoken.dao.SaTokenDao; import cn.dev33.satoken.strategy.SaStrategy; import cn.dev33.satoken.util.SaFoxUtil; +import io.quarkus.arc.DefaultBean; /** * Sa-Token持久层接口 [Redis版] (使用 jackson 序列化方式) * * @author kong */ +@DefaultBean @ApplicationScoped public class SaTokenDaoRedisJackson implements SaTokenDao { + private static final Logger LOG = LoggerFactory.getLogger(SaTokenDaoRedisJackson.class); - /** - * ObjectMapper对象 (以public作用域暴露出此对象,方便开发者二次更改配置) - */ - @Inject - public ObjectMapper objectMapper; - - @Inject - public RedissonClient redissonClient; + private RedissonClient redissonClient; /** * 标记:是否已初始化成功 */ public boolean isInit; + @Inject @PostConstruct - public void init() { + public void init( RedissonClient redissonClient) { + this.redissonClient = redissonClient; // 指定相应的序列化方案 // 通过反射获取Mapper对象, 增加一些配置, 增强兼容性 try { - // 重写Session生成策略 SaStrategy.me.createSession = (sessionId) -> new SaSessionForJacksonCustomized(sessionId); } catch (Exception e) { - System.err.println(e.getMessage()); + LOG.error("sa token redis jackson init failed", e); } - - // 开始初始化相关组件 - if (this.isInit == false) { + if (Objects.nonNull(this.redissonClient)&&Objects.nonNull(SaStrategy.me.createSession)){ this.isInit = true; } + } /** @@ -109,7 +106,8 @@ public void delete(String key) { */ @Override public long getTimeout(String key) { - return redissonClient.getBucket(key).remainTimeToLive() / 1000; + long time = redissonClient.getBucket(key).remainTimeToLive(); + return time > 0 ? time / 1000 : time; } /** diff --git a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaTokenJacksonModuleCustomizer.java b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaTokenJacksonModuleCustomizer.java similarity index 91% rename from quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaTokenJacksonModuleCustomizer.java rename to quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaTokenJacksonModuleCustomizer.java index 0b4f2b0..e124f41 100644 --- a/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/SaTokenJacksonModuleCustomizer.java +++ b/quarkus-satoken-plugins/quarkus-satoken-dao-redis-jackson/runtime/src/main/java/io/quarkiverse/satoken/dao/redis/jackson/core/SaTokenJacksonModuleCustomizer.java @@ -1,4 +1,4 @@ -package io.quarkiverse.satoken.dao.redis.jackson; +package io.quarkiverse.satoken.dao.redis.jackson.core; import java.time.LocalDate; import java.time.LocalDateTime; @@ -12,7 +12,6 @@ import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; @@ -44,12 +43,16 @@ public void customize(ObjectMapper objectMapper) { // 配置[忽略未知字段] objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - // 空对象可序列化 - objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + // // 空对象可序列化 + // objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); // null属性不序列化 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + /** + * 容错配置 start + */ + // 能解析注释符 objectMapper.enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature()); @@ -59,6 +62,10 @@ public void customize(ObjectMapper objectMapper) { // 解析单引号 objectMapper.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES); + /** + * 容错配置 end + */ + // 配置[时间类型转换] JavaTimeModule timeModule = new JavaTimeModule(); // LocalDateTime序列化与反序列化