Skip to content

Commit 2a22d06

Browse files
committed
feat(system/client): 添加双token认证配置
1 parent f6075ca commit 2a22d06

15 files changed

Lines changed: 285 additions & 23 deletions

File tree

continew-server/src/main/resources/db/changelog/db.changelog-master.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ databaseChangeLog:
1111
file: db/changelog/mysql/plugin/plugin_schedule.sql
1212
- include:
1313
file: db/changelog/mysql/plugin/plugin_generator.sql
14+
- includeAll:
15+
path: db/changelog/mysql/V4.2.0
1416
# PostgreSQL
1517
# - include:
1618
# file: db/changelog/postgresql/main_table.sql
@@ -23,4 +25,6 @@ databaseChangeLog:
2325
# - include:
2426
# file: db/changelog/postgresql/plugin/plugin_schedule.sql
2527
# - include:
26-
# file: db/changelog/postgresql/plugin/plugin_generator.sql
28+
# file: db/changelog/postgresql/plugin/plugin_generator.sql
29+
# - includeAll:
30+
# path: db/changelog/postgresql/V4.2.0
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- liquibase formatted sql
2+
3+
-- changeset luoqiz:4.2.0-1
4+
-- comment sys_client 客户端表更新
5+
-- sys_client 添加双token列
6+
ALTER TABLE `sys_client`
7+
ADD COLUMN `is_enable_refresh_token` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否启用refresh token (true: 启用; false: 禁用)' AFTER `timeout`,
8+
ADD COLUMN `refresh_token_timeout` bigint NULL DEFAULT 2592000 COMMENT 'Refresh token有效期(单位:秒; 值必须大于0,否则取token的有效时长)' AFTER `is_enable_refresh_token`;
9+
10+
-- 初始化客户端数据
11+
INSERT INTO `sys_client`
12+
(`id`, `client_id`, `client_type`, `auth_type`, `active_timeout`, `timeout`, `status`, `create_user`, `create_time`,
13+
`is_enable_refresh_token`, `refresh_token_timeout`)
14+
VALUES (2, 'ef51c9a3e9046c4f2ea45142c8a8344b', 'XCX', '["ACCOUNT", "EMAIL", "PHONE", "SOCIAL"]', 1800, 86400, 1, 1,
15+
NOW(), b'1', 2592000);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-- liquibase formatted sql
2+
3+
-- changeset luoqiz:4.2.0-1
4+
-- comment sys_client 客户端表更新
5+
-- sys_client 添加双token列
6+
ALTER TABLE "public"."sys_client"
7+
ADD COLUMN "is_enable_refresh_token" bool NOT NULL DEFAULT false,
8+
ADD COLUMN "refresh_token_timeout" int8 DEFAULT 2592000;
9+
10+
COMMENT
11+
ON COLUMN "public"."sys_client"."is_enable_refresh_token" IS '是否启用refresh token (true: 启用; false: 禁用)';
12+
13+
COMMENT
14+
ON COLUMN "public"."sys_client"."refresh_token_timeout" IS 'Refresh token有效期(单位:秒; 值必须大于0,否则取token的有效时长)';
15+
16+
-- 初始化客户端数据
17+
INSERT INTO "sys_client"
18+
("id", "client_id", "client_type", "auth_type", "active_timeout", "timeout", "status", "create_user", "create_time",
19+
"is_enable_refresh_token", "refresh_token_timeout")
20+
VALUES (2, 'ef51c9a3e9046c4f2ea45142c8a8344b', 'XCX', '["ACCOUNT", "EMAIL", "PHONE", "SOCIAL"]', 1800, 86400, 1, 1,
21+
NOW(), true, 2592000);

continew-system/src/main/java/top/continew/admin/auth/AbstractLoginHandler.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package top.continew.admin.auth;
1818

19-
import cn.dev33.satoken.stp.StpUtil;
2019
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
2120
import cn.dev33.satoken.stp.parameter.enums.SaLogoutMode;
2221
import cn.dev33.satoken.stp.parameter.enums.SaReplacedRange;
@@ -25,6 +24,7 @@
2524
import jakarta.servlet.http.HttpServletRequest;
2625
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
2726
import org.springframework.stereotype.Component;
27+
import top.continew.admin.auth.constant.AuthConstants;
2828
import top.continew.admin.auth.model.req.LoginReq;
2929
import top.continew.admin.auth.model.resp.LoginResp;
3030
import top.continew.admin.common.context.RoleContext;
@@ -46,6 +46,7 @@
4646
import top.continew.starter.extension.tenant.util.TenantUtils;
4747

4848
import java.util.HashSet;
49+
import java.util.Map;
4950
import java.util.Set;
5051
import java.util.concurrent.CompletableFuture;
5152

@@ -136,13 +137,11 @@ protected LoginResp authenticate(UserDO user, ClientResp client) {
136137
userContext.setClientId(client.getClientId());
137138
userContext.setTenantId(tenantId);
138139
// 登录并缓存用户信息
139-
StpUtil.login(userContext.getId(), loginParameter.setExtraData(BeanUtil
140-
.beanToMap(new UserExtraContext(ServletUtils.getRequest()))));
140+
Map<String, Object> extraData = BeanUtil.beanToMap(new UserExtraContext(ServletUtils.getRequest()));
141+
extraData.put(AuthConstants.LOGIN_USER, userContext);
142+
loginParameter.setExtraData(extraData);
141143
UserContextHolder.setContext(userContext);
142-
return LoginResp.builder()
143-
.token(StpUtil.getTokenValue())
144-
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
145-
.build();
144+
return LoginHandler.buildLoginResp(loginParameter, userContext, client);
146145
}
147146

148147
/**

continew-system/src/main/java/top/continew/admin/auth/LoginHandler.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,18 @@
1616

1717
package top.continew.admin.auth;
1818

19+
import cn.dev33.satoken.stp.StpUtil;
20+
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
21+
import cn.dev33.satoken.temp.SaTempUtil;
1922
import jakarta.servlet.http.HttpServletRequest;
2023
import top.continew.admin.auth.enums.AuthTypeEnum;
2124
import top.continew.admin.auth.model.req.LoginReq;
25+
import top.continew.admin.auth.model.resp.DoubleTokenLoginResp;
2226
import top.continew.admin.auth.model.resp.LoginResp;
27+
import top.continew.admin.auth.model.resp.SingleTokenLoginResp;
28+
import top.continew.admin.common.context.UserContext;
2329
import top.continew.admin.system.model.resp.ClientResp;
30+
import top.continew.starter.extension.tenant.context.TenantContextHolder;
2431

2532
/**
2633
* 登录处理器
@@ -65,4 +72,38 @@ public interface LoginHandler<T extends LoginReq> {
6572
* @return 认证类型
6673
*/
6774
AuthTypeEnum getAuthType();
75+
76+
/**
77+
* 构建登录信息
78+
*
79+
* @param loginParameter 登录的参数
80+
* @param userContext 用户上下文信息
81+
* @param client 客户端信息
82+
* @return
83+
*/
84+
static LoginResp buildLoginResp(SaLoginParameter loginParameter, UserContext userContext, ClientResp client) {
85+
StpUtil.login(userContext.getId(), loginParameter);
86+
if (Boolean.TRUE.equals(client.getIsEnableRefreshToken())) {
87+
// 刷新令牌设置的有效时长
88+
long refreshExpiresIn = (client.getRefreshTokenTimeout() != null && client.getRefreshTokenTimeout() > 0L)
89+
? client.getRefreshTokenTimeout() : client.getTimeout();
90+
String refreshToken = SaTempUtil.createToken(userContext.getId(), refreshExpiresIn, false);
91+
// 将生成的token保存一份,方便刷新token时删除先前的token
92+
loginParameter.setToken(StpUtil.getTokenValue());
93+
SaTempUtil.saveToken(refreshToken, loginParameter, refreshExpiresIn);
94+
return DoubleTokenLoginResp.builder()
95+
.accessToken(StpUtil.getTokenValue())
96+
.accessExpiresIn(StpUtil.getTokenTimeout())
97+
.refreshToken(refreshToken)
98+
.refreshExpiresIn(refreshExpiresIn)
99+
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
100+
.build();
101+
} else {
102+
return SingleTokenLoginResp.builder()
103+
.token(StpUtil.getTokenValue())
104+
.expiresIn(StpUtil.getTokenTimeout())
105+
.tenantId(TenantContextHolder.isTenantEnabled() ? TenantContextHolder.getTenantId() : null)
106+
.build();
107+
}
108+
}
68109
}

continew-system/src/main/java/top/continew/admin/auth/constant/AuthConstants.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public class AuthConstants {
3434
*/
3535
public static final String LOGOUT_URI = "/auth/logout";
3636

37+
/**
38+
* Map 存储登录用户信息时的 key 值
39+
*/
40+
public static final String LOGIN_USER = "loginUser";
41+
3742
private AuthConstants() {
3843
}
3944
}

continew-system/src/main/java/top/continew/admin/auth/controller/AuthController.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.swagger.v3.oas.annotations.tags.Tag;
2626
import jakarta.servlet.http.HttpServletRequest;
2727
import jakarta.validation.Valid;
28+
import jakarta.validation.constraints.NotBlank;
2829
import lombok.RequiredArgsConstructor;
2930
import me.zhyd.oauth.request.AuthRequest;
3031
import me.zhyd.oauth.utils.AuthStateUtils;
@@ -72,6 +73,13 @@ public LoginResp login(@RequestBody @Valid LoginReq req, HttpServletRequest requ
7273
return authService.login(req, request);
7374
}
7475

76+
@SaIgnore
77+
@Operation(summary = "刷新token", description = "刷新token")
78+
@PostMapping("/refreshToken")
79+
public LoginResp refreshToken(@RequestParam("refreshToken") @NotBlank String refreshToken) {
80+
return authService.refreshToken(refreshToken);
81+
}
82+
7583
@Operation(summary = "登出", description = "注销用户的当前登录")
7684
@Parameter(name = "Authorization", description = "令牌", required = true, example = "Bearer xxxx-xxxx-xxxx-xxxx", in = ParameterIn.HEADER)
7785
@PostMapping("/logout")
@@ -88,8 +96,8 @@ public Object logout() {
8896
public SocialAuthAuthorizeResp authorize(@PathVariable @EnumValue(value = SocialSourceEnum.class, message = "第三方平台无效") String source) {
8997
AuthRequest authRequest = authRequestFactory.getAuthRequest(source);
9098
return SocialAuthAuthorizeResp.builder()
91-
.authorizeUrl(authRequest.authorize(AuthStateUtils.createState()))
92-
.build();
99+
.authorizeUrl(authRequest.authorize(AuthStateUtils.createState()))
100+
.build();
93101
}
94102

95103
@Log(ignore = true)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package top.continew.admin.auth.model.resp;
18+
19+
import io.swagger.v3.oas.annotations.media.Schema;
20+
import lombok.Data;
21+
import lombok.EqualsAndHashCode;
22+
import lombok.experimental.SuperBuilder;
23+
24+
/**
25+
* 双token模式
26+
*/
27+
@Data
28+
@SuperBuilder
29+
@EqualsAndHashCode(callSuper = true)
30+
public class DoubleTokenLoginResp extends LoginResp {
31+
32+
/**
33+
* 访问令牌
34+
*/
35+
@Schema(description = "访问令牌", example = "eyJ0eXAiOiJlV1QiLCJhbGciqiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb29pbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiSjd4SUljYnU5cmNwU09vQ3Uyc1ND1BYYTYycFRjcjAifQ.KUPOYm-2wfuLUSfEEAbpGE527fzmkAJG7sMNcQ0pUZ8")
36+
private String accessToken;
37+
38+
/**
39+
* 刷新令牌
40+
*/
41+
@Schema(description = "刷新令牌", example = "12a5c5e1f87d4b4db614c6229c2c8916")
42+
private String refreshToken;
43+
44+
/**
45+
* 访问令牌有效时长(秒)
46+
*/
47+
@Schema(description = "访问令牌有效时长(秒)", example = "1800")
48+
private Long accessExpiresIn;
49+
50+
/**
51+
* 刷新令牌有效时长(秒)
52+
*/
53+
@Schema(description = "刷新令牌有效时长(秒)", example = "2592000")
54+
private Long refreshExpiresIn;
55+
}

continew-system/src/main/java/top/continew/admin/auth/model/resp/LoginResp.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package top.continew.admin.auth.model.resp;
1818

1919
import io.swagger.v3.oas.annotations.media.Schema;
20-
import lombok.Builder;
2120
import lombok.Data;
21+
import lombok.experimental.SuperBuilder;
2222

2323
import java.io.Serial;
2424
import java.io.Serializable;
@@ -30,19 +30,13 @@
3030
* @since 2022/12/21 20:42
3131
*/
3232
@Data
33-
@Builder
33+
@SuperBuilder
3434
@Schema(description = "登录响应参数")
3535
public class LoginResp implements Serializable {
3636

3737
@Serial
3838
private static final long serialVersionUID = 1L;
3939

40-
/**
41-
* 令牌
42-
*/
43-
@Schema(description = "令牌", example = "eyJ0eXAiOiJlV1QiLCJhbGciqiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb29pbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiSjd4SUljYnU5cmNwU09vQ3Uyc1ND1BYYTYycFRjcjAifQ.KUPOYm-2wfuLUSfEEAbpGE527fzmkAJG7sMNcQ0pUZ8")
44-
private String token;
45-
4640
/**
4741
* 租户 ID
4842
*/
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package top.continew.admin.auth.model.resp;
18+
19+
import io.swagger.v3.oas.annotations.media.Schema;
20+
import lombok.Data;
21+
import lombok.EqualsAndHashCode;
22+
import lombok.experimental.SuperBuilder;
23+
24+
/**
25+
* 单token模式,只有访问的token
26+
*/
27+
@Data
28+
@SuperBuilder
29+
@EqualsAndHashCode(callSuper = true)
30+
public class SingleTokenLoginResp extends LoginResp {
31+
32+
/**
33+
* 访问令牌
34+
*/
35+
@Schema(description = "访问令牌", example = "eyJ0eXAiOiJlV1QiLCJhbGciqiJIUzI1NiJ9.eyJsb2dpblR5cGUiOiJsb29pbiIsImxvZ2luSWQiOjEsInJuU3RyIjoiSjd4SUljYnU5cmNwU09vQ3Uyc1ND1BYYTYycFRjcjAifQ.KUPOYm-2wfuLUSfEEAbpGE527fzmkAJG7sMNcQ0pUZ8")
36+
private String token;
37+
38+
/**
39+
* 有效时长(秒)
40+
*/
41+
@Schema(description = "有效时长(秒)", example = "8600")
42+
private Long expiresIn;
43+
44+
}

0 commit comments

Comments
 (0)