Pre Merge pull request !346 from ltyzzz/jsencrypt

This commit is contained in:
ltyzzz 2023-06-25 08:14:50 +00:00 committed by Gitee
commit 0e3d3b29fc
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
9 changed files with 360 additions and 21 deletions

View File

@ -197,6 +197,11 @@ mybatis-encryptor:
publicKey:
privateKey:
# 接口加密
interface-encryptor:
enable: true
secret: abcdefghijklmnop
# Swagger配置
swagger:
info:

View File

@ -0,0 +1,85 @@
package com.ruoyi.common.filter;
import cn.hutool.core.io.IoUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.encrypt.EncryptContext;
import com.ruoyi.common.encrypt.encryptor.AesEncryptor;
import com.ruoyi.common.enums.AlgorithmType;
import com.ruoyi.common.enums.EncodeType;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
/**
* 加解密
*
* @author ltyzzz
* @email ltyzzz2000@gmail.com
* @date 2023/4/27 01:15
*/
public class EncryptFilter implements Filter {
private String secret;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
secret = filterConfig.getInitParameter("secret");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
String uri = req.getRequestURI();
System.out.println("加解密过滤器," + uri);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setPassword(secret);
encryptContext.setAlgorithm(AlgorithmType.AES);
encryptContext.setEncode(EncodeType.BASE64);
AesEncryptor aesEncryptor = new AesEncryptor(encryptContext);
byte[] body;
if (req.getContentLength() > 0) {
String reqBody = getRequestBody(req);
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.readValue(reqBody, Map.class);
String encryptData = (String) map.get("encryptData");
String data = aesEncryptor.decrypt(encryptData);
System.out.println(data);
body = data.getBytes();
} else {
body = IoUtil.readBytes(request.getInputStream(), false);
}
EncryptRequestWrapper reqWrapper = new EncryptRequestWrapper(req, body);
EncryptResponseWrapper respWrapper = new EncryptResponseWrapper(resp);
chain.doFilter(reqWrapper, respWrapper);
byte[] resData = respWrapper.getResponseData();
String encrypt = aesEncryptor.encrypt(new String(resData), EncodeType.BASE64);
PrintWriter out = response.getWriter();
out.print(encrypt);
out.flush();
out.close();
}
@Override
public void destroy() {
Filter.super.destroy();
}
private String getRequestBody(ServletRequest request) throws IOException {
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
}

View File

@ -0,0 +1,68 @@
package com.ruoyi.common.filter;
import com.ruoyi.common.encrypt.encryptor.AesEncryptor;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author ltyzzz
* @email ltyzzz2000@gmail.com
* @date 2023/4/27 20:58
*/
public class EncryptRequestWrapper extends HttpServletRequestWrapper {
private HttpServletRequest request;
private String secret;
private AesEncryptor aesEncryptor;
private byte[] body;
public EncryptRequestWrapper(HttpServletRequest request, byte[] body) throws IOException {
super(request);
this.body = body;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new ServletInputStream() {
private final ByteArrayInputStream inputStream = new ByteArrayInputStream(body);
@Override
public int read() throws IOException {
return inputStream.read();
}
@Override
public int available() throws IOException {
return body.length;
}
@Override
public boolean isFinished() {
return inputStream.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}

View File

@ -0,0 +1,91 @@
package com.ruoyi.common.filter;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
/**
* @author ltyzzz
* @email ltyzzz2000@gmail.com
* @date 2023/4/28 00:15
*/
public class EncryptResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer = null;
private ServletOutputStream out = null;
private PrintWriter writer = null;
public EncryptResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
buffer = new ByteArrayOutputStream();
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
@Override
public PrintWriter getWriter() throws UnsupportedEncodingException {
return writer;
}
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
@Override
public void reset() {
buffer.reset();
}
/**
* 将outwriter中的数据强制输出到WapperedResponse的buffer里面否则取不到数据
*/
public byte[] getResponseData() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
/**
* 内部类对ServletOutputStream进行包装
*/
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WapperedOutputStream(ByteArrayOutputStream stream)
throws IOException {
bos = stream;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
}
}

View File

@ -1,8 +1,10 @@
package com.ruoyi.framework.config;
import com.ruoyi.common.filter.EncryptFilter;
import com.ruoyi.common.filter.RepeatableFilter;
import com.ruoyi.common.filter.XssFilter;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.InterfaceEncryptProperties;
import com.ruoyi.framework.config.properties.XssProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -25,6 +27,9 @@ public class FilterConfig {
@Autowired
private XssProperties xssProperties;
@Autowired
private InterfaceEncryptProperties interfaceEncryptProperties;
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
@ -52,4 +57,19 @@ public class FilterConfig {
return registration;
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
@ConditionalOnProperty(value = "interface-encryptor.enable", havingValue = "true")
public FilterRegistrationBean encryptFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new EncryptFilter());
registration.addUrlPatterns("/*");
registration.setName("encryptFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("secret", interfaceEncryptProperties.getSecret());
registration.setInitParameters(initParameters);
return registration;
}
}

View File

@ -0,0 +1,26 @@
package com.ruoyi.framework.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author ltyzzz
* @email ltyzzz2000@gmail.com
* @date 2023/4/27 16:55
*/
@Data
@Component
@ConfigurationProperties(prefix = "interface-encryptor")
public class InterfaceEncryptProperties {
/**
* 过滤开关
*/
private Boolean enable;
/**
* 密钥
*/
private String secret;
}

View File

@ -39,6 +39,7 @@
"axios": "0.24.0",
"clipboard": "2.0.8",
"core-js": "3.25.3",
"crypto-js": "^4.1.1",
"echarts": "5.4.0",
"element-ui": "2.15.13",
"file-saver": "2.0.5",

View File

@ -0,0 +1,20 @@
import CryptoJS from "crypto-js";
const key = CryptoJS.enc.Utf8.parse("abcdefghijklmnop")
// 加密
export function encryptAES(data) {
var encrypted = CryptoJS.AES.encrypt(data, key, {
"mode": CryptoJS.mode.ECB,
"padding": CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
// 解密
export function decryptAES(ciphertext) {
var decrypted = CryptoJS.AES.decrypt(ciphertext, key, {
"mode": CryptoJS.mode.ECB,
"padding": CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
}

View File

@ -1,15 +1,16 @@
import axios from 'axios'
import { Notification, MessageBox, Message, Loading } from 'element-ui'
import {Notification, MessageBox, Message, Loading} from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
import {getToken} from '@/utils/auth'
import errorCode from '@/utils/errorCode'
import { tansParams, blobValidate } from "@/utils/ruoyi";
import {tansParams, blobValidate} from "@/utils/ruoyi";
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import {saveAs} from 'file-saver'
import {encryptAES, decryptAES} from "@/utils/crypto";
let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = { show: false };
export let isRelogin = {show: false};
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 对应国际化资源文件后缀
@ -21,6 +22,8 @@ const service = axios.create({
// 超时
timeout: 10000
})
// 是否需要加密请求
const isEncrypt = true
// request拦截器
service.interceptors.request.use(config => {
@ -61,43 +64,57 @@ service.interceptors.request.use(config => {
}
}
}
if (isEncrypt && config.data) {
let s = JSON.stringify(config.data)
let encryptData = encryptAES(s)
config.data = {
encryptData: encryptData
}
}
return config
}, error => {
console.log(error)
Promise.reject(error)
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
if (isEncrypt) {
res.data = JSON.parse(decryptAES(res.data));
}
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
return res.data
}
if (code === 401) {
if (!isRelogin.show) {
isRelogin.show = true;
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
confirmButtonText: '重新登录',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
isRelogin.show = false;
store.dispatch('LogOut').then(() => {
location.href = process.env.VUE_APP_CONTEXT_PATH + "index";
})
}).catch(() => {
isRelogin.show = false;
});
}
}).catch(() => {
isRelogin.show = false;
});
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
} else if (code === 500) {
Message({ message: msg, type: 'error' })
Message({message: msg, type: 'error'})
return Promise.reject(new Error(msg))
} else if (code === 601) {
Message({ message: msg, type: 'warning' })
Message({message: msg, type: 'warning'})
return Promise.reject('error')
} else if (code !== 200) {
Notification.error({ title: msg })
Notification.error({title: msg})
return Promise.reject('error')
} else {
return res.data
@ -105,7 +122,7 @@ service.interceptors.response.use(res => {
},
error => {
console.log('err' + error)
let { message } = error;
let {message} = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
@ -113,17 +130,23 @@ service.interceptors.response.use(res => {
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Message({ message: message, type: 'error', duration: 5 * 1000 })
Message({message: message, type: 'error', duration: 5 * 1000})
return Promise.reject(error)
}
)
// 通用下载方法
export function download(url, params, filename, config) {
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
downloadLoadingInstance = Loading.service({
text: "正在下载数据,请稍候",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)",
})
return service.post(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
transformRequest: [(params) => {
return tansParams(params)
}],
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
responseType: 'blob',
...config
}).then(async (data) => {