diff --git a/sa-token-demo/sa-token-demo-case/.gitignore b/sa-token-demo/sa-token-demo-case/.gitignore
new file mode 100644
index 00000000..99a6e767
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/.gitignore
@@ -0,0 +1,12 @@
+target/
+
+node_modules/
+bin/
+.settings/
+unpackage/
+.classpath
+.project
+
+.idea/
+
+.factorypath
\ No newline at end of file
diff --git a/sa-token-demo/sa-token-demo-case/pom.xml b/sa-token-demo/sa-token-demo-case/pom.xml
new file mode 100644
index 00000000..f3f4829b
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/pom.xml
@@ -0,0 +1,72 @@
+
+ 4.0.0
+ cn.dev33
+ sa-token-demo-case
+ 0.0.1-SNAPSHOT
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.12
+
+
+
+
+
+
+ 1.31.0
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+
+ cn.dev33
+ sa-token-spring-boot-starter
+ ${sa-token-version}
+
+
+
+
+
+
+
+ cn.dev33
+ sa-token-dao-redis-jackson
+ ${sa-token-version}
+
+
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/SaTokenCaseApplication.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/SaTokenCaseApplication.java
new file mode 100644
index 00000000..5261af0d
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/SaTokenCaseApplication.java
@@ -0,0 +1,21 @@
+package com.pj;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import cn.dev33.satoken.SaManager;
+
+/**
+ * Sa-Token 示例
+ * @author kong
+ *
+ */
+@SpringBootApplication
+public class SaTokenCaseApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SaTokenCaseApplication.class, args);
+ System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/LoginAuthController.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/LoginAuthController.java
new file mode 100644
index 00000000..e51be2a4
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/cases/LoginAuthController.java
@@ -0,0 +1,49 @@
+package com.pj.cases;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.dev33.satoken.util.SaResult;
+
+/**
+ * 登录认证示例
+ *
+ * @author kong
+ *
+ */
+@RestController
+@RequestMapping("/acc/")
+public class LoginAuthController {
+
+ // 测试登录 ---- http://localhost:8081/acc/doLogin?name=zhang&pwd=123456
+ @RequestMapping("doLogin")
+ public SaResult doLogin(String name, String pwd) {
+ // 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
+ if("zhang".equals(name) && "123456".equals(pwd)) {
+ StpUtil.login(10001);
+ return SaResult.ok("登录成功");
+ }
+ return SaResult.error("登录失败");
+ }
+
+ // 查询登录状态 ---- http://localhost:8081/acc/isLogin
+ @RequestMapping("isLogin")
+ public SaResult isLogin() {
+ return SaResult.ok("是否登录:" + StpUtil.isLogin());
+ }
+
+ // 查询 Token 信息 ---- http://localhost:8081/acc/tokenInfo
+ @RequestMapping("tokenInfo")
+ public SaResult tokenInfo() {
+ return SaResult.data(StpUtil.getTokenInfo());
+ }
+
+ // 测试注销 ---- http://localhost:8081/acc/logout
+ @RequestMapping("logout")
+ public SaResult logout() {
+ StpUtil.logout();
+ return SaResult.ok();
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/current/GlobalException.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/current/GlobalException.java
new file mode 100644
index 00000000..3c75d793
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/current/GlobalException.java
@@ -0,0 +1,25 @@
+package com.pj.current;
+
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import cn.dev33.satoken.util.SaResult;
+
+/**
+ * 全局异常处理
+ */
+@RestControllerAdvice
+public class GlobalException {
+
+ // 全局异常拦截
+ @ExceptionHandler
+ public SaResult handlerException(Exception e) {
+
+ // 打印堆栈,以供调试
+ e.printStackTrace();
+
+ // 返回给前端
+ return SaResult.error(e.getMessage());
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/current/NotFoundHandle.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/current/NotFoundHandle.java
new file mode 100644
index 00000000..1e82e95a
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/current/NotFoundHandle.java
@@ -0,0 +1,27 @@
+package com.pj.current;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.boot.web.servlet.error.ErrorController;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import cn.dev33.satoken.util.SaResult;
+
+/**
+ * 处理 404
+ * @author kong
+ */
+@RestController
+public class NotFoundHandle implements ErrorController {
+
+ @RequestMapping("/error")
+ public Object error(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ response.setStatus(200);
+ return SaResult.get(404, "not found", null);
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
new file mode 100644
index 00000000..1f8ad801
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/SaTokenConfigure.java
@@ -0,0 +1,84 @@
+package com.pj.satoken;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import cn.dev33.satoken.context.SaHolder;
+import cn.dev33.satoken.filter.SaServletFilter;
+import cn.dev33.satoken.interceptor.SaInterceptor;
+import cn.dev33.satoken.strategy.SaStrategy;
+import cn.dev33.satoken.util.SaResult;
+
+
+/**
+ * [Sa-Token 权限认证] 配置类
+ * @author kong
+ *
+ */
+@Configuration
+public class SaTokenConfigure implements WebMvcConfigurer {
+
+ /**
+ * 注册 Sa-Token 拦截器打开注解鉴权功能
+ */
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 注册 Sa-Token 拦截器打开注解鉴权功能
+ registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
+ }
+
+ /**
+ * 注册 [Sa-Token 全局过滤器]
+ */
+ @Bean
+ public SaServletFilter getSaServletFilter() {
+ return new SaServletFilter()
+
+ // 指定 [拦截路由] 与 [放行路由]
+ .addInclude("/**")// .addExclude("/favicon.ico")
+
+ // 认证函数: 每次请求执行
+ .setAuth(obj -> {
+ // System.out.println("---------- sa全局认证 " + SaHolder.getRequest().getRequestPath());
+
+ })
+
+ // 异常处理函数:每次认证函数发生异常时执行此函数
+ .setError(e -> {
+ System.out.println("---------- sa全局异常 ");
+ return SaResult.error(e.getMessage());
+ })
+
+ // 前置函数:在每次认证函数之前执行
+ .setBeforeAuth(r -> {
+ // ---------- 设置一些安全响应头 ----------
+ SaHolder.getResponse()
+ // 服务器名称
+ .setServer("sa-server")
+ // 是否可以在iframe显示视图: DENY=不可以 | SAMEORIGIN=同域下可以 | ALLOW-FROM uri=指定域名下可以
+ .setHeader("X-Frame-Options", "SAMEORIGIN")
+ // 是否启用浏览器默认XSS防护: 0=禁用 | 1=启用 | 1; mode=block 启用, 并在检查到XSS攻击时,停止渲染页面
+ .setHeader("X-XSS-Protection", "1; mode=block")
+ // 禁用浏览器内容嗅探
+ .setHeader("X-Content-Type-Options", "nosniff")
+ ;
+ })
+ ;
+ }
+
+ /**
+ * 重写 Sa-Token 框架内部算法策略
+ */
+ @Autowired
+ public void rewriteSaStrategy() {
+ // 重写Sa-Token的注解处理器,增加注解合并功能
+ SaStrategy.me.getAnnotation = (element, annotationClass) -> {
+ return AnnotatedElementUtils.getMergedAnnotation(element, annotationClass);
+ };
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java
new file mode 100644
index 00000000..b6cc79f7
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/java/com/pj/satoken/StpInterfaceImpl.java
@@ -0,0 +1,44 @@
+package com.pj.satoken;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.stereotype.Component;
+
+import cn.dev33.satoken.stp.StpInterface;
+
+/**
+ * 自定义权限验证接口扩展
+ */
+@Component // 打开此注解,保证此类被springboot扫描,即可完成sa-token的自定义权限验证扩展
+public class StpInterfaceImpl implements StpInterface {
+
+ /**
+ * 返回一个账号所拥有的权限码集合
+ */
+ @Override
+ public List getPermissionList(Object loginId, String loginType) {
+ // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限
+ List list = new ArrayList();
+ list.add("101");
+ list.add("user-add");
+ list.add("user-delete");
+ list.add("user-update");
+ list.add("user-get");
+ list.add("article-get");
+ return list;
+ }
+
+ /**
+ * 返回一个账号所拥有的角色标识集合
+ */
+ @Override
+ public List getRoleList(Object loginId, String loginType) {
+ // 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色
+ List list = new ArrayList();
+ list.add("admin");
+ list.add("super-admin");
+ return list;
+ }
+
+}
diff --git a/sa-token-demo/sa-token-demo-case/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-case/src/main/resources/application.yml
new file mode 100644
index 00000000..cb63067a
--- /dev/null
+++ b/sa-token-demo/sa-token-demo-case/src/main/resources/application.yml
@@ -0,0 +1,49 @@
+# 端口
+server:
+ port: 8081
+
+# sa-token配置
+sa-token:
+ # token名称 (同时也是cookie名称)
+ token-name: satoken
+ # token有效期,单位s 默认30天, -1代表永不过期
+ timeout: 2592000
+ # token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
+ activity-timeout: -1
+ # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+ is-concurrent: true
+ # 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
+ is-share: true
+ # token风格
+ token-style: uuid
+ # 是否输出操作日志
+ is-log: false
+
+spring:
+ # redis配置
+ redis:
+ # Redis数据库索引(默认为0)
+ database: 0
+ # Redis服务器地址
+ host: 127.0.0.1
+ # Redis服务器连接端口
+ port: 6379
+ # Redis服务器连接密码(默认为空)
+ password:
+ # 连接超时时间
+ timeout: 10s
+ lettuce:
+ pool:
+ # 连接池最大连接数
+ max-active: 200
+ # 连接池最大阻塞等待时间(使用负值表示没有限制)
+ max-wait: -1ms
+ # 连接池中的最大空闲连接
+ max-idle: 10
+ # 连接池中的最小空闲连接
+ min-idle: 0
+
+
+
+
+
\ No newline at end of file