领导:你加的水印怎么还能被删掉的,扣工资!

发布于:2024-04-28 ⋅ 阅读:(17) ⋅ 点赞:(0)

故事是这样的

领导:小李,你加的水印怎么还能被删掉的?这可是关乎公司信息安全的大事!这种疏忽怎么能不扣工资呢?

小李:领导,请您听我解释一下!我确实按照常规的方法加了水印,可是……

领导:(打断)但是什么?难道这就是你对公司资料的保护吗?

小李:我也不明白,按理说水印是无法删除的,我会再仔细检查一下……

领导:我不能容忍这样的失误。这种安全隐患严重影响了我们的机密性。

小李焦虑地试图解释,但领导的目光如同刀剑一般锐利。他决定,这次一定要找到解决方法,否则,这将是一场职场危机……

2 (2).gif

水印组件

小李想到antd中有现成的水印组件,便去研究了一下。即使删掉了水印div,水印依然存在,因为瞬间又生成了一个相同的水印div。他一瞬间想到了解决方案,并开始了重构水印组件。

3.gif

原始代码

//app.vue
<template>
  <div>
    <Watermark text="前端百事通">
      <div class="content"></div>
    </Watermark>
  </div>
</template>

<script setup>
import Watermark from './components/Watermark.vue';
</script>


<style scoped>
.content{
  width: 400px;
  height: 400px;
  background-color: aquamarine;
}
</style>
//watermark.vue
<template>
  <div ref="watermarkRef" class="watermark-container">
    <slot>

    </slot>
  </div>

</template>

<script setup>
import { onMounted, ref } from 'vue';
const watermarkRef=ref(null)
const props = defineProps({
  text: {
    type: String,
    default: '前端百事通'
  },
  fontSize: {
    type: Number,
    default: 14
  },
  gap: {
    type: Number,
    default: 50
  },
  rotate: {
    type: Number,
    default: 45
  }
})
onMounted(() => {
  addWatermark()
})
const addWatermark = () => {
  const { rotate, gap, text, fontSize } = props
  const color = 'rgba(0, 0, 0, 0.3)'; // 可以从props中传入 
  const watermarkContainer = watermarkRef.value;

  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  const font=fontSize+'px DejaVu Sans Mono'
  // 设置水印文字的宽度和高度  
  const metrics = context.measureText(text);
  const canvasWidth=metrics.width+gap
  canvas.width=canvasWidth
  canvas.height=canvasWidth
  // 绘制水印文字  
  context.translate(canvas.width/2,canvas.height/2)
  context.rotate((-1 * rotate * Math.PI / 180));
  context.fillStyle = color;
  context.font=font
  context.textAlign='center'
  context.textBaseline='middle'
  context.fillText(text,0,0)
  // 将canvas转为图片  
  const url = canvas.toDataURL('image/png');
  // 创建水印元素并添加到容器中  
  const watermarkLayer = document.createElement('div');
  watermarkLayer.style.position = 'absolute';
  watermarkLayer.style.top = '0';
  watermarkLayer.style.left = '0';
  watermarkLayer.style.width = '100%';
  watermarkLayer.style.height = '100%';
  watermarkLayer.style.pointerEvents = 'none';
  watermarkLayer.style.backgroundImage = `url(${url})`;
  watermarkLayer.style.backgroundRepeat = 'repeat';
  watermarkLayer.style.zIndex = '9999';
  watermarkContainer.appendChild(watermarkLayer);
}
</script>

<style>
.watermark-container {  
  position: relative;  
  width: 100%;  
  height: 100%;  
  overflow: auto;  
}  
</style>

防篡改思路

  • 监听删除dom操作,在删除dom操作的瞬间重新生成一个相同的dom元素
  • 监听修改dom样式操作
  • 不能使用onMounted,改为watchEffect进行监听操作

使用MutationObserver监听整个区域

let ob
onMounted(() => {
  ob=new MutationObserver((records)=>{
    console.log(records)
  })
  ob.observe(watermarkRef.value,{
    childList:true,
    attributes:true,
    subtree:true
  })
})
onUnmounted(()=>{
  ob.disconnect()
})

在删除水印div之后,打印一下看看records是什么。

image.png

在修改div样式之后,打印一下records

image.png

很明显,如果是删除,我们就关注removedNodes字段,如果是修改,我们就关注attributeName字段。

onMounted(() => {
  ob=new MutationObserver((records)=>{
    for(let item of records){
      //监听删除
      for(let ele of item.removedNodes){
        if(ele===watermarkDiv){
          generateFlag.value=!generateFlag.value
          return
        }
      }
      //监听修改
      if(item.attributeName==='style'){
        generateFlag.value=!generateFlag.value
        return
      }
    }
  })
  ob.observe(watermarkRef.value,{
    childList:true,
    attributes:true,
    subtree:true
  })
})

watchEffect(() => {
  //generateFlag的用处是让watchEffect收集这个依赖 
  //通过改变generateFlag的值来重新调用生成水印的函数
  generateFlag.value
  if(watermarkRef.value){
    addWatermark()
  }
})

4.gif

最终,小李向领导展示了新的水印组件,取得了领导的认可和赞许,保住了工资。

全剧终。 文章同步发表于公众号,欢迎关注!


网站公告

今日签到

点亮在社区的每一天
去签到