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文件中修改硬编码的字符串
    ...
相关推荐
Jeremy爱编码1 天前
电话号码的字母组合
java·算法·leetcode
月明长歌1 天前
JavaThread类详解核心属性、常用方法与实践
java·开发语言·jvm
Good_Starry1 天前
Java——网络编程
java
zfj3211 天前
java垃圾收集 minorgc majargc fullgc
java·开发语言·jvm·gc·垃圾收集器
Vic101011 天前
华为云高斯数据库:gsqlexec用法
java·大数据·数据库·postgresql·华为云
Seven971 天前
剑指offer-61、序列化二叉树
java
姓蔡小朋友1 天前
算法-子串
java·数据结构·算法
耀耀_很无聊1 天前
16_大文件上传方案:分片上传、断点续传与秒传
java·spring boot·后端
2501_941805931 天前
面向高可用微服务体系的状态管理演进与多语言实现经验融合实践分享文章
java·大数据·分布式