整合 Sa-Token

概述

Tio-Boot 已经内了内置对 Sa-Token 支持 ,提供了一种有效的方式来处理身份验证和授权。虽然 Tio-Boot 已有内置支持,但仍需手动添加 sa-token-core 依赖。此外,若希望将 token 存储在 Redis 中,还需要添加 java-dbjedisfst 依赖。

使用 Sa-Token 实现登录功能

存储 token 到浏览器

添加依赖

首先,需要在项目的 pom.xml 文件中添加以下依赖,以便引入必要的库:

<!-- Sa-Token 核心库 -->
<dependency>
  <groupId>cn.dev33</groupId>
  <artifactId>sa-token-core</artifactId>
  <version>1.37.0</version>
</dependency>

配置 Sa-Token

接下来,需要设置 Sa-Token 的配置类。此类负责初始化 Sa-Token 相关的配置,如 token 的存储方式、有效期、cookie 配置等。

import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.tio.boot.satoken.SaTokenContextForTio;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.config.SaCookieConfig;
import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.context.SaTokenContext;
import cn.dev33.satoken.util.SaTokenConsts;

@AConfiguration
public class SaTokenConfiguration {

  @AInitialization
  public void config() {
    // 初始化 Sa-Token 上下文
    SaTokenContext saTokenContext = new SaTokenContextForTio();

    // 设置 Cookie 配置,例如启用 HttpOnly 属性
    SaCookieConfig saCookieConfig = new SaCookieConfig();
    saCookieConfig.setHttpOnly(true);

    // 初始化和配置 Sa-Token 主配置
    SaTokenConfig saTokenConfig = new SaTokenConfig();
    saTokenConfig.setTokenStyle(SaTokenConsts.TOKEN_STYLE_SIMPLE_UUID);
    saTokenConfig.setActiveTimeout(50 * 60); // 设置活动超时时间为 50 分钟

    saTokenConfig.setIsShare(false);
    saTokenConfig.setTokenName("token"); // 设置 token 的名称
    saTokenConfig.setIsWriteHeader(true); // 将 token 写入响应头
    saTokenConfig.setIsReadHeader(true); // 从请求头中读取 token

    saTokenConfig.setCookie(saCookieConfig);

    // 应用配置到 Sa-Token 管理器
    SaManager.setConfig(saTokenConfig);
    SaManager.setSaTokenContext(saTokenContext);
  }
}

添加拦截器

为了对请求进行验证,需要设置一个拦截器,该拦截器将根据配置的规则允许或阻止请求。

// 导入必要的类和注解
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.tio.boot.http.interceptor.HttpServerInterceptorModel;
import com.litongjava.tio.boot.http.interceptor.ServerInteceptorConfigure;
import com.litongjava.tio.boot.satoken.SaTokenInterceptor;
import com.litongjava.tio.boot.server.TioBootServer;

@AConfiguration
public class InterceptorConfiguration {

  @AInitialization
  public void config() {
    // 创建 SaToken 拦截器实例
    SaTokenInterceptor saTokenInterceptor = new SaTokenInterceptor();
    HttpServerInterceptorModel model = new HttpServerInterceptorModel();
    model.setInterceptor(saTokenInterceptor);
    model.addblockeUrl("/**"); // 拦截所有路由
    model.addAlloweUrls("/register/*", "/auth/*"); // 设置例外路由

    ServerInteceptorConfigure serverInteceptorConfigure = new ServerInteceptorConfigure();
    serverInteceptorConfigure.add(model);
    // 将拦截器配置添加到 Tio 服务器
    TioBootServer.me().setServerInteceptorConfigure(serverInteceptorConfigure);
  }
}



实现登录和登出逻辑

接下来,可以在控制器中实现登录和登出的逻辑。这将使应用能够处理用户身份验证。

// 导入必要的类和注解
package com.litongjava.tio.web.hello;

import com.litongjava.tio.boot.http.TioControllerContext;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.server.annotation.RequestPath;
import com.litongjava.tio.http.server.util.Resps;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;

@RequestPath("/auth")
@Slf4j
public class AuthController {

  public Object doLogin() {
    StpUtil.login("litong");
    HttpResponse response = TioControllerContext.getResponse();
    log.info("response", response);
    return response;
  }

  public HttpResponse logout() {
    StpUtil.logout();
    SaResult ok = SaResult.ok("ok");
    HttpResponse response = TioControllerContext.getResponse();
    return Resps.json(response, ok);
  }

  public boolean validateToken() {
    try {
      StpUtil.checkLogin();
      return true;
    } catch (Exception e) {
      return false;
    }
  }
}

验证访问权限

最后,可以创建一个 IndexController 来验证用户的登录状态。如果用户没有登录,他们将无法访问此控制器的路由。

// 导入必要的类和注解
package com.litongjava.tio.web.hello;

import com.litongjava.tio.http.server.annotation.RequestPath;

@RequestPath("/")
public class IndexController {
  @RequestPath()
  public String index() {
    return "index";
  }
}

测试应用

运行应用后,可以通过以下 URL 测试登录、登出和验证 token 的功能:

  • 登录:http://localhost:8080/auth/doLogin?username=zhang&password=123456
  • 登出:http://localhost:8080/auth/logout
  • 验证 Token:http://localhost:8080/auth/validateToken
  • 访问首页:http://localhost:8080/
登录

登录成功会回返回下面的信息 请求头

token:c5edff10712b4c49b64da5b910ecdb53

or

Set-Cookie:token=c5edff10712b4c49b64da5b910ecdb53; Max-Age=2592000; Expires=Tue, 9 Apr 2024 23:07:50 -1000; Path=/; HttpOnly

上面两种方式使用任意一种方式进行验证都可以

验证

可以添加使用 cookie token=c5edff10712b4c49b64da5b910ecdb53

GET /auth/validateToken HTTP/1.1
Cookie: token=c5edff10712b4c49b64da5b910ecdb53; PHPSESSID=e11a6f19b3504fec816a3d07862341ec

也支持请求头参数 token

生成 JWT token

添加依赖

<!-- Sa-Token 整合 jwt -->
<dependency>
  <groupId>cn.dev33</groupId>
  <artifactId>sa-token-jwt</artifactId>
  <version>1.37.0</version>
</dependency>

修改 SaTokenConfiguration 添加 jwt 相关配置

saTokenConfig.setJwtSecretKey("asdasdasifhueuiwyurfewbfjsdafjk");
saTokenConfig.setTokenPrefix("Bearer");
StpLogicJwtForSimple stpLogicJwtForSimple = new StpLogicJwtForSimple();
StpUtil.setStpLogic(stpLogicJwtForSimple);

其他不变,正常登录即可

将 Token 存入 Redis

为什么要将访问 token 存入 Redis

将访问令牌(Token)存储在 Redis 中主要是出于以下原因:

  • 易于管理过期时间: 访问令牌通常有一个有效期限。Redis 提供了设置键值对过期时间的功能,这使得管理令牌过期变得简单和自动化,过期的令牌会被自动从数据库中删除,从而节省存储空间。

  • 支持分布式系统: 在微服务架构或分布式系统中,应用组件可能部署在不同的服务器或容器上。使用 Redis 存储访问令牌可以确保所有组件都能实时访问到最新的令牌信息,便于系统的扩展和维护。

  • 可伸缩性: 即便后端服务器重启,因为 token 存储到 redis 中,重启后依然可以识别前端传递过来的 token

添加依赖

<!-- JFinal 插件,用于与 Redis 交互 -->
<dependency>
  <groupId>com.litongjava</groupId>
  <artifactId>java-db</artifactId>
  <version>1.0.6</version>
</dependency>

<!-- Jedis,Redis 的 Java 客户端 -->
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>3.6.3</version>
</dependency>

<!-- FST 序列化工具,用于对象和字节流间的转换 -->
<dependency>
  <groupId>de.ruedigermoeller</groupId>
  <artifactId>fst</artifactId>
  <version>2.57</version> <!-- 注意:更高版本不支持 JDK 8 -->
</dependency>

为了将 token 存储在 Redis 中,需要配置 Redis 连接,并让 Sa-Token 使用 Redis 来存储 token。

配置 Redis 连接

首先,创建一个配置类来启动和配置 Redis。 app.properties

redis.host=127.0.0.1
redis.port=6789
redis.password=fdsafgh
redis.database=2
redis.timeout=15000
redis.cacheName=main
package com.litongjava.tio.boot.admin.config;

import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.jfinal.aop.annotation.AInitialization;
import com.litongjava.jfinal.plugin.redis.Cache;
import com.litongjava.jfinal.plugin.redis.Redis;
import com.litongjava.jfinal.plugin.redis.RedisPlugin;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.utils.environment.EnvUtils;

@AConfiguration
public class RedisPluginConfig {

  @AInitialization
  public RedisPlugin redisPlugin() {
    String host = EnvUtils.getStr("redis.host");
    Integer port = EnvUtils.getInt("redis.port");
    String password = EnvUtils.getStr("redis.password");
    int redisTimeout = EnvUtils.getInt("redis.timeout", 60);
    int redisDatabase = EnvUtils.getInt("redis.database", 0);
    String cacheName = EnvUtils.get("redis.cacheName");

    // 创建并启动 Redis 插件
    RedisPlugin mainRedis = new RedisPlugin(cacheName, host, port, redisTimeout, password, redisDatabase);
    mainRedis.start();

    // 测试连接
    Cache cache = Redis.use(cacheName);
    cache.getJedis().connect();

    TioBootServer.me().addDestroyMethod(mainRedis::stop);
    return mainRedis;
  }
}

使用 Sa-Token 的 Redis 存储

然后,在 Sa-Token 的配置中指定使用 Redis 来存储 token。

import com.litongjava.jfinal.plugin.satoken.SaTokenDaoRedis;
// 在 Sa-Token 配置类中添加
String cacheName = EnvUtils.get("redis.cacheName");
SaManager.setSaTokenDao(new SaTokenDaoRedis(cacheName));

通过这种方式,Sa-Token 将使用 Redis 作为 token 的存储介质,提高了应用的可伸缩性和性能。

总结

整合 Sa-Token 到并使用 Redis 作为存储,为应用提供了一种强大且灵活的方式来处理用户认证和会话管理。通过上述步骤,您可以轻松地在 Tio-Boot 项目中实现基于 token 的身份验证系统,并利用 Redis 的高性能和持久化特性来管理这些 token。

记得在实际部署应用时,根据实际环境调整 Redis 的连接配置,确保应用的安全性和性能。同时,您可能还需要对登录逻辑进行进一步的扩展,以支持例如用户验证、密码加密等功能。

这样,您就完成了 Sa-Token 在 Tio-Boot 中的整合,并能够处理复杂的身份验证和授权场景,为您的应用添加了一层安全的保护。

增加 Api Key

    // 增加一个Api用户设置token永不过期,让外部系统通过这个token调用本系统
    SaLoginModel loginModel = new SaLoginModel();
    loginModel.setTimeout(-1);
    loginModel.setToken("123456");
    StpUtil.createLoginSession("1", loginModel);