# 常见问题

# 如何新增系统图标

如果你没有在本项目 Icon (opens new window) 中找到需要的图标,可以到 iconfont.cn (opens new window) 上选择并生成自己的业务图标库,再进行使用。或者其它 svg 图标网站,下载 svg 并放到文件夹之中就可以了。

下载完成之后将下载好的 .svg 文件放入 @/icons/svg 文件夹下之后就会自动导入。

使用方式

<svg-icon icon-class="password" /> // icon-class 为 icon 的名字
1

提示

菜单图标会自动引入@/icons/svg,放入此文件夹中图标就可以选择了

# 如何不登录直接访问

在 SecurityConfig 中设置httpSecurity 配置匿名访问

// 使用 permitAll() 方法所有人都能访问,包括带上 token 访问
.antMatchers("/admins/**").permitAll()

// 使用 anonymous() 所有人都能访问,但是带上 token 访问后会报错
.antMatchers("/admins/**").anonymous()
1
2
3
4
5

提示

匿名访问方法@PreAuthorize权限注解也需要去掉。

前端不登录如何直接访问

如果是前端页面可以在src/permission.js配置whiteList属性白名单即可。

# 如何更换项目包路径

参考如何更换项目包路径

# 业务模块访问出现404

参考业务模块访问出现404

# 系统接口访问出现401

在测试系统接口中可能存在一些接口用到用户信息或权限验证,此时需要添加全局的token参数。如图

swagger

token是在登录成功后返回的,可以在浏览器通过F12查看Network中的请求地址,对应参数Authorization。复制截图内容到swagger全局Authorization属性value参数中,点击Authorize,以后每次访问接口会携带此token信息。

swagger

# 如何更换后端请求地址

vue.config.js中,修改target值为对应的的后端接口地址。

devServer: {
  ...,
  proxy: {
    [process.env.VUE_APP_BASE_API]: {
      target: `http://localhost:8080`,
      ...
    }
  },
  ...
},
1
2
3
4
5
6
7
8
9
10

# 如何获取用户登录信息

  1. 第一种方法
// 获取当前的用户名称
String userName = SecurityUtils.getUsername();
1
2

2、缓存获取当前用户信息

@Autowired
private TokenService tokenService;
	
LoginUser loginUser = tokenService.getLoginUser();
// 获取当前的用户名称
String userName = loginUser.getUsername();
1
2
3
4
5
6

3、vue中获取当前用户信息

var userName = this.$store.state.user.name;
1

# 提示您没有数据的权限

这种情况都属于权限标识配置不对在菜单管理配置好权限标识(菜单&按钮)

  1. 确认此用户是否已经配置角色
  2. 确认此角色是否已经配置菜单权限
  3. 确认此菜单权限标识是否和后台代码一致

如参数管理
后台配置@PreAuthorize("@ss.hasPermi('system:config:query')")对应参数管理权限标识为system:config:query

注:如需要角色权限,配置角色权限字符 使用@PreAuthorize("@ss.hasRole('admin')")

# 如何防止请求重复提交

后端可以通过@RepeatSubmit注解控制

/**
 * 在对应方法添加注解 @RepeatSubmit
 */
@RepeatSubmit
public AjaxResult edit()
1
2
3
4
5

# 异步处理获取用户信息

项目中可以通过SecurityContextHolder.getContext().getAuthentication()获取用户信息,例如

LoginUser loginUser = SecurityUtils.getLoginUser()
1

绝大多数情况下都是通过同步的方式来获取用户信息,如果通过异步获取还需要添加AsyncConfigurerSupport处理。

// 启动类上面添加,开启异步调用
@EnableAsync
// 方法上面添加,异步执行
@Async
1
2
3
4
package com.ruoyi.framework.config;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;

@Configuration
public class AsyncConfig extends AsyncConfigurerSupport
{
    /**
     * 异步执行需要使用权限框架自带的包装线程池 保证权限信息的传递
     */
    @Override
    public Executor getAsyncExecutor()
    {
        return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 前端如何配置后端接口

对于特殊情况,需要直接调用后台接口或者指定域名可以修改.env.production文件VUE_APP_BASE_API属性

# 后端接口地址
VUE_APP_BASE_API = '//localhost:8080'
1
2

# 图片上传成功不能显示

文件上传成功后,请求访问后台地址会根据profile进行匹配,需要自己配置nginx代理,参考如下。

location /profile/ {
    # 方式一:指向地址
    proxy_pass http://127.0.0.1:9999/profile/; 
}
1
2
3
4
location /profile/
{
    # 方式二:指向目录,对应后台`application.yml`中的`profile`配置
    alias /home/ruoyi/uploadPath/;
}
1
2
3
4
5

# 进入首页如何默认记忆控制台

例如用户退出后,下次登陆系统,能默认打开之前工作路径。

可以在request.js,修改LogOut

store.dispatch('LogOut').then(() => {
  location.href = '/index';
})
1
2
3

换成

store.dispatch('LogOut').then(() => {
  location.reload();
})
1
2
3

# 解决node-sass安装失败

node-sass 安装失败的原因 npm 安装 node-sass 依赖时,会从 github.com 上下载 .node 文件。由于国内网络环境的问题,这个下载时间可能会很长,甚至导致超时失败。 这是使用 sass 的同学可能都会遇到的郁闷的问题。

解决方案就是使用其他源,或者使用工具下载,然后将安装源指定到本地。

解决方法一:使用淘宝镜像源(推荐)
设置变量 sass_binary_site,指向淘宝镜像地址。示例

npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

// 也可以设置系统环境变量的方式。示例
// linux、mac 下
SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ npm install node-sass

// window 下
set SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ && npm install node-sass
1
2
3
4
5
6
7
8

或者设置全局镜像源:

npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/
1

之后再涉及到 node-sass 的安装时就会从淘宝镜像下载。

解决方法二:使用 cnpm
使用 cnpm 安装 node-sass 会默认从淘宝镜像源下载,也是一个办法:

cnpm install node-sass
1

解决方法三:创建.npmrc文件
在项目根目录创建.npmrc文件,复制下面代码到该文件。

phantomjs_cdnurl=http://cnpmjs.org/downloads
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
registry=https://registry.npm.taobao.org
1
2
3

保存后 删除之前安装失败的包(第一次安装请跳过此步)

npm uninstall node-sass
1

重新安装

npm install node-sass
1

# 浏览器兼容性问题需求

本项目暂时没有兼容性需求,如有兼容性需求可自行使用 babel-polyfill。

// 下载依赖
npm install --save babel-polyfill
1
2

在入口文件中引入

import 'babel-polyfill'
// 或者
require('babel-polyfill') //es6
1
2
3

在 webpack.config.js 中加入 babel-polyfill 到你的入口数组:

module.exports = {
  entry: ['babel-polyfill', './app/js']
}
1
2
3

具体可参考 link (opens new window)

或者更简单暴力 polyfill.io (opens new window) 使用它给的一个 cdn 地址,引入这段 js 之后它会自动判断游览器,加载缺少的那部分 polyfill,但国内速度肯能不行,大家可以自己搭 cdn。

# 如何分析构建文件体积

如果你的构建文件很大,你可以通过 webpack-bundle-analyzer 命令构建并分析依赖模块的体积分布,从而优化你的代码。

npm run preview -- --report
1

运行之后你就可以在 http://localhost:9526/report.html (opens new window) 页面看到具体的体积分布

具体的优化可以参考 Webpack 大法之 Code Splitting (opens new window)

TIP

强烈建议开启 gzip ,使用之后普遍体积只有原先 1/3 左右。打出来的 app.js 过大,查看一下是不是 Uglify 配置不正确或者 sourceMap 没弄对。 优化相关请看该 Webpack Freestyle 之 Long Term Cache (opens new window)

# 模态框点击空白不消失

设置属性:close-on-click-modal="false"

<el-dialog  :close-on-click-modal="false"></el-dialog>
1

# 如何支持多类型数据库

参考如何支持多类型数据库

# 如何降低mysql驱动版本

参考如何降低mysql驱动版本

# 如何配置tomcat访问日志

参考如何配置tomcat访问日志

# 如何汉化系统接口Swagger

参考如何汉化系统接口Swagger

# 如何Excel导出子对象多个字段

参考如何Excel导出子对象多个字段

# Tomcat部署多个War包项目异常

参考Tomcat部署多个War包项目异常

# Tomcat临时目录tmp抛错误异常

参考Tomcat临时目录tmp抛错误异常

# 如何部署配置支持https访问

参考如何部署配置支持https访问

# 特殊字符串被过滤的解决办法

参考特殊字符串被过滤的解决办法

# Linux系统验证码乱码解决方法

参考Linux系统验证码乱码解决方法

# 如何处理Long类型精度丢失问题

如何处理Long类型精度丢失问题

# 如何修改Swagger默认访问地址

由于采用的前后端分离模式,且前端Swagger使用的iframe打开页面。所以默认请求的是前端地址,然后前端通过代理转发到后端接口。对于特殊情况需要直接请求后端提供如下方案:

方案1:使用新窗口打开,不要用iframe打开。因为swagger默认是获取当前服务的地址。

方案2:在SwaggerConfig配置中createRestApi方法设置后端的地址。

return new Docket(DocumentationType.SWAGGER_2)
    ....
	// 后端地址
    .host("localhost:8080")
1
2
3
4

# 如何修改超级管理员登录密码

1、如果是自己知道超级管理员的密码且需要修改的情况。
默认口令 admin/admin123,可以登录后在首页个人中心修改密码。

2、如果自己忘记了超级管理员的密码可以重新生成秘钥替换数据库密码。

public static void main(String[] args)
{
	System.out.println(SecurityUtils.encryptPassword("admin123"));
}
1
2
3
4

# 如何格式化前端日期时间戳内容

对应一些时间格式需要在前端进行格式化操作情况,解决方案如下

1、后端使用JsonFormat注解格式化日期,时间戳yyyy-MM-dd HH:mm:ss

/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date time;
1
2
3

2、前端使用parseTime方法格式化日期,时间戳{y}-{m}-{d} {h}:{i}:{s}

<el-table-column label="创建时间" align="center" prop="createTime" width="160">
	<template slot-scope="scope">
	  <span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
	</template>
</el-table-column>
1
2
3
4
5

# 如何限制账户不允许多终端登录

这本来是一个可有可无的问题,不过经常有小伙伴有这样的需求。废话不多说,先来看同一用户不同终端限制登录的解决方法。方法很简单,大致思路就是做出userid与token(一个用户对应一个token,userid唯一)的键值对,存于缓存中。用于登录时判断用户是否在别的终端在线。详细实现代码如下:

1、application.yml新增一个配置soloLogin用于限制多终端同时登录。

# token配置
token:
    # 是否允许账户多终端同时登录(true允许 false不允许)
    soloLogin: false
1
2
3
4

2、Constants.java新增一个常量LOGIN_USERID_KEY公用

/**
 * 登录用户编号 redis key
 */
public static final String LOGIN_USERID_KEY = "login_userid:";
1
2
3
4

3、调整TokenService.java,存储&刷新缓存用户编号信息

// 是否允许账户多终端同时登录(true允许 false不允许)
@Value("${token.soloLogin}")
private boolean soloLogin;

/**
 * 删除用户身份信息
 */
public void delLoginUser(String token, Long userId)
{
	if (StringUtils.isNotEmpty(token))
	{
		String userKey = getTokenKey(token);
		redisCache.deleteObject(userKey);
	}
	if (!soloLogin && StringUtils.isNotNull(userId))
	{
		String userIdKey = getUserIdKey(userId);
		redisCache.deleteObject(userIdKey);
	}
}

/**
 * 刷新令牌有效期
 * 
 * @param loginUser 登录信息
 */
public void refreshToken(LoginUser loginUser)
{
	loginUser.setLoginTime(System.currentTimeMillis());
	loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
	// 根据uuid将loginUser缓存
	String userKey = getTokenKey(loginUser.getToken());
	redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
	if (!soloLogin)
	{
		// 缓存用户唯一标识,防止同一帐号,同时登录
		String userIdKey = getUserIdKey(loginUser.getUser().getUserId());
		redisCache.setCacheObject(userIdKey, userKey, expireTime, TimeUnit.MINUTES);
	}
}

private String getUserIdKey(Long userId)
{
	return Constants.LOGIN_USERID_KEY + userId;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

4、自定义退出处理类LogoutSuccessHandlerImpl.java清除缓存方法添加用户编号

// 删除用户缓存记录
tokenService.delLoginUser(loginUser.getToken(), loginUser.getUser().getUserId());
1
2

5、登录方法SysLoginService.java,验证如果用户不允许多终端同时登录,清除缓存信息

// 是否允许账户多终端同时登录(true允许 false不允许)
@Value("${token.soloLogin}")
private boolean soloLogin;
	
if (!soloLogin)
{
	// 如果用户不允许多终端同时登录,清除缓存信息
	String userIdKey = Constants.LOGIN_USERID_KEY + loginUser.getUser().getUserId();
	String userKey = redisCache.getCacheObject(userIdKey);
	if (StringUtils.isNotEmpty(userKey))
	{
		redisCache.deleteObject(userIdKey);
		redisCache.deleteObject(userKey);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 前端静态资源如何整合到后端访问

分离版本都是前端和后端单独部署的,但是有些特殊情况想把前端静态资源整合到后端。提供如下方案:

1、修改ruoyi-ui中的.env.production(二选一)

// 本机地址访问
VUE_APP_BASE_API = '/'
1
2
// 任意地址访问
VUE_APP_BASE_API = '//localhost:8080'
1
2

2、修改ruoyi-ui中的router/index.js,设置mode属性为hash

export default new Router({
  mode: 'hash',
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})
1
2
3
4
5

3、执行bin\build.bat打包前端静态资源文件。

4、修改后端resources中的application.yml,添加thymeleaf模板引擎配置

spring:
  # 模板引擎
  thymeleaf:
    mode: HTML
    encoding: utf-8
    cache: false
1
2
3
4
5
6

5、修改后端pom.xml,增加thymeleaf模板引擎依赖

<!-- spring-boot-thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
1
2
3
4
5

6、修改后端ResourcesConfig.java中的addResourceHandlers,添加静态资源映射地址

/** 前端静态资源配置 */
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
1
2

7、修改后端SecurityConfig.java中的configure,添加允许访问的地址。

.antMatchers(
		HttpMethod.GET,
		"/*.html",
		"/**/*.html",
		"/**/*.css",
		"/**/*.js",
		"/static/**",
		"/",
		"/index"
).permitAll()
1
2
3
4
5
6
7
8
9
10

8、后端新建访问控制处理IndexController.java设置对应访问页面。

package com.ruoyi.project.system.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController
{
    // 系统首页
    @GetMapping(value = { "/", "/index", "/login" })
    public String index()
    {
        return "index";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

9、整合前端dist静态资源文件到后端

  • 后端resources下新建templates目录,复制静态页面index.html过来。

  • 复制静态文件staticresources目录下。

10、启动测试访问地址

打开浏览器,输入:http://localhost:8080 能正常访问和登录表示成功。

# 使用Velocity模板引擎兼容$符号

使用Velocity模板引擎兼容$符号

# 登录密码如何使用加密传输方式

目前登录接口密码是明文传输,如果安全性有要求,可以调整成加密方式传输。参考如下

1、修改前端login.js对密码进行rsa加密。

import { encrypt } from '@/utils/jsencrypt'

export function login(username, password, code, uuid) {
  password = encrypt(password);
  .........
}
1
2
3
4
5
6

2、工具类sign包下添加RsaUtils.java,用于RSA加密解密。

package com.ruoyi.common.utils.sign;

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * RSA加密解密
 * 
 * @author ruoyi
 **/
public class RsaUtils
{
    // Rsa 私钥
    public static String privateKey = "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY"
            + "7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN"
            + "PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA"
            + "kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow"
            + "cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv"
            + "DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh"
            + "YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3" 
            + "UP8iWi1Qw0Y=";

    /**
     * 私钥解密
     *
     * @param privateKeyString 私钥
     * @param text 待解密的文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(String text) throws Exception
    {
        return decryptByPrivateKey(privateKey, text);
    }

    /**
     * 公钥解密
     *
     * @param publicKeyString 公钥
     * @param text 待解密的信息
     * @return 解密后的文本
     */
    public static String decryptByPublicKey(String publicKeyString, String text) throws Exception
    {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 私钥加密
     *
     * @param privateKeyString 私钥
     * @param text 待加密的信息
     * @return 加密后的文本
     */
    public static String encryptByPrivateKey(String privateKeyString, String text) throws Exception
    {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * 私钥解密
     *
     * @param privateKeyString 私钥
     * @param text 待解密的文本
     * @return 解密后的文本
     */
    public static String decryptByPrivateKey(String privateKeyString, String text) throws Exception
    {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(Base64.decodeBase64(text));
        return new String(result);
    }

    /**
     * 公钥加密
     *
     * @param publicKeyString 公钥
     * @param text 待加密的文本
     * @return 加密后的文本
     */
    public static String encryptByPublicKey(String publicKeyString, String text) throws Exception
    {
        X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyString));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.encodeBase64String(result);
    }

    /**
     * 构建RSA密钥对
     *
     * @return 生成后的公私钥信息
     */
    public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException
    {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
        String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded());
        String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
        return new RsaKeyPair(publicKeyString, privateKeyString);
    }

    /**
     * RSA密钥对对象
     */
    public static class RsaKeyPair
    {
        private final String publicKey;
        private final String privateKey;

        public RsaKeyPair(String publicKey, String privateKey)
        {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }

        public String getPublicKey()
        {
            return publicKey;
        }

        public String getPrivateKey()
        {
            return privateKey;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

3、登录方法SysLoginService.java,对密码进行rsa解密。

// 关键代码 RsaUtils.decryptByPrivateKey(password)
authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, RsaUtils.decryptByPrivateKey(password)));
1
2

访问 http://localhost/login (opens new window) 登录页面。提交时检查密码是否为加密传输,且后台也能正常解密。

# 如何解决多数据源事务的一致性

参考如何解决多数据源事务的一致性

# 登录出现DatatypeConverter异常

错误提示:Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: javax/xml/bind/DatatypeConverter

由于>= jdk9中不再包含这个jar包,所以需要在ruoyi-framework\pom.xml手动添加依赖。

<dependency>
	<groupId>javax.xml.bind</groupId>
	<artifactId>jaxb-api</artifactId>
	<version>2.3.0</version>
</dependency>
1
2
3
4
5