mirror of
https://gitee.com/dromara/sa-token.git
synced 2025-10-07 23:24:24 +08:00
refactor: 重构所有 starter 组件的 SaTokenContext 上下文读写策略
This commit is contained in:
@@ -17,4 +17,5 @@ public class SaTokenDemoApp {
|
||||
Solon.start(SaTokenDemoApp.class, args);
|
||||
System.out.println("\n启动成功:Sa-Token配置如下:" + SaManager.getConfig());
|
||||
}
|
||||
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.servlet.util.SaTokenContextUtil;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -18,7 +19,9 @@ public class TestController {
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public SaResult test() {
|
||||
System.out.println("------------进来了");
|
||||
System.out.println("------------进来了");
|
||||
System.out.println(SpringMVCUtil.getRequest());
|
||||
System.out.println(SaTokenContextUtil.getRequest());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
|
@@ -73,8 +73,12 @@
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</dependencies>
|
||||
|
||||
<!-- 构建配置 -->
|
||||
<build>
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.servlet.util.SaTokenContextUtil;
|
||||
import cn.dev33.satoken.spring.SpringMVCUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -17,9 +18,12 @@ public class Test2Controller {
|
||||
@RequestMapping("/test")
|
||||
public SaResult test2() {
|
||||
|
||||
StpUtil.login(30003);
|
||||
System.out.println(StpUtil.getSession().timeout());
|
||||
System.out.println(StpUtil.getStpLogic().getTokenSession(false));
|
||||
System.out.println(SpringMVCUtil.getRequest());
|
||||
System.out.println(SaTokenContextUtil.getRequest());
|
||||
|
||||
// StpUtil.login(30003);
|
||||
// System.out.println(StpUtil.getSession().timeout());
|
||||
// System.out.println(StpUtil.getStpLogic().getTokenSession(false));
|
||||
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
@@ -26,10 +26,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc/ -->
|
||||
<dependency>
|
||||
@@ -37,21 +33,14 @@
|
||||
<artifactId>sa-token-reactor-spring-boot3-starter</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
|
||||
<!-- Sa-Token 整合 Redis -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis</artifactId>
|
||||
<artifactId>sa-token-redis-template</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency> -->
|
||||
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
@@ -1,12 +1,10 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
|
||||
|
||||
/**
|
||||
* [Sa-Token 权限认证] 配置类
|
||||
* @author click33
|
||||
@@ -33,7 +31,7 @@ public class SaTokenConfigure {
|
||||
// 指定[异常处理函数]:每次[认证函数]发生异常时执行此函数
|
||||
.setError(e -> {
|
||||
System.out.println("---------- sa全局异常 ");
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
;
|
||||
}
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -8,10 +11,6 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@Configuration
|
||||
public class DefineRoutes {
|
||||
|
||||
@@ -23,12 +22,11 @@ public class DefineRoutes {
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> getRoutes() {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/fun"), req -> {
|
||||
// 测试打印
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
// 返回结果
|
||||
AjaxJson aj = AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).syncBody(aj);
|
||||
return SaReactorSyncHolder.setContext(req.exchange(), () -> {
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
SaResult res = SaResult.data(StpUtil.getTokenInfo());
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).syncBody(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,52 +1,19 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
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 org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@ControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
@RestControllerAdvice
|
||||
public class GlobalException {
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
} else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
} else if(e instanceof DisableServiceException) { // 如果是被封禁异常
|
||||
DisableServiceException ee = (DisableServiceException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,17 +1,19 @@
|
||||
package com.pj.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* 测试专用Controller
|
||||
* @author click33
|
||||
@@ -21,60 +23,93 @@ import reactor.core.publisher.Mono;
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
// 测试登录接口 [同步模式], 浏览器访问: http://localhost:8081/test/login
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
// 登录测试:Controller 里调用 Sa-Token API --- http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
StpUtil.login(id);
|
||||
return AjaxJson.getSuccess("登录成功");
|
||||
}
|
||||
|
||||
// API测试 [同步模式], 浏览器访问: http://localhost:8081/test/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public AjaxJson isLogin() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
}
|
||||
|
||||
// API测试 [异步模式], 浏览器访问: http://localhost:8081/test/isLogin2
|
||||
@RequestMapping("isLogin2")
|
||||
public Mono<AjaxJson> isLogin2() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
AjaxJson aj = AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
return Mono.just(aj);
|
||||
}
|
||||
|
||||
// API测试 [异步模式, 同一线程], 浏览器访问: http://localhost:8081/test/isLogin3
|
||||
@RequestMapping("isLogin3")
|
||||
public Mono<AjaxJson> isLogin3() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
// 异步方式
|
||||
return SaReactorHolder.getContext().map(e -> {
|
||||
System.out.println("当前会话是否登录2:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
public Mono<SaResult> login(@RequestParam(defaultValue="10001") String id) {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
StpUtil.login(id);
|
||||
return SaResult.ok("登录成功");
|
||||
});
|
||||
}
|
||||
|
||||
// API测试 [异步模式, 不同线程], 浏览器访问: http://localhost:8081/test/isLogin4
|
||||
// API测试:手动设置上下文、try-finally 形式 --- http://localhost:8081/test/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin(ServerWebExchange exchange) {
|
||||
try {
|
||||
SaReactorSyncHolder.setContext(exchange);
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
} finally {
|
||||
SaReactorSyncHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
// API测试:手动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin2
|
||||
@RequestMapping("isLogin2")
|
||||
public SaResult isLogin2(ServerWebExchange exchange) {
|
||||
SaResult res = SaReactorSyncHolder.setContext(exchange, ()->{
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
return SaResult.data(res);
|
||||
}
|
||||
|
||||
// API测试:自动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin3
|
||||
@RequestMapping("isLogin3")
|
||||
public Mono<SaResult> isLogin3() {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
userService.isLogin();
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:自动设置上下文、调用 userService Mono 方法 --- http://localhost:8081/test/isLogin4
|
||||
@RequestMapping("isLogin4")
|
||||
public Mono<AjaxJson> isLogin4() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("线程id-----" + Thread.currentThread().getId());
|
||||
return Mono.delay(Duration.ofSeconds(1)).flatMap(r->{
|
||||
return SaReactorHolder.getContext().map(rr->{
|
||||
System.out.println("线程id---内--" + Thread.currentThread().getId());
|
||||
System.out.println("当前会话是否登录2:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
public Mono<SaResult> isLogin4() {
|
||||
return userService.findUserIdByNamePwd("ZhangSan", "123456").flatMap(userId -> {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
StpUtil.login(userId);
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// API测试:切换线程、复杂嵌套调用 --- http://localhost:8081/test/isLogin5
|
||||
@RequestMapping("isLogin5")
|
||||
public Mono<SaResult> isLogin5() {
|
||||
System.out.println("线程id-----" + Thread.currentThread().getId());
|
||||
// 要点:在流里调用 Sa-Token API 之前,必须用 SaReactorHolder.sync( () -> {} ) 进行包裹
|
||||
return Mono.delay(Duration.ofSeconds(1))
|
||||
.doOnNext(r-> System.out.println("线程id-----" + Thread.currentThread().getId()))
|
||||
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
|
||||
.map(r-> userService.findUserIdByNamePwd("ZhangSan", "123456"))
|
||||
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
|
||||
.flatMap(isLogin -> {
|
||||
System.out.println("是否登录 " + isLogin);
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("是否登录 " + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:使用上下文无关的API --- http://localhost:8081/test/isLogin6
|
||||
@RequestMapping("isLogin6")
|
||||
public SaResult isLogin6(@CookieValue("satoken") String satoken) {
|
||||
System.out.println("token 为:" + satoken);
|
||||
System.out.println("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
System.out.println("线程id-----------Controller--" + Thread.currentThread().getId() + "\t\t");
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
public SaResult test() {
|
||||
System.out.println("线程id------- " + Thread.currentThread().getId());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 模拟 Service 方法
|
||||
* @author click33
|
||||
* @since 2025/4/6
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
public boolean isLogin() {
|
||||
System.out.println("UserService 里调用 API 测试,是否登录:" + StpUtil.isLogin());
|
||||
return StpUtil.isLogin();
|
||||
}
|
||||
|
||||
public Mono<Long> findUserIdByNamePwd(String name, String pwd) {
|
||||
// ...
|
||||
return Mono.just(10001L);
|
||||
}
|
||||
|
||||
}
|
@@ -1,154 +0,0 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
// 转JSON格式输出
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@@ -10,7 +10,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.5.14</version>
|
||||
<version>2.7.18</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -26,11 +26,7 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Sa-Token 权限认证(Reactor响应式集成), 在线文档:https://sa-token.cc/ -->
|
||||
<dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
@@ -38,20 +34,13 @@
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sa-token整合redis (使用jdk默认序列化方式) -->
|
||||
<!-- Sa-Token 整合 Redis -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis</artifactId>
|
||||
<artifactId>sa-token-redis-template</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency> -->
|
||||
|
||||
<!-- sa-token整合redis (使用jackson序列化方式) -->
|
||||
<!-- <dependency>
|
||||
<groupId>cn.dev33</groupId>
|
||||
<artifactId>sa-token-redis-jackson</artifactId>
|
||||
<version>${sa-token.version}</version>
|
||||
</dependency> -->
|
||||
|
||||
|
||||
<!-- 提供redis连接池 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
@@ -1,10 +1,9 @@
|
||||
package com.pj;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import cn.dev33.satoken.SaManager;
|
||||
|
||||
/**
|
||||
* Sa-Token整合webflux 示例
|
||||
* @author click33
|
||||
|
@@ -1,7 +1,7 @@
|
||||
package com.pj.satoken;
|
||||
|
||||
import cn.dev33.satoken.reactor.filter.SaReactorFilter;
|
||||
import com.pj.util.AjaxJson;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class SaTokenConfigure {
|
||||
.setError(e -> {
|
||||
System.out.println("---------- sa全局异常 ");
|
||||
e.printStackTrace();
|
||||
return AjaxJson.getError(e.getMessage());
|
||||
return SaResult.error(e.getMessage());
|
||||
})
|
||||
;
|
||||
}
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -8,10 +11,6 @@ import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
@Configuration
|
||||
public class DefineRoutes {
|
||||
|
||||
@@ -23,12 +22,11 @@ public class DefineRoutes {
|
||||
@Bean
|
||||
public RouterFunction<ServerResponse> getRoutes() {
|
||||
return RouterFunctions.route(RequestPredicates.GET("/fun"), req -> {
|
||||
// 测试打印
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
|
||||
// 返回结果
|
||||
AjaxJson aj = AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).syncBody(aj);
|
||||
return SaReactorSyncHolder.setContext(req.exchange(), () -> {
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
SaResult res = SaResult.data(StpUtil.getTokenInfo());
|
||||
return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON_UTF8).syncBody(res);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -1,52 +1,19 @@
|
||||
package com.pj.test;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.pj.util.AjaxJson;
|
||||
|
||||
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 org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理
|
||||
*/
|
||||
@ControllerAdvice // 可指定包前缀,比如:(basePackages = "com.pj.admin")
|
||||
@RestControllerAdvice
|
||||
public class GlobalException {
|
||||
|
||||
// 全局异常拦截(拦截项目中的所有异常)
|
||||
@ResponseBody
|
||||
@ExceptionHandler
|
||||
public AjaxJson handlerException(Exception e)
|
||||
throws Exception {
|
||||
|
||||
// 打印堆栈,以供调试
|
||||
System.out.println("全局异常---------------");
|
||||
e.printStackTrace();
|
||||
|
||||
// 不同异常返回不同状态码
|
||||
AjaxJson aj = null;
|
||||
if (e instanceof NotLoginException) { // 如果是未登录异常
|
||||
NotLoginException ee = (NotLoginException) e;
|
||||
aj = AjaxJson.getNotLogin().setMsg(ee.getMessage());
|
||||
} else if(e instanceof NotRoleException) { // 如果是角色异常
|
||||
NotRoleException ee = (NotRoleException) e;
|
||||
aj = AjaxJson.getNotJur("无此角色:" + ee.getRole());
|
||||
} else if(e instanceof NotPermissionException) { // 如果是权限异常
|
||||
NotPermissionException ee = (NotPermissionException) e;
|
||||
aj = AjaxJson.getNotJur("无此权限:" + ee.getPermission());
|
||||
} else if(e instanceof DisableServiceException) { // 如果是被封禁异常
|
||||
DisableServiceException ee = (DisableServiceException) e;
|
||||
aj = AjaxJson.getNotJur("账号被封禁:" + ee.getDisableTime() + "秒后解封");
|
||||
} else { // 普通异常, 输出:500 + 异常信息
|
||||
aj = AjaxJson.getError(e.getMessage());
|
||||
}
|
||||
|
||||
// 返回给前端
|
||||
return aj;
|
||||
public SaResult handlerException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return SaResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,11 +1,15 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.reactor.context.SaReactorHolder;
|
||||
import cn.dev33.satoken.reactor.context.SaReactorSyncHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.pj.util.AjaxJson;
|
||||
import cn.dev33.satoken.util.SaResult;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -19,60 +23,93 @@ import java.time.Duration;
|
||||
@RequestMapping("/test/")
|
||||
public class TestController {
|
||||
|
||||
// 测试登录接口 [同步模式], 浏览器访问: http://localhost:8081/test/login
|
||||
@Autowired
|
||||
UserService userService;
|
||||
|
||||
// 登录测试:Controller 里调用 Sa-Token API --- http://localhost:8081/test/login
|
||||
@RequestMapping("login")
|
||||
public AjaxJson login(@RequestParam(defaultValue="10001") String id) {
|
||||
StpUtil.login(id);
|
||||
return AjaxJson.getSuccess("登录成功");
|
||||
}
|
||||
|
||||
// API测试 [同步模式], 浏览器访问: http://localhost:8081/test/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public AjaxJson isLogin() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
}
|
||||
|
||||
// API测试 [异步模式], 浏览器访问: http://localhost:8081/test/isLogin2
|
||||
@RequestMapping("isLogin2")
|
||||
public Mono<AjaxJson> isLogin2() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
AjaxJson aj = AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
return Mono.just(aj);
|
||||
}
|
||||
|
||||
// API测试 [异步模式, 同一线程], 浏览器访问: http://localhost:8081/test/isLogin3
|
||||
@RequestMapping("isLogin3")
|
||||
public Mono<AjaxJson> isLogin3() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
// 异步方式
|
||||
return SaReactorHolder.getContext().map(e -> {
|
||||
System.out.println("当前会话是否登录2:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
public Mono<SaResult> login(@RequestParam(defaultValue="10001") String id) {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
StpUtil.login(id);
|
||||
return SaResult.ok("登录成功");
|
||||
});
|
||||
}
|
||||
|
||||
// API测试 [异步模式, 不同线程], 浏览器访问: http://localhost:8081/test/isLogin4
|
||||
// API测试:手动设置上下文、try-finally 形式 --- http://localhost:8081/test/isLogin
|
||||
@RequestMapping("isLogin")
|
||||
public SaResult isLogin(ServerWebExchange exchange) {
|
||||
try {
|
||||
SaReactorSyncHolder.setContext(exchange);
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
} finally {
|
||||
SaReactorSyncHolder.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
// API测试:手动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin2
|
||||
@RequestMapping("isLogin2")
|
||||
public SaResult isLogin2(ServerWebExchange exchange) {
|
||||
SaResult res = SaReactorSyncHolder.setContext(exchange, ()->{
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
return SaResult.data(res);
|
||||
}
|
||||
|
||||
// API测试:自动设置上下文、lambda 表达式形式 --- http://localhost:8081/test/isLogin3
|
||||
@RequestMapping("isLogin3")
|
||||
public Mono<SaResult> isLogin3() {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("是否登录:" + StpUtil.isLogin());
|
||||
userService.isLogin();
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:自动设置上下文、调用 userService Mono 方法 --- http://localhost:8081/test/isLogin4
|
||||
@RequestMapping("isLogin4")
|
||||
public Mono<AjaxJson> isLogin4() {
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
System.out.println("线程id-----" + Thread.currentThread().getId());
|
||||
return Mono.delay(Duration.ofSeconds(1)).flatMap(r->{
|
||||
return SaReactorHolder.getContext().map(rr->{
|
||||
System.out.println("线程id---内--" + Thread.currentThread().getId());
|
||||
System.out.println("当前会话是否登录2:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
public Mono<SaResult> isLogin4() {
|
||||
return userService.findUserIdByNamePwd("ZhangSan", "123456").flatMap(userId -> {
|
||||
return SaReactorHolder.sync(() -> {
|
||||
StpUtil.login(userId);
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// API测试:切换线程、复杂嵌套调用 --- http://localhost:8081/test/isLogin5
|
||||
@RequestMapping("isLogin5")
|
||||
public Mono<SaResult> isLogin5() {
|
||||
System.out.println("线程id-----" + Thread.currentThread().getId());
|
||||
// 要点:在流里调用 Sa-Token API 之前,必须用 SaReactorHolder.sync( () -> {} ) 进行包裹
|
||||
return Mono.delay(Duration.ofSeconds(1))
|
||||
.doOnNext(r-> System.out.println("线程id-----" + Thread.currentThread().getId()))
|
||||
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
|
||||
.map(r-> userService.findUserIdByNamePwd("ZhangSan", "123456"))
|
||||
.map(r-> SaReactorHolder.sync( () -> userService.isLogin() ))
|
||||
.flatMap(isLogin -> {
|
||||
System.out.println("是否登录 " + isLogin);
|
||||
return SaReactorHolder.sync(() -> {
|
||||
System.out.println("是否登录 " + StpUtil.isLogin());
|
||||
return SaResult.data(StpUtil.getTokenInfo());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// API测试:使用上下文无关的API --- http://localhost:8081/test/isLogin6
|
||||
@RequestMapping("isLogin6")
|
||||
public SaResult isLogin6(@CookieValue("satoken") String satoken) {
|
||||
System.out.println("token 为:" + satoken);
|
||||
System.out.println("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
return SaResult.ok("登录人:" + StpUtil.getLoginIdByToken(satoken));
|
||||
}
|
||||
|
||||
// 测试 浏览器访问: http://localhost:8081/test/test
|
||||
@RequestMapping("test")
|
||||
public AjaxJson test() {
|
||||
System.out.println("线程id-----------Controller--" + Thread.currentThread().getId() + "\t\t");
|
||||
System.out.println("当前会话是否登录:" + StpUtil.isLogin());
|
||||
return AjaxJson.getSuccessData(StpUtil.getTokenInfo());
|
||||
public SaResult test() {
|
||||
System.out.println("线程id------- " + Thread.currentThread().getId());
|
||||
return SaResult.ok();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,25 @@
|
||||
package com.pj.test;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 模拟 Service 方法
|
||||
* @author click33
|
||||
* @since 2025/4/6
|
||||
*/
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
public boolean isLogin() {
|
||||
System.out.println("UserService 里调用 API 测试,是否登录:" + StpUtil.isLogin());
|
||||
return StpUtil.isLogin();
|
||||
}
|
||||
|
||||
public Mono<Long> findUserIdByNamePwd(String name, String pwd) {
|
||||
// ...
|
||||
return Mono.just(10001L);
|
||||
}
|
||||
|
||||
}
|
@@ -1,154 +0,0 @@
|
||||
package com.pj.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
|
||||
/**
|
||||
* ajax请求返回Json格式数据的封装
|
||||
*/
|
||||
public class AjaxJson implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L; // 序列化版本号
|
||||
|
||||
public static final int CODE_SUCCESS = 200; // 成功状态码
|
||||
public static final int CODE_ERROR = 500; // 错误状态码
|
||||
public static final int CODE_WARNING = 501; // 警告状态码
|
||||
public static final int CODE_NOT_JUR = 403; // 无权限状态码
|
||||
public static final int CODE_NOT_LOGIN = 401; // 未登录状态码
|
||||
public static final int CODE_INVALID_REQUEST = 400; // 无效请求状态码
|
||||
|
||||
public int code; // 状态码
|
||||
public String msg; // 描述信息
|
||||
public Object data; // 携带对象
|
||||
public Long dataCount; // 数据总数,用于分页
|
||||
|
||||
/**
|
||||
* 返回code
|
||||
* @return
|
||||
*/
|
||||
public int getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给msg赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
return this;
|
||||
}
|
||||
public String getMsg() {
|
||||
return this.msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给data赋值,连缀风格
|
||||
*/
|
||||
public AjaxJson setData(Object data) {
|
||||
this.data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将data还原为指定类型并返回
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(Class<T> cs) {
|
||||
return (T) data;
|
||||
}
|
||||
|
||||
// ============================ 构建 ==================================
|
||||
|
||||
public AjaxJson(int code, String msg, Object data, Long dataCount) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.dataCount = dataCount;
|
||||
}
|
||||
|
||||
// 返回成功
|
||||
public static AjaxJson getSuccess() {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, null, null);
|
||||
}
|
||||
public static AjaxJson getSuccess(String msg, Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, msg, data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessData(Object data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
public static AjaxJson getSuccessArray(Object... data) {
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, null);
|
||||
}
|
||||
|
||||
// 返回失败
|
||||
public static AjaxJson getError() {
|
||||
return new AjaxJson(CODE_ERROR, "error", null, null);
|
||||
}
|
||||
public static AjaxJson getError(String msg) {
|
||||
return new AjaxJson(CODE_ERROR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回警告
|
||||
public static AjaxJson getWarning() {
|
||||
return new AjaxJson(CODE_ERROR, "warning", null, null);
|
||||
}
|
||||
public static AjaxJson getWarning(String msg) {
|
||||
return new AjaxJson(CODE_WARNING, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回未登录
|
||||
public static AjaxJson getNotLogin() {
|
||||
return new AjaxJson(CODE_NOT_LOGIN, "未登录,请登录后再次访问", null, null);
|
||||
}
|
||||
|
||||
// 返回没有权限的
|
||||
public static AjaxJson getNotJur(String msg) {
|
||||
return new AjaxJson(CODE_NOT_JUR, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回一个自定义状态码的
|
||||
public static AjaxJson get(int code, String msg){
|
||||
return new AjaxJson(code, msg, null, null);
|
||||
}
|
||||
|
||||
// 返回分页和数据的
|
||||
public static AjaxJson getPageData(Long dataCount, Object data){
|
||||
return new AjaxJson(CODE_SUCCESS, "ok", data, dataCount);
|
||||
}
|
||||
|
||||
// 返回,根据受影响行数的(大于0=ok,小于0=error)
|
||||
public static AjaxJson getByLine(int line){
|
||||
if(line > 0){
|
||||
return getSuccess("ok", line);
|
||||
}
|
||||
return getError("error").setData(line);
|
||||
}
|
||||
|
||||
// 返回,根据布尔值来确定最终结果的 (true=ok,false=error)
|
||||
public static AjaxJson getByBoolean(boolean b){
|
||||
return b ? getSuccess("ok") : getError("error");
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
// 转JSON格式输出
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(this);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user