在基于若依 (Ruoyi) 框架开发微信绑定功能时,不少开发者会遇到一个常见问题:微信用户的昵称存入数据库后出现乱码(例如显示为杨、😀等乱码字符)。这种问题看似是编码格式不匹配,但实际排查过程中需要兼顾数据传输、框架配置和数据库存储三个层面。本文将以实际项目为例,详细分析乱码产生的原因,并提供可落地的解决方案。

一、问题现象:微信昵称存入数据库后乱码

在若依项目中开发微信绑定功能时,通过微信接口获取用户昵称后,调用updateUserOpenid方法更新到sys_user表的wx_nick_name字段,结果数据库中该字段显示为乱码。具体表现为:

  • 日志中打印的微信昵称原始数据已乱码(例如:微信返回的原始昵称(未处理):杨);
  • 数据库中wx_nick_name字段值为类似阿里的乱码字符;
  • 前端展示时,从数据库读取的昵称同样乱码。

二、根因分析:三层编码不匹配导致乱码

乱码问题的核心是字符编码在传输 / 存储过程中出现不一致。结合若依框架和微信接口的特性,可从以下三个层面排查:

1. 数据库字符集不支持特殊字符(最常见原因)

若依框架默认的数据库表可能使用utf8字符集,但utf8在 MySQL 中仅支持3 字节以内的 UTF-8 编码,而微信昵称中可能包含 emoji(如😊)、生僻字等4 字节 UTF-8 字符,此时utf8无法正确存储,导致乱码。

从提供的表结构来看,wx_nick_name字段的定义为:

`wx_nick_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '微信昵称'

其中utf8_bin属于utf8字符集的排序规则,无法支持 4 字节字符,这是乱码的潜在原因之一。

2. 微信接口响应解析编码错误

微信接口(如https://api.weixin.qq.com/sns/userinfo)返回的用户信息是UTF-8 编码的 JSON,但如果后端解析响应时使用了错误的编码格式(如默认的ISO-8859-1),会导致昵称在获取阶段就已乱码。

在项目代码中,通过RestTemplate调用微信接口时,若未指定编码格式,RestTemplate默认会用ISO-8859-1解析响应体,导致 UTF-8 编码的昵称被错误解码,例如:

// 错误示例:未配置编码的RestTemplate
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> userResponse = restTemplate.getForEntity(userInfoUrl, String.class);
JSONObject userJson = JSONObject.parseObject(userResponse.getBody()); // 此时body已乱码

3. 若依框架的 XSS 过滤或字符转义影响

若依框架默认启用了 XSS 过滤功能(通过@Xss注解),用于防止跨站脚本攻击。如果微信昵称中包含特殊字符(如<、>),可能被 XSS 过滤器转义,间接导致编码异常。

在SysUser实体类中,wxNickName字段未添加@Xss注解,看似不受影响,但需确认框架全局过滤器是否对所有字符串字段进行强制转义。

三、解决方案:三层编码统一与适配

针对上述原因,需从数据库存储、接口解析、框架配置三个层面逐一修复,确保编码一致。

1. 数据库层:升级字符集至 utf8mb4

utf8mb4是 MySQL 支持完整 UTF-8 编码的字符集(包括 4 字节字符),需修改表字段和数据库连接参数:

(1)修改wx_nick_name字段字符集

执行 SQL 将字段字符集改为utf8mb4:

ALTER TABLE `sys_user` 
MODIFY COLUMN `wx_nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '微信昵称';
  • utf8mb4_unicode_ci是推荐的排序规则,支持多语言排序,且兼容 emoji。
(2)修改数据库连接参数

在若依框架的application-druid.yml(或application.yml)中,确保数据库连接 URL 包含characterEncoding=utf8mb4:

spring:
  datasource:
    druid:
      url: jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai&allowMultiQueries=true
  • 必须同时指定useUnicode=true和characterEncoding=utf8mb4,确保 Java 与数据库的编码一致。

2. 接口解析层:配置 RestTemplate 使用 UTF-8 编码

修复微信接口响应的解析编码,强制RestTemplate用 UTF-8 处理响应体:

(1)自定义 RestTemplate 配置

在若依框架的配置类中(如WebMvcConfig),定义一个全局的RestTemplate Bean,覆盖默认的消息转换器编码:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        // 遍历消息转换器,将StringHttpMessageConverter的编码改为UTF-8
        List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters();
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8);
                break;
            }
        }
        return restTemplate;
    }
}
(2)注入自定义 RestTemplate

在SysUserServiceImpl中,注入配置好的RestTemplate,替代原来的new RestTemplate():

@Service
public class SysUserServiceImpl implements ISysUserService {
    @Autowired
    private RestTemplate restTemplate; // 注入配置好的RestTemplate

    @Override
    public SysUser getOpenid(String code) {
        try {
            // 调用微信接口时使用注入的restTemplate
            ResponseEntity<String> response = restTemplate.getForEntity(accessTokenUrl, String.class);
            // ... 后续解析逻辑不变
        } catch (Exception e) {
            log.error("微信接口调用异常", e);
            return null;
        }
    }
}

3. 框架适配层:排除微信昵称的 XSS 过滤(可选)

若微信昵称中包含特殊字符(如&、#),可能被若依的 XSS 过滤器转义。可通过以下方式排除过滤:

(1)在实体类中跳过 XSS 过滤

在SysUser的wxNickName字段上添加@Xss(ignore = true)(若依框架支持):

public class SysUser extends BaseEntity {
    // ... 其他字段
    /** 微信昵称 */
    @Excel(name = "微信昵称")
    @Xss(ignore = true) // 跳过XSS过滤
    private String wxNickName;
}
(2)在全局过滤器中排除 URL

若通过接口传输昵称,可在XssFilter中排除微信绑定相关接口:

// 若依的XssFilter配置类
public class XssFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        HttpServletRequest req = (HttpServletRequest) request;
        String url = req.getRequestURI();
        // 排除微信绑定接口
        if (url.contains("/common/wx/bind-openid")) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(new XssHttpServletRequestWrapper(req), response);
        }
    }
}

四、验证方案:确认乱码问题已解决

修复后,通过以下步骤验证效果:

  1. 查看日志:确认log.info("微信返回的原始昵称(未处理):{}", nickname);打印的昵称正常(如 “张三😊”);
  2. 检查数据库:直接查询sys_user表,wx_nick_name字段应显示正确昵称(包括 emoji);
  3. 前端展示:调用checkBind接口,前端应正确显示微信昵称。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐