ThingsBoard - APP密码输入错误提示改成中文

问题描述

在APP中,密码输入错误,显示英文。

问题分析

APP部分已经全面翻译,出现英文应该是服务端的反馈。看来需要彻底解决电脑端翻译问题。

1.搜索已编译代码

由于资源管理器搜索能力有限,使用Visual Code进行搜索,先将相关jar文件解压。在D盘建立一个临时目录test,将thingsboard.jar拷入

  • 进入下面目录,执行对应命令进行解压
    D:\test\thingsboard\lib>jar xf thingsboard.jar
    D:\test\thingsboard\lib\BOOT-INF\lib>jar xf ui-ngx-4.1.0.jar
  • 用Visual Code打开D:\test\thingsboard目录,搜索Invalid username or password
    没有搜到。
    发现D:\test\thingsboard\lib\BOOT-INF\lib\public\assets\locale有翻译文件,但是都是完全翻译了的。

该方法没有结果。

2.搜索源代码

下载源代码,搜索,有三个文件包含Invalid username or password

java 复制代码
//1.\thingsboard-master\application\src\main\java\org\thingsboard\server\exception\ThingsboardErrorResponseHandler.java
public class SwaggerConfiguration {
    ...
    private static ApiResponses loginErrorResponses() {
        ApiResponses apiResponses = new ApiResponses();

        apiResponses.addApiResponse("401", errorResponse("Unauthorized",
                Map.of(
                        "bad-credentials", errorExample("Bad credentials",
                                ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
                        "token-expired", errorExample("JWT token expired",
                                ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)),
                        "account-disabled", errorExample("Disabled account",
                                ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
                        "account-locked", errorExample("Locked account",
                                ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
                        "authentication-failed", errorExample("General authentication error",
                                ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))
                )
        ));
        ...
    }
    ...
}    

//2.\thingsboard-master\application\src\main\java\org\thingsboard\server\exception\ThingsboardErrorResponseHandler.java

public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler, ErrorController {
    ....
    private void handleAuthenticationException(AuthenticationException authenticationException, HttpServletResponse response) throws IOException {
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        if (authenticationException instanceof BadCredentialsException || authenticationException instanceof UsernameNotFoundException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
        } else if (authenticationException instanceof DisabledException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
        } else if (authenticationException instanceof LockedException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
        } else if (authenticationException instanceof JwtExpiredTokenException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED));
        } else if (authenticationException instanceof AuthMethodNotSupportedException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of(authenticationException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
        } else if (authenticationException instanceof UserPasswordExpiredException expiredException) {
            String resetToken = expiredException.getResetToken();
            JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsExpiredResponse.of(expiredException.getMessage(), resetToken));
        } else if (authenticationException instanceof UserPasswordNotValidException expiredException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsViolationResponse.of(expiredException.getMessage()));
        } else if (authenticationException instanceof CredentialsExpiredException credentialsExpiredException) {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardCredentialsViolationResponse.of(credentialsExpiredException.getMessage(), ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
        } else {
            JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED));
        }
    }
    ...
}

//3.\thingsboard-master\application\src\test\java\org\thingsboard\server\controller\AuthControllerTest.java
public class AuthControllerTest extends AbstractControllerTest {
    ....
    public void testFailedLogin() throws Exception {
        int maxFailedLoginAttempts = 3;
        loginSysAdmin();
        updateSecuritySettings(securitySettings -> {
            securitySettings.setMaxFailedLoginAttempts(maxFailedLoginAttempts);
        });
        loginTenantAdmin();

        for (int i = 0; i < maxFailedLoginAttempts; i++) {
            String error = getErrorMessage(doPost("/api/auth/login",
                    new LoginRequest(CUSTOMER_USER_EMAIL, "IncorrectPassword"))
                    .andExpect(status().isUnauthorized()));
            assertThat(error).containsIgnoringCase("invalid username or password");
        }
        ...
    }
    ...
}

3.三个文件的作用

  1. SwaggerConfiguration.java - API文档配置
    作用:生成Swagger/OpenAPI文档中的错误响应示例
  • 这是纯文档说明,不参与实际业务逻辑
  • 告诉API使用者:当用户名或密码错误时,会返回这个错误信息
  • 用于自动生成API文档(你在Swagger UI上看到的示例)
  • 修改这里只影响文档显示,不影响实际错误返回
  1. ThingsboardErrorResponseHandler.java - 错误处理核心
    作用:实际处理认证异常并返回错误响应的核心处理器
  • 这是真正的业务逻辑所在!
  • 当BadCredentialsException或UsernameNotFoundException发生时
  • 会实际构造并返回"Invalid username or password"错误响应
  • 这是必须修改的地方才能改变实际返回的错误信息
  • 位置:handleAuthenticationException方法中
  1. AuthControllerTest.java - 单元测试
    作用:自动化测试用例,验证登录失败场景
  • 这是测试代码,只在测试时运行
  • 模拟用户输入错误密码的场景
  • 断言(assert)返回的错误信息包含"invalid username or password"
  • 确保错误处理逻辑正确工作
  • 修改这里只影响测试验证逻辑
4.ThingsBoard 的国际化现状

由此可以看出ThingsBoard 的国际化现状:

  1. 前端有国际化
  • UI 有完整的 i18n 支持:/ui-ngx/src/assets/i18n/ 下有多种语言文件
  1. 后端几乎无国际化
  • 错误信息都是硬编码英文
  • 后端异常消息直接返回给前端
  • 前后端语言不一致的隐患
5.修改优先级
文件 重要性 是否需要修改 影响范围
ThingsboardErrorResponseHandler.java ⭐⭐⭐⭐⭐ 必须修改 实际用户看到的错误
SwaggerConfiguration.java ⭐⭐⭐ 建议修改 API文档显示
AuthControllerTest.java 可选修改 测试用例通过性

问题解决

  1. 修改源代码并编译
    ...
  2. 直接在JAR文件中修改硬编码的字符串
    ...
相关推荐
MEIXIFU131 分钟前
便利店实际经营面积怎么选最合适
大数据·人工智能·物联网·生活·迭代加深
xiaohaiAIgeo37 分钟前
【2026年】HG/T 20656-2024化工暖通空调设计规范:新版标准的变化与影响
java·前端·javascript·科普知识
小强库计算机毕业设计1 小时前
基于 Spring Boot + Vue3 的校园管理系统
java·spring boot·后端·项目实战·管理系统·技术分享·校园管理系统实战
霸道流氓气质1 小时前
Java中信号量(Semaphore):从本地到分布式
java·开发语言·分布式
怪奇云呼军1 小时前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
编程令我快乐1 小时前
maven部分构建参数讲解
java·maven
ZC跨境爬虫1 小时前
LeetCode 27. 移除元素(双指针详解 + Java Python 多解法对比)
java·python·leetcode
kisbad2 小时前
Day 036|OpenAI Agents SDK 快速开始:今天跑通第一个 Agent
java·前端·javascript
憧憬成为java架构高手的小白2 小时前
langchain4j框架学习
java
0x532 小时前
Java网络编程(四)
java·网络