# How to Configure JWT Token Refresh and Expiration in Sa-Token: A Complete Guide to ContiNew-Admin

> Learn to configure JWT token refresh and expiration in Sa-Token for ContiNew-Admin. Set dynamic active timeout, absolute expiry, and idle expiry for secure authentication.

- Repository: [OpenContiNew/continew-admin](https://github.com/continew-org/continew-admin)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Configure JWT token refresh and expiration in Sa-Token by enabling JWT-simple mode in [`application.yml`](https://github.com/continew-org/continew-admin/blob/main/application.yml), setting `dynamic-active-timeout: true` for automatic refresh, and defining per-client `timeout` (absolute expiry) and `activeTimeout` (idle expiry) values that are applied via `SaLoginParameter` during authentication.**

ContiNew-Admin leverages Sa-Token with JWT-simple mode to provide flexible, client-specific token lifecycle management. Understanding how to configure JWT token refresh and expiration in Sa-Token requires navigating three distinct configuration layers—from global YAML settings to dynamic client-specific parameters. This guide breaks down the exact implementation found in the continew-org/continew-admin repository, showing you how to control token longevity and automatic renewal based on the actual source code.

## Understanding the Three Configuration Layers

The ContiNew-Admin project implements a hierarchical approach to token management. Each layer overrides the previous one, allowing for both global defaults and client-specific customization:

- **Global Sa-Token configuration** defines the JWT mode, secret keys, and dynamic timeout behavior in [`application.yml`](https://github.com/continew-org/continew-admin/blob/main/application.yml)
- **Client-side configuration** stores per-client `timeout` and `activeTimeout` values in the database via the Client entity
- **Login-time parameters** bind these values to individual tokens through `SaLoginParameter` during the authentication handshake

## Global Sa-Token Configuration

All global JWT settings reside in [`continew-server/src/main/resources/config/application.yml`](https://github.com/continew-org/continew-admin/blob/main/continew-server/src/main/resources/config/application.yml). This file controls the fundamental token behavior for the entire application.

```yaml

# --- ### Sa-Token 配置

sa-token:
  # Token 名称（同时也是 cookie 名称）

  token-name: Authorization
  # 是否启用动态 activeTimeout 功能

  dynamic-active-timeout: true
  # JWT 秘钥（用于签名和校验 JWT）

  jwt-secret-key: asdasdasifhueuiwyurfewbfjsdafjk
  extension:
    enabled: true
    enableJwt: true        # <‑‑ 开启 JWT‑simple 模式

    dao.type: REDIS        # 持久化方式

```

The **critical settings** for JWT token refresh and expiration in this configuration are:

- `enableJwt: true` activates JWT-simple mode, causing Sa-Token to generate signed JWT tokens rather than opaque references
- `dynamic-active-timeout: true` enables the idle timeout mechanism that automatically refreshes the token's last-active timestamp on each validated request
- `jwt-secret-key` provides the HMAC secret used to sign and verify token integrity

You may optionally uncomment the global `timeout` and `active-timeout` fields in this file to establish site-wide defaults (for example, `timeout: 86400` for a 24-hour absolute expiration).

## Per-Client Token Lifetime Configuration

ContiNew-Admin allows individual clients (web applications, mobile apps, or API consumers) to specify their own token expiration policies. These values are defined in the client model and stored persistently.

The relevant fields appear in three files:

- [`continew-system/src/main/java/top/continew/admin/system/model/resp/ClientResp.java`](https://github.com/continew-org/continew-admin/blob/main/continew-system/src/main/java/top/continew/admin/system/model/resp/ClientResp.java)
- [`continew-system/src/main/java/top/continew/admin/system/model/req/ClientReq.java`](https://github.com/continew-org/continew-admin/blob/main/continew-system/src/main/java/top/continew/admin/system/model/req/ClientReq.java)
- [`continew-system/src/main/java/top/continew/admin/system/model/entity/ClientDO.java`](https://github.com/continew-org/continew-admin/blob/main/continew-system/src/main/java/top/continew/admin/system/model/entity/ClientDO.java)

```java
// ClientResp.java fragment
private Long timeout;        // token 有效期（秒），-1 表示永不过期
private Long activeTimeout;  // token 空闲失效时间（秒），-1 表示不限制

```

- **timeout**: Represents the absolute token expiration time in seconds. A value of `-1` indicates the token never expires absolutely.
- **activeTimeout**: Represents the idle timeout in seconds. If the token remains unused for this duration, it expires. A value of `-1` disables idle expiration.

When creating or updating a client through the admin interface, these values populate the `ClientDO` entity and subsequently drive token generation for that specific client's authentication sessions.

## Applying Token Settings During Authentication

When a user authenticates, the system binds the client-specific timeout values to the token through `SaLoginParameter`. This occurs in [`continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java`](https://github.com/continew-org/continew-admin/blob/main/continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java).

```java
// AbstractLoginHandler.authenticate(..) – relevant fragment
SaLoginParameter loginParameter = new SaLoginParameter();
loginParameter.setActiveTimeout(client.getActiveTimeout()); // idle expiration
loginParameter.setTimeout(client.getTimeout());             // absolute expiration
...
StpUtil.login(userContext.getId(),
               loginParameter.setExtraData(BeanUtil.beanToMap(new UserExtraContext(ServletUtils.getRequest()))));

```

The `StpUtil.login()` method receives these parameters and embeds the expiration claims within the JWT payload. Because these values originate from the specific client record associated with the login request, different clients can simultaneously maintain different token lifecycle policies under the same authentication endpoint.

## Automatic Token Refresh Mechanism

Token refresh operates transparently when **dynamic-active-timeout** is enabled. The mechanism relies on Sa-Token's interceptor chain configured in [`continew-server/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java`](https://github.com/continew-org/continew-admin/blob/main/continew-server/src/main/java/top/continew/admin/config/satoken/SaTokenConfiguration.java).

```java
@Bean
public SaInterceptor saInterceptor() {
    return new SaExtensionInterceptor(handle -> SaRouter.match(StringConstants.PATH_PATTERN)
        .notMatch(properties.getSecurity().getExcludes())
        .check(r -> {
            // normal sign‑validation or login check
            StpUtil.checkLogin();
            // … additional checks …
        }));
}

```

When `StpUtil.checkLogin()` executes during request processing, Sa-Token performs two operations:

1. Validates the current token against both the absolute `timeout` and the idle `activeTimeout`
2. If validation succeeds, automatically updates the token's last-active timestamp, effectively resetting the idle timeout window

This means as long as the client sends requests within the `activeTimeout` interval (e.g., every 30 minutes if `activeTimeout` is 1800 seconds), the token remains valid indefinitely regardless of the absolute `timeout` setting. No explicit refresh endpoint or manual token exchange is required.

## Implementation Walkthrough

To implement JWT token refresh and expiration in your ContiNew-Admin instance:

1. **Configure the JWT foundation** in [`application.yml`](https://github.com/continew-org/continew-admin/blob/main/application.yml):
   - Set `sa-token.extension.enableJwt: true`
   - Define `sa-token.jwt-secret-key` with a cryptographically secure random string
   - Enable `sa-token.dynamic-active-timeout: true`

2. **Define global fallbacks** (optional):
   - Uncomment and set `sa-token.timeout` for default absolute expiration
   - Uncomment and set `sa-token.active-timeout` for default idle expiration

3. **Create a client configuration** via the admin API or database:
   - Set `timeout` to `86400` for 24-hour absolute expiry, or `-1` for no expiry
   - Set `activeTimeout` to `1800` for 30-minute idle timeout, or `-1` to disable

4. **Authenticate using the client credentials**:
   - The system automatically retrieves the client's timeout settings
   - `AbstractLoginHandler` applies these to `SaLoginParameter`
   - Sa-Token generates a JWT containing the expiration claims

5. **Verify automatic refresh**:
   - Make subsequent requests within the `activeTimeout` window
   - Observe that `StpUtil.checkLogin()` extends the token life without requiring a new login

## Summary

Configuring JWT token refresh and expiration in Sa-Token within ContiNew-Admin involves coordinating global YAML settings with per-client database records:

- Enable **JWT-simple mode** and **dynamic-active-timeout** in [`application.yml`](https://github.com/continew-org/continew-admin/blob/main/application.yml) to activate the refresh infrastructure
- Store **client-specific** `timeout` (absolute) and `activeTimeout` (idle) values in the `ClientDO` entity
- Apply these values via **SaLoginParameter** in `AbstractLoginHandler.authenticate()` during login
- Rely on **SaInterceptor** and `StpUtil.checkLogin()` to automatically refresh the idle timeout on every valid request
- Use `-1` for either timeout value to disable that specific expiration mechanism

## Frequently Asked Questions

### What is the difference between timeout and activeTimeout in Sa-Token?

**Timeout** represents the absolute expiration time—the maximum lifetime of a token regardless of activity, measured in seconds since creation. **ActiveTimeout** represents the idle expiration time—the duration of inactivity allowed before the token becomes invalid. When `dynamic-active-timeout` is enabled, each request resets the activeTimeout counter, while the timeout counter continues toward its absolute limit. Set either value to `-1` to disable that specific expiration check.

### How does automatic token refresh work without a dedicated refresh endpoint?

Sa-Token's **dynamic-active-timeout** mechanism eliminates the need for explicit refresh endpoints. When configured in `SaTokenConfiguration`, the `SaInterceptor` calls `StpUtil.checkLogin()` on every protected request. This method automatically updates the token's last-active timestamp in Redis if the token is valid, effectively extending the idle window defined by `activeTimeout`. The token remains valid as long as the user remains active within the specified interval.

### Can I use standard Sa-Token tokens instead of JWT in ContiNew-Admin?

Yes. To disable JWT mode and revert to Sa-Token's default UUID-based token mechanism, set `sa-token.extension.enableJwt: false` in [`application.yml`](https://github.com/continew-org/continew-admin/blob/main/application.yml). This change forces Sa-Token to generate opaque token strings rather than signed JWTs. Note that this requires different handling for token persistence and validation, though the timeout and activeTimeout mechanisms continue to function identically through the Redis-backed DAO configuration.

### Where does ContiNew-Admin store JWT token state when using Redis?

According to the configuration in [`application.yml`](https://github.com/continew-org/continew-admin/blob/main/application.yml), setting `sa-token.extension.dao.type: REDIS` directs Sa-Token to persist token metadata—including timeout timestamps, active timeout tracking, and session mapping—to Redis. While the JWT itself contains signed claims readable by clients, the server-side state required for timeout enforcement and concurrent login management resides in Redis key-value entries managed by Sa-Token's DAO layer.