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文件中修改硬编码的字符串
    ...
相关推荐
云原生指北6 分钟前
GitHub Copilot SDK 入门:五分钟构建你的第一个 AI Agent
java
Leinwin4 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
薛定谔的悦4 小时前
MQTT通信协议业务层实现的完整开发流程
java·后端·mqtt·struts
enjoy嚣士5 小时前
springboot之Exel工具类
java·spring boot·后端·easyexcel·excel工具类
罗超驿5 小时前
独立实现双向链表_LinkedList
java·数据结构·链表·linkedlist
盐水冰6 小时前
【烘焙坊项目】后端搭建(12) - 订单状态定时处理,来单提醒和顾客催单
java·后端·学习
凸头6 小时前
CompletableFuture 与 Future 对比与实战示例
java·开发语言
wuqingshun3141596 小时前
线程安全需要保证几个基本特征
java·开发语言·jvm
努力也学不会java7 小时前
【缓存算法】一篇文章带你彻底搞懂面试高频题LRU/LFU
java·数据结构·人工智能·算法·缓存·面试
攒了一袋星辰7 小时前
高并发强一致性顺序号生成系统 -- SequenceGenerator
java·数据库·mysql