Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ server:
key-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
# 优雅停机
shutdown: graceful
netty:
Expand Down Expand Up @@ -199,7 +199,6 @@ request-matcher:
- /doc.html=laokou-gateway
- /webjars/**=laokou-gateway
- /ws=laokou-gateway
- /api/login=laokou-gateway

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Login blocked by gateway 🐞 Bug ✓ Correctness

网关移除了 /api/login 的放行规则后,AuthFilter 会对未带 Authorization 的登录页请求直接返回 401,导致授权码登录页不可访问。由于 auth 服务
context-path 为 /api 且 LoginController 映射 /login,登录页实际路径为 /api/login。
Agent Prompt
### Issue description
Gateway 的 `request-matcher.ignore-patterns.GET` 中移除了 `/api/login`,而 `AuthFilter` 对未命中放行列表且缺少 `Authorization` 的请求会直接返回 401,导致登录页(`/api/login`)无法访问。

### Issue Context
- auth 服务 `server.servlet.context-path=/api`,`GET /login` 实际对外为 `/api/login`。
- 登录页还会加载静态资源(例如 `/api/js/**`, `/api/img/**`),如通过网关访问也应同步放行。

### Fix Focus Areas
- laokou-cloud/laokou-gateway/src/main/resources/application.yml[190-207]
- laokou-cloud/laokou-gateway/src/main/java/org/laokou/gateway/filter/AuthFilter.java[64-86]
- laokou-service/laokou-auth/laokou-auth-start/src/main/resources/application.yml[30-55]
- laokou-service/laokou-auth/laokou-auth-adapter/src/main/java/org/laokou/auth/web/LoginController.java[24-38]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

POST:
- /api/v1/captchas/send/mail=laokou-gateway
- /api/v1/captchas/send/mobile=laokou-gateway
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,16 @@

import io.minio.BucketExistsArgs;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.Http;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.http.Method;
import io.minio.errors.MinioException;
import org.laokou.common.i18n.common.exception.BizException;
import org.laokou.common.oss.model.BaseOss;
import org.laokou.common.oss.model.FileInfo;
import org.laokou.common.oss.model.MinIO;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -61,9 +53,7 @@ protected MinioClient getObj() {
}

@Override
protected void checkBucket(MinioClient minioClient) throws ServerException, InsufficientDataException,
ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException,
InvalidResponseException, XmlParserException, InternalException {
protected void checkBucket(MinioClient minioClient) throws MinioException {
String bucketName = this.minIO.getBucketName();
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!isExist) {
Expand All @@ -73,35 +63,29 @@ protected void checkBucket(MinioClient minioClient) throws ServerException, Insu
}

@Override
protected void upload(MinioClient minioClient) throws ServerException, InsufficientDataException,
ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException,
InvalidResponseException, XmlParserException, InternalException {
protected void upload(MinioClient minioClient) throws MinioException {
PutObjectArgs objectArgs = PutObjectArgs.builder()
.bucket(this.minIO.getBucketName())
.object(fileInfo.name())
.stream(fileInfo.inputStream(), fileInfo.size(), -1)
.stream(fileInfo.inputStream(), fileInfo.size(), -1L)
.contentType(fileInfo.contentType())
.build();
minioClient.putObject(objectArgs);
}

@Override
protected String getUrl(MinioClient minioClient) throws ServerException, InsufficientDataException,
ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException,
InvalidResponseException, XmlParserException, InternalException {
protected String getUrl(MinioClient minioClient) throws MinioException {
GetPresignedObjectUrlArgs objectUrlArgs = GetPresignedObjectUrlArgs.builder()
.bucket(this.minIO.getBucketName())
.object(fileInfo.name())
.method(Method.GET)
.method(Http.Method.GET)
Comment on lines +77 to +81
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): 在 MinIO 的 GetPresignedObjectUrlArgs 中使用 Http.Method.GET 看起来前后不一致,并且可能无法通过编译。

之前这里使用的是 MinIO 的 io.minio.http.Method.GET,现在改成向 .method(...) 传入 Http.Method.GET。除非 Http.Methodio.minio.http.Method 是同一种类型,否则这段代码要么无法编译,要么会被 builder 拒绝。请继续使用 MinIO 的 Method 枚举,或者添加一个从 Http.Method 转换为 GetPresignedObjectUrlArgs.builder().method(...) 所需类型的显式转换。

Original comment in English

issue (bug_risk): Using Http.Method.GET with MinIO’s GetPresignedObjectUrlArgs looks inconsistent and may not compile.

This previously used MinIO’s io.minio.http.Method.GET, but now passes Http.Method.GET into .method(...). Unless Http.Method is the same type as io.minio.http.Method, this will fail to compile or be rejected by the builder. Please either keep using MinIO’s Method enum or add an explicit conversion from Http.Method to the type expected by GetPresignedObjectUrlArgs.builder().method(...).

.expiry(5, TimeUnit.DAYS)
.build();
return minioClient.getPresignedObjectUrl(objectUrlArgs);
}

@Override
public void createBucket() throws ServerException, InsufficientDataException, ErrorResponseException, IOException,
NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException,
InternalException {
public void createBucket() throws MinioException {
MinioClient minioClient = getObj();
String bucketName = this.minIO.getBucketName();
boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
servlet:
context-path: /api
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http,
// https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/form.html
// 登录页面 -> DefaultLoginPageGeneratingFilter
.formLogin(form -> form.loginPage("/login")
// .failureHandler(oAuth2AuthenticationFailureHandler)
.permitAll())
// 清除 session
.logout(logout -> logout.clearAuthentication(true).invalidateHttpSession(true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
port: ${SERVER_PORT:1111}
servlet:
Expand Down Expand Up @@ -162,8 +162,8 @@ spring:
- /v1/secrets=laokou-auth
- /doc.html=laokou-auth
- /webjars/**=laokou-auth
- /login=laokou-auth
- /img/**=laokou-auth
- /js/**=laokou-auth
POST:
- /v1/captchas/send/mail=laokou-auth
- /v1/captchas/send/mobile=laokou-auth
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>老寇IoT云平台统一认证</title>
<script src="js/axios.min.js"></script>
<style>
html, body {
height: 100%;
Expand All @@ -13,7 +14,7 @@
overflow-x: hidden;
min-height: 100vh;
font-family: Arial, Helvetica, sans-serif;
background: url('/api/img/FfdJeJRQWjEeGTpqgBKj.png') no-repeat center center;
background: url('img/FfdJeJRQWjEeGTpqgBKj.png') no-repeat center center;
background-size: cover;

/* 更健壮的居中布局:适配不同屏幕尺寸 */
Expand Down Expand Up @@ -98,32 +99,56 @@
<h2>老寇IoT云平台统一认证</h2>
</div>

<div class="alert alert-danger" th:if="${session.SPRING_SECURITY_LAST_EXCEPTION != null}">
<div class="alert alert-danger" th:if="${param.error != null and session.SPRING_SECURITY_LAST_EXCEPTION != null}">
<span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION?.message}"></span>
</div>
<form method="post" th:action="@{/login}">
<form method="post" th:action="@{/login}" autocomplete="off">

<div class="form-item row" style="width: 100%;">
<input id="tenant" name="tenant" type="text" placeholder="请输入租户编号" required autofocus/>
<input id="uuid" name="uuid" type="text" autocomplete="new-password" hidden="hidden" placeholder="请输入唯一标识" required/>
</div>

<div class="form-item row" style="width: 100%;">
<input id="username" name="username" type="text" placeholder="请输入用户名" required/>
<input id="tenant_code" name="tenant_code" autocomplete="new-password" value="laokouyun" type="text" placeholder="请输入租户编号" required autofocus/>
</div>

<div class="form-item row" style="width: 100%;">
<input id="password" name="password" type="password" placeholder="请输入密码" required/>
<input id="username" name="username" type="text" autocomplete="new-password" value="admin" placeholder="请输入用户名" required/>
</div>

<div class="form-item row" style="width: 100%;">
<input id="password" name="password" type="password" autocomplete="new-password" value="admin123" placeholder="请输入密码" required/>
</div>
Comment on lines +108 to 121

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Hardcoded admin credentials 🐞 Bug ⛨ Security

两个 login.html 模板将 tenant_code/username/password 预填为
laokouyun/admin/admin123,任何未认证访问者都能直接看到并一键尝试该组合,存在明显账号安全风险。
Agent Prompt
### Issue description
登录页模板硬编码并预填了 `tenant_code` / `username` / `password`(`laokouyun` / `admin` / `admin123`)。这会把“默认凭据”暴露给所有访问者,并显著降低账号安全性。

### Issue Context
这是服务端渲染模板(Thymeleaf),任何未认证访问者都可查看页面源代码或直接看到输入框中的默认值。

### Fix Focus Areas
- laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html[111-121]
- laokou-service/laokou-standalone/laokou-standalone-auth/laokou-standalone-auth-start/src/main/resources/templates/login.html[111-121]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


<div class="form-item">
<div class="row">
<input id="captcha" name="captcha" type="text" placeholder="请输入验证码" required/>
<img class="captcha" th:src="@{/captcha}" alt="captcha"
onclick="this.src='/captcha?ts='+Date.now()"/>
<input id="captcha" name="captcha" autocomplete="new-password" type="text" placeholder="请输入验证码" required/>
<img id="captcha-img" class="captcha" alt="验证码" onclick="getCaptcha()"/>

Check warning on line 126 in laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a 'onKeyPress|onKeyDown|onKeyUp' attribute to this <img> tag.

See more on https://sonarcloud.io/project/issues?id=KouShenhai_KCloud-Platform-Alibaba&issues=AZ0QezKUEnrKnygjfPYz&open=AZ0QezKUEnrKnygjfPYz&pullRequest=5914

Check warning on line 126 in laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Non-interactive elements should not be assigned mouse or keyboard event listeners.

See more on https://sonarcloud.io/project/issues?id=KouShenhai_KCloud-Platform-Alibaba&issues=AZ0QezKUEnrKnygjfPYy&open=AZ0QezKUEnrKnygjfPYy&pullRequest=5914
</div>
</div>

<button class="btn" type="submit">登&nbsp;&nbsp;录</button>
</form>
</div>
<script>
function getUuid() {
if (window.crypto && crypto.randomUUID) {

Check warning on line 135 in laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=KouShenhai_KCloud-Platform-Alibaba&issues=AZ0QezKUEnrKnygjfPYu&open=AZ0QezKUEnrKnygjfPYu&pullRequest=5914
return crypto.randomUUID();
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {

Check warning on line 138 in laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=KouShenhai_KCloud-Platform-Alibaba&issues=AZ0QezKUEnrKnygjfPYv&open=AZ0QezKUEnrKnygjfPYv&pullRequest=5914
const r = Math.random() * 16 | 0;

Check failure on line 139 in laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use `Math.trunc` instead of `| 0`.

See more on https://sonarcloud.io/project/issues?id=KouShenhai_KCloud-Platform-Alibaba&issues=AZ0QezKUEnrKnygjfPYw&open=AZ0QezKUEnrKnygjfPYw&pullRequest=5914
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
function getCaptcha() {
const uuid = getUuid()
document.getElementById("uuid").value = uuid
axios.get("v1/authorization-code/captchas/" + uuid).then(res => {
document.getElementById("captcha-img").src = res.data.data
})
}
getCaptcha()
</script>
Comment on lines 123 to +152

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Captcha not validated server-side 🐞 Bug ⛨ Security

登录页强制填写 uuid/captcha 并从 /v1/authorization-code/captchas/{uuid} 获取验证码,但当前 formLogin 认证链路使用默认
DaoAuthenticationProvider,未看到对 uuid/captcha 的服务端校验,验证码形同虚设且可被直接绕过。
Agent Prompt
### Issue description
登录页引入了 `uuid`/`captcha` 并要求用户填写,但当前 Spring Security 的 formLogin 认证链路未实现对这些参数的服务端校验,导致验证码只存在于前端交互层面,攻击者可直接 POST `/login` 绕过。

### Issue Context
- GET `/v1/authorization-code/captchas/{uuid}` 会生成验证码并写入 Redis(key 由控制器使用 `RedisKeyUtils.getAuthorizationCodeAuthCaptchaKey(uuid)` 生成),并返回 base64 图片。
- 但 formLogin 使用默认认证流程(DaoAuthenticationProvider),默认只校验 username/password。

### Fix Focus Areas
- laokou-service/laokou-auth/laokou-auth-start/src/main/resources/templates/login.html[107-152]
- laokou-service/laokou-auth/laokou-auth-app/src/main/java/org/laokou/auth/command/query/CaptchaGetQryExe.java[40-51]
- laokou-service/laokou-auth/laokou-auth-infrastructure/src/main/java/org/laokou/auth/config/OAuth2ResourceServerConfig.java[49-72]
- laokou-service/laokou-auth/laokou-auth-infrastructure/src/main/java/org/laokou/auth/config/OAuth2AuthorizationServerConfig.java[176-186]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
servlet:
context-path: /api
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
servlet:
context-path: /api
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ server:
http2:
enabled: false
shutdown: graceful
forward-headers-strategy: native
forward-headers-strategy: framework

management:
endpoints:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
servlet:
context-path: /api
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
# spring
spring:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
port: ${SERVER_PORT:8099}
servlet:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ server:
key-store-password: laokou
http2:
enabled: false
forward-headers-strategy: native
forward-headers-strategy: framework
shutdown: graceful
port: ${SERVER_PORT:8098}
servlet:
Expand Down Expand Up @@ -157,10 +157,13 @@ spring:
- /v3/api-docs/**=laokou-standalone-auth
- /actuator/**=laokou-standalone-auth
- /error=laokou-standalone-auth
- /v1/captchas/{uuid}=laokou-standalone-auth
- /v1/username-password/captchas/{uuid}=laokou-standalone-auth
- /v1/authorization-code/captchas/{uuid}=laokou-standalone-auth
- /v1/secrets=laokou-standalone-auth
- /doc.html=laokou-standalone-auth
- /webjars/**=laokou-standalone-auth
- /img/**=laokou-standalone-auth
- /js/**=laokou-standalone-auth
POST:
- /v1/captchas/send/mail=laokou-standalone-auth
- /v1/captchas/send/mobile=laokou-standalone-auth
Expand Down

Large diffs are not rendered by default.

Loading
Loading