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文件中修改硬编码的字符串
    ...
相关推荐
侠客行03176 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪6 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚8 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
coderzxy8 小时前
mbpoll 交叉编译
物联网
风流倜傥唐伯虎8 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码8 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚8 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂8 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang9 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐9 小时前
最大堆和最小堆 实现思路
java·开发语言·算法