大连地区建设网站,大地seo,广州网络推广培训,waf防火墙排名44 服务监控讲解
1 需求 显示CPU、内存、服务器信息、Java虚拟机信息、磁盘状态的信息
2 前端
RuoYi-Vue\ruoyi-ui\src\views\monitor\server\index.vue script
import { getServer } from /api/monitor/server;export default {name: Serverscript
import { getServer } from /api/monitor/server;export default {name: Server,data() {return {// 服务器信息server: []};},created() {this.getList();this.openLoading();},methods: {/** 查询服务器信息 */getList() {getServer().then(response {this.server response.data;this.$modal.closeLoading();});},// 打开加载层openLoading() {this.$modal.loading(正在加载服务监控数据请稍候);}}
};
/script RuoYi-Vue\ruoyi-ui\src\api\monitor\server.js import request from /utils/request// 获取服务信息
export function getServer() {return request({url: /monitor/server,method: get})
}
3 后端
RuoYi-Vue\pom.xml引入开源框架获取cpu、内存、磁盘等信息。 !-- 获取系统信息 --
dependencygroupIdcom.github.oshi/groupIdartifactIdoshi-core/artifactIdversion${oshi.version}/version
/dependency RuoYi-Vue\ruoyi-framework\pom.xml引入开源框架获取cpu、内存、磁盘等信息。 !-- 获取系统信息 --
dependencygroupIdcom.github.oshi/groupIdartifactIdoshi-core/artifactId
/dependency ServerController#getInfo /*** 服务器监控* * author ruoyi*/
RestController
RequestMapping(/monitor/server)
public class ServerController
{PreAuthorize(ss.hasPermi(monitor:server:list))GetMapping()public AjaxResult getInfo() throws Exception{// 实例化Server server new Server();// 设置相关的值server.copyTo();// 返回给前端return AjaxResult.success(server);}
} Server返回数据 /*** 服务器相关信息* * author ruoyi*/
public class Server
{private static final int OSHI_WAIT_SECOND 1000;/*** CPU相关信息*/private Cpu cpu new Cpu();/*** 內存相关信息*/private Mem mem new Mem();/*** JVM相关信息*/private Jvm jvm new Jvm();/*** 服务器相关信息*/private Sys sys new Sys();/*** 磁盘相关信息*/private ListSysFile sysFiles new LinkedListSysFile();
} Server#copyTo数据填充 /*** 数据填充*/public void copyTo() throws Exception{// SystemInfo框架apiSystemInfo si new SystemInfo();// SystemInfo框架apiHardwareAbstractionLayer hal si.getHardware();// cpu信息大多从开源框架的API中获取setCpuInfo(hal.getProcessor());// 内存信息setMemInfo(hal.getMemory());// 服务器信息setSysInfo();// 虚拟机信息jdk的APIsetJvmInfo();// 磁盘信息setSysFiles(si.getOperatingSystem());} Cpu.java /*** CPU相关信息* * author ruoyi*/
public class Cpu
{/*** 核心数*/private int cpuNum;/*** CPU总的使用率*/private double total;/*** CPU系统使用率*/private double sys;/*** CPU用户使用率*/private double used;/*** CPU当前等待率*/private double wait;/*** CPU当前空闲率*/private double free;
} Jvm.java /*** JVM相关信息* * author ruoyi*/
public class Jvm
{/*** 当前JVM占用的内存总数(M)*/private double total;/*** JVM最大可用内存总数(M)*/private double max;/*** JVM空闲内存(M)*/private double free;/*** JDK版本*/private String version;/*** JDK路径*/private String home;
} Mem.java /*** 內存相关信息* * author ruoyi*/
public class Mem
{/*** 内存总量*/private double total;/*** 已用内存*/private double used;/*** 剩余内存*/private double free;
} Sys.java /*** 系统相关信息* * author ruoyi*/
public class Sys
{/*** 服务器名称*/private String computerName;/*** 服务器Ip*/private String computerIp;/*** 项目路径*/private String userDir;/*** 操作系统*/private String osName;/*** 系统架构*/private String osArch;
} SysFile.java /*** 系统文件相关信息* * author ruoyi*/
public class SysFile
{/*** 盘符路径*/private String dirName;/*** 盘符类型*/private String sysTypeName;/*** 文件类型*/private String typeName;/*** 总大小*/private String total;/*** 剩余大小*/private String free;/*** 已经使用量*/private String used;/*** 资源的使用率*/private double usage;
} 4 拓展
4 拓展集群 如果要做成集群就需要再扩展一下
需要一个表去控制把服务器的的名称、IP、地址等相关信息入库。然后需要做实时的监控比如使用websocket。 45 系统接口使用详解
1 需求 api文档
2 效果 3 使用
第一步获取token第二步点击 Authorize 配置token第三步传参数调接口
4 拓展 可以使用很多第三方插件把界面优化的更漂亮。
46 系统接口实现详解
1 RuoYi-Vue\pom.xml propertiesswagger.version3.0.0/swagger.version/properties!-- Swagger3依赖 --dependencygroupIdio.springfox/groupIdartifactIdspringfox-boot-starter/artifactIdversion${swagger.version}/versionexclusions!--排除它和前端的UI有冲突--exclusiongroupIdio.swagger/groupIdartifactIdswagger-models/artifactId/exclusion/exclusions/dependency
2 RuoYi-Vue\ruoyi-admin\pom.xml !-- swagger3界面是swagger ui渲染出来的。而sawagger ui的所有页面都在它的jar包中。--dependencygroupIdio.springfox/groupIdartifactIdspringfox-boot-starter/artifactId/dependency!-- 因为与前端ui冲突所以做了排除。并把版本降了一下。--!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 --dependencygroupIdio.swagger/groupIdartifactIdswagger-models/artifactIdversion1.6.2/version/dependency
3 前端
ruoyi-ui\src\views\tool\swagger\index.vue template!--url指定了后台的接口地址 --i-frame :srcurl /
/template
script
import iFrame from /components/iFrame/index;
export default {name: Swagger,components: { iFrame },data() {return {url: process.env.VUE_APP_BASE_API /swagger-ui/index.html};},
};
/script
4 后端
ResourcesConfig#addResourceHandlersswagger ui映射。swagger ui怎么去加载的呢 Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry){/** 本地文件上传路径 */registry.addResourceHandler(Constants.RESOURCE_PREFIX /**).addResourceLocations(file: RuoYiConfig.getProfile() /);/** swagger配置 *//*** 映射配置* /swagger-ui/**访问的地址。* classpath:/META-INF/resources/webjars/springfox-swagger-ui/找到对应的路径在jar包中。* 因为默认情况下swagger ui首页是英文版的但是可以对它做一些国际化的操作。*/registry.addResourceHandler(/swagger-ui/**).addResourceLocations(classpath:/META-INF/resources/webjars/springfox-swagger-ui/).setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());;} SwaggerConfigswagger配置类 package com.ruoyi.web.core.config;import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;/*** Swagger2的接口配置*/
Configuration
public class SwaggerConfig
{/*** 系统基础配置。* 读取项目相关配置文件常用的几个属性。* */Autowiredprivate RuoYiConfig ruoyiConfig;/** 是否开启swagger */Value(${swagger.enabled})private boolean enabled;/*** 设置请求的统一前缀。* 即使用swagger ui页面调用接口进行测试时请求url都会拼接这样的一个前缀。不然就映射不到后台来了因为前端有对应的路由控制。* 为什么需要请求前缀呢因为路由有映射。* 当然这个是可以改的可根据实际情况去调整。* */Value(${swagger.pathMapping})private String pathMapping;/*** 创建API*/Beanpublic Docket createRestApi(){// 版本DocumentationType.OAS_30return new Docket(DocumentationType.OAS_30)// 是否启用Swagger.enable(enabled)// 用来创建该API的基本信息展示在文档的页面中自定义展示的信息.apiInfo(apiInfo())// 设置哪些接口暴露给Swagger展示.select()// 暴露方式1扫描所有有注解的api用这种方式更灵活.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))// 暴露方式2扫描指定包中的swagger注解// .apis(RequestHandlerSelectors.basePackage(com.ruoyi.project.tool.swagger))// 暴露方式3扫描所有 .apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build()/* 设置安全模式swagger可以设置访问token *//*** 默认情况下直接访问会提示没有权限,所以需要设置一下安全模式即页面中的” Authorize按钮 ”。* 点击“ Authorize按钮 ”可以看到接口需要哪些属性。* 可以按实际情况传更多的参数。*/.securitySchemes(securitySchemes()).securityContexts(securityContexts()).pathMapping(pathMapping);}/*** 安全模式这里指定token通过Authorization头请求头传递*/private ListSecurityScheme securitySchemes(){ListSecurityScheme apiKeyList new ArrayListSecurityScheme();apiKeyList.add(new ApiKey(Authorization, Authorization, In.HEADER.toValue()));return apiKeyList;}/*** 安全上下文*/private ListSecurityContext securityContexts(){ListSecurityContext securityContexts new ArrayList();securityContexts.add(SecurityContext.builder().securityReferences(defaultAuth()).operationSelector(o - o.requestMappingPattern().matches(/.*)).build());return securityContexts;}/*** 默认的安全上引用*/private ListSecurityReference defaultAuth(){AuthorizationScope authorizationScope new AuthorizationScope(global, accessEverything);AuthorizationScope[] authorizationScopes new AuthorizationScope[1];authorizationScopes[0] authorizationScope;ListSecurityReference securityReferences new ArrayList();securityReferences.add(new SecurityReference(Authorization, authorizationScopes));return securityReferences;}/*** 添加摘要信息。* 自定义展示的api基本信息。*/private ApiInfo apiInfo(){// 用ApiInfoBuilder进行定制return new ApiInfoBuilder()// 设置标题.title(标题若依管理系统_接口文档)// 描述.description(描述用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...)// 作者信息.contact(new Contact(ruoyiConfig.getName(), null, null))// 版本.version(版本号: ruoyiConfig.getVersion()).build();}
}RuoYiConfig系统基础配置 /*** 读取项目相关配置。* 基础配置对应配置文件中的常用的几个属性。*/
Component
ConfigurationProperties(prefix ruoyi)
public class RuoYiConfig
{/** 项目名称 */private String name;/** 版本 */private String version;/** 版权年份 */private String copyrightYear;/** 实例演示开关 */private boolean demoEnabled;/** 上传路径 */private static String profile;/** 获取地址开关 */private static boolean addressEnabled;/** 验证码类型 */private static String captchaType;/*** 获取导入上传路径*/public static String getImportPath(){return getProfile() /import;}/*** 获取头像上传路径*/public static String getAvatarPath(){return getProfile() /avatar;}/*** 获取下载路径*/public static String getDownloadPath(){return getProfile() /download/;}/*** 获取上传路径*/public static String getUploadPath(){return getProfile() /upload;}
}application.yml # 项目相关配置
ruoyi:# 名称name: RuoYi# 版本version: 3.8.6# 版权年份copyrightYear: 2023# 实例演示开关demoEnabled: true# 文件路径 示例 Windows配置D:/ruoyi/uploadPathLinux配置 /home/ruoyi/uploadPathprofile: D:/ruoyi/uploadPath# 获取ip地址开关addressEnabled: false# 验证码类型 math 数字计算 char 字符验证captchaType: math SwaggerConfig#securitySchemes安全模式这里指定token通过Authorization头请求头传递 /*** 安全模式这里指定token通过Authorization头请求头传递*/private ListSecurityScheme securitySchemes(){ListSecurityScheme apiKeyList new ArrayListSecurityScheme();apiKeyList.add(new ApiKey(Authorization, Authorization, In.HEADER.toValue()));return apiKeyList;} 5 如何汉化系统接口Swagger 想必很多小伙伴都曾经使用过Swagger但是打开UI界面是纯英文的界面并不太友好作为国人还是习惯中文界面。
找到m2/repository/io/springfox/springfox-swagger-ui/x.x.x/springfox-swagger-ui-x.x.x.jar修改对应springfox-swagger-ui-x.x.x.jar包内resources目录下swagger-ui.html添加如下JS代码 !-- 选择中文版 --
script srcwebjars/springfox-swagger-ui/lang/translator.js typetext/javascript/script
script srcwebjars/springfox-swagger-ui/lang/zh-cn.js typetext/javascript/script本地修改结束后在覆盖压缩包文件重启就实现汉化了
6 编码案例 Api(用户信息管理)
public class TestController extends BaseController{// 暴露注解ApiOperation(获取用户列表)// 参数注解/*** name参数* value显示的文本* required是否必填* dataType数据类型* paramType参数类型。如path对应的是getUser(PathVariable Integer userId)中的PathVariable传参方式。* dataTypeClass数据类型*/ApiImplicitParam(name userId, value 用户ID, required true, dataType int, paramType path, dataTypeClass Integer.class)public RUserEntity getUser(PathVariable Integer userId){ListUserEntity userList new ArrayListUserEntity(users.values());return R.ok(userList);}
} 完整代码
/*** swagger 用户测试方法* * author ruoyi*/
Api(用户信息管理)
RestController
RequestMapping(/test/user)
public class TestController extends BaseController
{private final static MapInteger, UserEntity users new LinkedHashMapInteger, UserEntity();{users.put(1, new UserEntity(1, admin, admin123, 15888888888));users.put(2, new UserEntity(2, ry, admin123, 15666666666));}ApiOperation(获取用户列表)GetMapping(/list)public RListUserEntity userList(){ListUserEntity userList new ArrayListUserEntity(users.values());return R.ok(userList);}ApiOperation(获取用户详细)/*** name参数* value显示的文本* required是否必填* dataType数据类型* paramType参数类型。如path对应的是getUser(PathVariable Integer userId)中的PathVariable传参方式。* dataTypeClass数据类型*/ApiImplicitParam(name userId, value 用户ID, required true, dataType int, paramType path, dataTypeClass Integer.class)GetMapping(/{userId})public RUserEntity getUser(PathVariable Integer userId){if (!users.isEmpty() users.containsKey(userId)){return R.ok(users.get(userId));}else{return R.fail(用户不存在);}}ApiOperation(新增用户)ApiImplicitParams({ApiImplicitParam(name userId, value 用户id, dataType Integer, dataTypeClass Integer.class),ApiImplicitParam(name username, value 用户名称, dataType String, dataTypeClass String.class),ApiImplicitParam(name password, value 用户密码, dataType String, dataTypeClass String.class),ApiImplicitParam(name mobile, value 用户手机, dataType String, dataTypeClass String.class)})PostMapping(/save)public RString save(UserEntity user){if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())){return R.fail(用户ID不能为空);}users.put(user.getUserId(), user);return R.ok();}ApiOperation(更新用户)PutMapping(/update)public RString update(RequestBody UserEntity user){if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())){return R.fail(用户ID不能为空);}if (users.isEmpty() || !users.containsKey(user.getUserId())){return R.fail(用户不存在);}users.remove(user.getUserId());users.put(user.getUserId(), user);return R.ok();}ApiOperation(删除用户信息)ApiImplicitParam(name userId, value 用户ID, required true, dataType int, paramType path, dataTypeClass Integer.class)DeleteMapping(/{userId})public RString delete(PathVariable Integer userId){if (!users.isEmpty() users.containsKey(userId)){users.remove(userId);return R.ok();}else{return R.fail(用户不存在);}}
}/*** 参数类型为实体类*/
ApiModel(value UserEntity, description 用户实体)
class UserEntity
{// 注解显示的文本。如果没有这个注解默认使用属性名称。ApiModelProperty(用户ID)private Integer userId;ApiModelProperty(用户名称)private String username;ApiModelProperty(用户密码)private String password;ApiModelProperty(用户手机)private String mobile;public UserEntity(){}public UserEntity(Integer userId, String username, String password, String mobile){this.userId userId;this.username username;this.password password;this.mobile mobile;}// get/set方法
}47 XSS脚本过滤详解
1 XSS攻击的定义 跨站脚本攻击XSS是最普遍的Web应用安全漏洞。
2 模拟xss攻击示例1页面效果 ruoyi-ui\src\views\system\config\index.vuev-text效果正确
!-- 添加或修改参数配置对话框 --el-dialog :titletitle :visible.syncopen width500px append-to-bodyel-form refform :modelform :rulesrules label-width80pxel-form-item label参数名称 propconfigName!--el-input v-modelform.configName placeholder请输入参数名称 /--!-- v-text其实是没有问题的因为它默认会给我们做一些处理。 --div v-textform.configName/div/el-form-itemel-form-item label参数键名 propconfigKeyel-input v-modelform.configKey placeholder请输入参数键名 //el-form-itemel-form-item label参数键值 propconfigValueel-input v-modelform.configValue placeholder请输入参数键值 //el-form-itemel-form-item label系统内置 propconfigTypeel-radio-group v-modelform.configTypeel-radiov-fordict in dict.type.sys_yes_no:keydict.value:labeldict.value{{dict.label}}/el-radio/el-radio-group/el-form-itemel-form-item label备注 propremarkel-input v-modelform.remark typetextarea placeholder请输入内容 //el-form-item/el-formdiv slotfooter classdialog-footerel-button typeprimary clicksubmitForm确 定/el-buttonel-button clickcancel取 消/el-button/div/el-dialog ruoyi-ui\src\views\system\config\index.vuev-html效果不正确。点击出现脚本。这种情况下可能会造成恶意代码没有经过特殊处理的脚本注入到html里面去进行攻击
!-- 添加或修改参数配置对话框 --el-dialog :titletitle :visible.syncopen width500px append-to-bodyel-form refform :modelform :rulesrules label-width80pxel-form-item label参数名称 propconfigName!--el-input v-modelform.configName placeholder请输入参数名称 /--!-- v-text其实是没有问题的因为它默认会给我们做一些处理。 --!--div v-textform.configName/div--!-- v-html有问题。v-html即支持标签为一个HTML类型。 --div v-htmlform.configName/div/el-form-itemel-form-item label参数键名 propconfigKeyel-input v-modelform.configKey placeholder请输入参数键名 //el-form-itemel-form-item label参数键值 propconfigValueel-input v-modelform.configValue placeholder请输入参数键值 //el-form-itemel-form-item label系统内置 propconfigTypeel-radio-group v-modelform.configTypeel-radiov-fordict in dict.type.sys_yes_no:keydict.value:labeldict.value{{dict.label}}/el-radio/el-radio-group/el-form-itemel-form-item label备注 propremarkel-input v-modelform.remark typetextarea placeholder请输入内容 //el-form-item/el-formdiv slotfooter classdialog-footerel-button typeprimary clicksubmitForm确 定/el-buttonel-button clickcancel取 消/el-button/div/el-dialog 3 模拟xss攻击示例2数据库效果 数据库表 sys_conifg。如下图所示如果没有做XSS处理在数据库里面就会完整的把标签和脚本进行入库是非常危险的 4 如何在项目处理xss攻击?
application.ymlxss处理开关 # 防止XSS攻击
xss:# 过滤开关enabled: true# 排除链接多个用逗号分隔# 即哪些不需要去过滤。# 因为通知公告的内容是富文本内容可能会包含一些HTML内容。# 所以一般排除就会排除一些富文本的url。# * 表示所有excludes: /system/notice# 匹配链接# 即需要过滤的链接/system/*系统管理/monitor/*监控/tool/*工具urlPatterns: /system/*,/monitor/*,/tool/* 重启项目刷新页面面对 模拟xss攻击示例2 数据库效果面对 模拟xss攻击示例2 页面效果点击不再有弹出框 5 项目处理xss攻击实现原理
FilterConfig通用的过滤器的配置 /*** Filter配置* 通用过滤器的配置。*/
Configuration
public class FilterConfig
{/*** 获取application.yaml中的xss相关参数配置*/Value(${xss.excludes})private String excludes;/*** 获取application.yaml中的xss相关参数配置*/Value(${xss.urlPatterns})private String urlPatterns;/*** 这里可以添加加很多过滤器现在只有一个xss过滤器* 后续有其他的过滤器直接往里面加就行了* 这样方便统一去管理所有的过滤器。*/SuppressWarnings({ rawtypes, unchecked })BeanConditionalOnProperty(value xss.enabled, havingValue true)public FilterRegistrationBean xssFilterRegistration(){FilterRegistrationBean registration new FilterRegistrationBean();/*** 配置请求类型*/registration.setDispatcherTypes(DispatcherType.REQUEST);/*** 配置xss核心过滤器*/registration.setFilter(new XssFilter());/*** 配置需要过滤的链接*/registration.addUrlPatterns(StringUtils.split(urlPatterns, ,));/*** 配置xss核心过滤器的名称随意*/registration.setName(xssFilter);/*** 配置xss核心过滤器的优先级*/registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);/*** 配置xss核心过滤器的初始化参数*/MapString, String initParameters new HashMapString, String();/*** 配置xss核心过滤器的初始化参数 —— 排除的链接不需要过滤的链接*/initParameters.put(excludes, excludes);registration.setInitParameters(initParameters);return registration;}
} com.ruoyi.common.filter.XssFilter防止XSS攻击的过滤器 /*** 防止XSS攻击的过滤器* * author ruoyi*/
public class XssFilter implements Filter
{/*** 排除链接*/public ListString excludes new ArrayList();/*** 初化方法*/Overridepublic void init(FilterConfig filterConfig) throws ServletException{/*** 排除链接List*/String tempExcludes filterConfig.getInitParameter(excludes);if (StringUtils.isNotEmpty(tempExcludes)){String[] url tempExcludes.split(,);for (int i 0; url ! null i url.length; i){excludes.add(url[i]);}}}/*** 核心处理* param request* param response* param chain* throws IOException* throws ServletException*/Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException{HttpServletRequest req (HttpServletRequest) request;HttpServletResponse resp (HttpServletResponse) response;if (handleExcludeURL(req, resp)){/*** 排除链接正常执行*/chain.doFilter(request, response);return;}/*** xss攻击的处理*/XssHttpServletRequestWrapper xssRequest new XssHttpServletRequestWrapper((HttpServletRequest) request);chain.doFilter(xssRequest, response);}private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response){/*** 获取请求url*/String url request.getServletPath();String method request.getMethod();// GET DELETE 不过滤if (method null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)){return true;}return StringUtils.matches(url, excludes);}Overridepublic void destroy(){}
} XssHttpServletRequestWrapperXSS过滤处理 /*** XSS过滤处理*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper
{/*** param request*/public XssHttpServletRequestWrapper(HttpServletRequest request){super(request);}/*** 处理非json类型* param name* return*/Overridepublic String[] getParameterValues(String name){/*** 获取所有请求参数值*/String[] values super.getParameterValues(name);if (values ! null){int length values.length;String[] escapesValues new String[length];for (int i 0; i length; i){// 防xss攻击和过滤前后空格escapesValues[i] EscapeUtil.clean(values[i]).trim();}return escapesValues;}return super.getParameterValues(name);}/*** 处理json类型*/Overridepublic ServletInputStream getInputStream() throws IOException{// 非json类型直接返回if (!isJsonRequest()){return super.getInputStream();}// 为空直接返回String json IOUtils.toString(super.getInputStream(), utf-8);if (StringUtils.isEmpty(json)){return super.getInputStream();}// xss过滤json EscapeUtil.clean(json).trim();byte[] jsonBytes json.getBytes(utf-8);final ByteArrayInputStream bis new ByteArrayInputStream(jsonBytes);return new ServletInputStream(){Overridepublic boolean isFinished(){return true;}Overridepublic boolean isReady(){return true;}Overridepublic int available() throws IOException{return jsonBytes.length;}Overridepublic void setReadListener(ReadListener readListener){}Overridepublic int read() throws IOException{return bis.read();}};}/*** 是否是Json请求* * param request*/public boolean isJsonRequest(){String header super.getHeader(HttpHeaders.CONTENT_TYPE);return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);}
} EscapeUtil#clean清除所有HTML标签但是不删除标签内的内容 /*** 清除所有HTML标签但是不删除标签内的内容* * param content 文本* return 清除标签后的文本*/public static String clean(String content){return new HTMLFilter().filter(content);}
48 防止重复提交过滤详解
1 前端处理方案自己研究
2 后端处理方案
RepeatSubmit自定义注解防止表单重复提交 /*** 自定义注解防止表单重复提交**/
Inherited
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
Documented
public interface RepeatSubmit
{/*** 间隔时间(ms)小于此时间视为重复提交*/public int interval() default 5000;/*** 提示消息*/public String message() default 不允许重复提交请稍候再试;
} SysConfigController#addXxxController的接口方法上加上防重复提交注解 /*** 新增参数配置*/PreAuthorize(ss.hasPermi(system:config:add))Log(title 参数管理, businessType BusinessType.INSERT)PostMappingRepeatSubmitpublic AjaxResult add(Validated RequestBody SysConfig config){if (!configService.checkConfigKeyUnique(config)){return error(新增参数 config.getConfigName() 失败参数键名已存在);}config.setCreateBy(getUsername());return toAjax(configService.insertConfig(config));} 重启项目再走 多次点击产生重复提交 提示 不允许重复提交请稍后再试
3 后端处理实现原理
FilterConfig#someFilterRegistration过滤器统一配置 /*** 后续新建过滤器后在这里可以添加多个过滤器*/Beanpublic FilterRegistrationBean someFilterRegistration(){FilterRegistrationBean registration new FilterRegistrationBean();registration.setFilter(new RepeatableFilter());/*** 过滤所有的请求*/registration.addUrlPatterns(/*);/*** 过滤器的名称随意*/registration.setName(repeatableFilter);/*** 优先级。* 防重复提交过滤的优先级比XSS低因此所有请求都会先进到他XSS里面去校验然后才走防重复提交过滤器。*/registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);return registration;} RepeatableFilter#doFilter /*** Repeatable 过滤器* * author ruoyi*/
public class RepeatableFilter implements Filter
{Overridepublic void init(FilterConfig filterConfig) throws ServletException{}/*** 方法中没有做太多的处理简单来说就只是说把request给包装了一下。*/Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException{ServletRequest requestWrapper null;if (request instanceof HttpServletRequest StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)){requestWrapper new RepeatedlyRequestWrapper((HttpServletRequest) request, response);}if (null requestWrapper){chain.doFilter(request, response);}else{chain.doFilter(requestWrapper, response);}}Overridepublic void destroy(){}
}