之前这个事情都CA公司去做的,现在给客户做demo,要模拟一下签字盖章了,我们的业务PDF文件是动态生成的所以没法通过坐标定位,只能通过关键字查找定位了。

之前在网上看了许多通多通过查询关键字,然后图片盖章的文章都不完整,说白了基本上没完成。我这边利用了网上查找关键字的方法。

我自己查看了相关Api,然后完善了这个功能。不多说了,直接上代码。

我这个只是示例,默认只取第一个关键字,多个相同关键字,根据业务场景定。   推荐方式:设置白色文字作为关键字,公司的业务我基本上都这样操作。

1.帮助类方法(关键字签字)

  1 /// <summary>
2 /// pdf上图片签章
3 /// </summary>
4 public class SealPictureHelper
5 {
6 static float ReSizeMaxWidth = 30;
7 static float ReSizeMaxHeight = 30;
8 /// <summary>
9 /// 手写签字(流和base64格式)
10 /// </summary>
11 /// <param name="bytePdf">byte数组的pdf文件</param>
12 /// <param name="SignImgBase64">base64格式的图片</param>
13 /// <param name="SignKeyWord">关键字</param>
14 /// <returns></returns>
15 public static byte[] SignBase64Img(byte[] bytePdf, string SignImgBase64, string SignKeyWord)
16 {
17 byte[] newbytefile;
18 try
19 {
20 using (MemoryStream ms = new MemoryStream())
21 {
22 // 创建一个PdfReader对象
23 using (PdfReader reader = new PdfReader(bytePdf))
24 {
25 using (PdfStamper stamper = new PdfStamper(reader, ms))
26 {
27 // 获得文档页数
28 int n = reader.NumberOfPages;
29 for (int i = 1; i <= n; i++)
30 {
31 //获取当前页
32 PdfContentByte cb = stamper.GetOverContent(i);
33 PdfLocation pz = new PdfLocation();
34 iTextSharp.text.pdf.parser.PdfReaderContentParser p = new iTextSharp.text.pdf.parser.PdfReaderContentParser(reader);
35 p.ProcessContent<PdfLocation>(i, pz);
36 //查找当前页的关键字
37 pz.SearchKeywords(SignKeyWord);
38 if (pz.TextLocationInfo.Count > 0)
39 {
40 //坐标是从左下角往上,左下角为(0,0)零点
41 XTextInfo o = pz.TextLocationInfo[0];
42 //获取关键字左上角开始坐标
43 string[] L_T_Location = o.TopLeft.ToString().Split(',');//272.15,766.46,1
44 var left_x = float.Parse(L_T_Location[0]);
45 var top_y = float.Parse(L_T_Location[1]);
46 //获取关键字右下角结束坐标
47 string[] R_B_Location = o.BottomRight.ToString().Split(',');//305.15,755.46,1
48 var right_x = float.Parse(R_B_Location[0]);
49 var bottom_y = float.Parse(R_B_Location[1]);
50 //计算得到关键字的中心点
51 float x = (right_x - left_x) / 2 + left_x;
52 float y = (top_y - bottom_y) / 2 + bottom_y;
53 var imgtest = ConvertBase64ToImage(SignImgBase64);
54 //创建一个图片对象
55 iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(imgtest, System.Drawing.Imaging.ImageFormat.Jpeg);
56 //设置图片的指定大小
57 float expectWidth = img.Width;
58 float expectHeight = img.Height;
59 if (img.Width > img.Height && img.Width > ReSizeMaxWidth)
60 {
61 expectWidth = ReSizeMaxWidth;
62 expectHeight = expectWidth * img.Height / img.Width;
63 }
64 else if (img.Height > img.Width && img.Height > ReSizeMaxHeight)
65 {
66 expectHeight = ReSizeMaxHeight;
67 expectWidth = expectHeight * img.Width / img.Height;
68 }
69 img.ScaleToFit(expectWidth, expectHeight);//img.ScaleToFit(128, 128);
70 //设置图片位置在关键字正中心
71 img.SetAbsolutePosition(x - expectWidth / 2, y - expectHeight / 2);//
72 img.Transparency = new int[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
73 cb.AddImage(img);
74 }
75 }
76 }
77 }
78 newbytefile = ms.ToArray();
79 }
80 return newbytefile;
81 }
82 catch (Exception ex)
83 {
84 South.Tools.Logger.SaveLogUtil.Error(ex.Message, ex, "SignPicPDF");
85 return null;
86 }
87 }
88 /// <summary>
89 /// 手写签字(文件路径)
90 /// </summary>
91 /// <param name="Pdf_filePath">要签字的pdf文件路径</param>
92 /// <param name="SignImgPath">签字的图片路径</param>
93 /// <param name="SignKeyWord">关键字</param>
94 /// <returns></returns>
95 public static byte[] SignFile(string Pdf_filePath, string SignImgPath, string SignKeyWord)
96 {
97 byte[] newbytefile;
98 try
99 {
100 using (MemoryStream ms = new MemoryStream())
101 {
102 // 创建一个PdfReader对象
103 using (PdfReader reader = new PdfReader(Pdf_filePath))
104 {
105 using (PdfStamper stamper = new PdfStamper(reader, ms))
106 {
107 // 获得文档页数
108 int n = reader.NumberOfPages;
109 for (int i = 1; i <= n; i++)
110 {
111 //获取当前页
112 PdfContentByte cb = stamper.GetOverContent(i);
113 PdfLocation pz = new PdfLocation();
114 iTextSharp.text.pdf.parser.PdfReaderContentParser p = new iTextSharp.text.pdf.parser.PdfReaderContentParser(reader);
115 p.ProcessContent<PdfLocation>(i, pz);
116 //查找当前页的关键字
117 pz.SearchKeywords(SignKeyWord);
118 if (pz.TextLocationInfo.Count > 0)
119 {
120 XTextInfo o = pz.TextLocationInfo[0];
121 //获取关键字左上角开始坐标
122 string[] L_T_Location = o.TopLeft.ToString().Split(',');//272.15,766.46,1
123 var left_x = float.Parse(L_T_Location[0]);
124 var top_y = float.Parse(L_T_Location[1]);
125 //获取关键字右下角结束坐标
126 string[] R_B_Location = o.BottomRight.ToString().Split(',');//305.15,755.46,1
127 var right_x = float.Parse(R_B_Location[0]);
128 var bottom_y = float.Parse(R_B_Location[1]);
129 //计算得到关键字的中心点
130 float x = (right_x - left_x) / 2 + left_x;
131 float y = (top_y - bottom_y) / 2 + bottom_y;
132 //创建一个图片对象
133 iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(SignImgPath);
134 float expectWidth = img.Width;
135 float expectHeight = img.Height;
136 if (img.Width > img.Height && img.Width > ReSizeMaxWidth)
137 {
138 expectWidth = ReSizeMaxWidth;
139 expectHeight = expectWidth * img.Height / img.Width;
140 }
141 else if (img.Height > img.Width && img.Height > ReSizeMaxHeight)
142 {
143 expectHeight = ReSizeMaxHeight;
144 expectWidth = expectHeight * img.Width / img.Height;
145 }
146 //设置图片的指定大小
147 img.ScaleToFit(expectWidth, expectHeight);//img.ScaleToFit(128, 128);
148 //设置图片位置在关键字正中心
149 img.SetAbsolutePosition(x - expectWidth / 2, y - expectHeight / 2);//
150 img.Transparency = new int[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
151 cb.AddImage(img);
152 }
153 }
154 }
155 }
156 newbytefile = ms.ToArray();
157 }
158 return newbytefile;
159 }
160 catch (Exception ex)
161 {
162 South.Tools.Logger.SaveLogUtil.Error(ex.Message, ex, "SignPicPDF");
163 return null;
164 }
165 }
166
167 public static System.Drawing.Image ConvertBase64ToImage(string base64String)
168 {
169 byte[] imageBytes = Convert.FromBase64String(base64String);
170 System.Drawing.Bitmap bitmap = null;
171 MemoryStream stream = null;
172 try
173 {
174 stream = new MemoryStream(imageBytes);
175 bitmap = new System.Drawing.Bitmap(stream);
176 //bitmap.Save(IOHelper.getPhysicalDir() + "/test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
177 return bitmap;
178 }
179 catch (Exception)
180 {
181
182 throw;
183 }
184 finally
185 {
186
187 //if (stream != null)
188 //{
189 // stream.Dispose();
190 //}
191 }
192
193 }
194 }

2.查找关键字相关类

  1  public class PdfLocation : LocationTextExtractionStrategy
2 {
3 private List<XTextChunk> m_locationResult = new List<XTextChunk>();
4 private List<XTextInfo> m_TextLocationInfo = new List<XTextInfo>();
5 public List<XTextChunk> LocationResult
6 {
7 get { return m_locationResult; }
8 }
9 public List<XTextInfo> TextLocationInfo
10 {
11 get { return m_TextLocationInfo; }
12 }
13
14 /// <summary>
15 /// Creates a new LocationTextExtracationStrategyEx
16 /// </summary>
17 public PdfLocation()
18 {
19 }
20 public void SearchKeywords(string sKeyword, bool bDefaultFirst = true)
21 {
22 m_locationResult.Sort();
23 m_TextLocationInfo.Clear();
24 //StringBuilder sb = new StringBuilder();
25 XTextChunk lastChunk = null;
26 XTextInfo lastXTextInfo = null;
27 foreach (XTextChunk chunk in m_locationResult)
28 {
29 if (lastChunk == null)
30 {
31 //sb.Append(chunk.Text);
32 lastXTextInfo = new XTextInfo(chunk);
33 //if (lastXTextInfo.Text.Contains(sKeyword))
34 //{
35 // m_TextLocationInfo.Add(lastXTextInfo);
36 //}
37 }
38 else
39 {
40 if (chunk.sameLine(lastChunk))
41 {
42 float dist = chunk.distanceFromEndOf(lastChunk);
43
44 if (dist < -chunk.CharSpaceWidth)
45 {
46 //sb.Append(' ');
47 lastXTextInfo.addSpace();
48 }
49 //append a space if the trailing char of the prev string wasn't a space && the 1st char of the current string isn't a space
50 else if (dist > chunk.CharSpaceWidth / 2.0f && chunk.Text[0] != ' ' && lastChunk.Text[lastChunk.Text.Length - 1] != ' ')
51 {
52 //sb.Append(' ');
53 lastXTextInfo.addSpace();
54 }
55 //sb.Append(chunk.Text);
56 lastXTextInfo.appendText(chunk);
57 }
58 else
59 {
60 //sb.Append('\n');
61 //sb.Append(chunk.Text);
62 lastXTextInfo = new XTextInfo(chunk);
63 //if (lastXTextInfo.Text.Contains(sKeyword))
64 //{
65 // m_TextLocationInfo.Add(lastXTextInfo);
66 //}
67 }
68 }
69 lastChunk = chunk;
70 if (lastXTextInfo.Text.Contains(sKeyword))
71 {
72 m_TextLocationInfo.Add(lastXTextInfo);
73 break;
74 }
75 }
76 }
77 /// <summary>
78 /// Returns the result so far
79 /// </summary>
80 /// <returns>a String with the resulting text</returns>
81 public override String GetResultantText()
82 {
83 m_locationResult.Sort();
84
85 StringBuilder sb = new StringBuilder();
86 XTextChunk lastChunk = null;
87 XTextInfo lastXTextInfo = null;
88 foreach (XTextChunk chunk in m_locationResult)
89 {
90 if (lastChunk == null)
91 {
92 sb.Append(chunk.Text);
93 lastXTextInfo = new XTextInfo(chunk);
94 m_TextLocationInfo.Add(lastXTextInfo);
95 }
96 else
97 {
98 if (chunk.sameLine(lastChunk))
99 {
100 float dist = chunk.distanceFromEndOf(lastChunk);
101
102 if (dist < -chunk.CharSpaceWidth)
103 {
104 sb.Append(' ');
105 lastXTextInfo.addSpace();
106 }
107 //append a space if the trailing char of the prev string wasn't a space && the 1st char of the current string isn't a space
108 else if (dist > chunk.CharSpaceWidth / 2.0f && chunk.Text[0] != ' ' && lastChunk.Text[lastChunk.Text.Length - 1] != ' ')
109 {
110 sb.Append(' ');
111 lastXTextInfo.addSpace();
112 }
113 sb.Append(chunk.Text);
114 lastXTextInfo.appendText(chunk);
115 }
116 else
117 {
118 sb.Append('\n');
119 sb.Append(chunk.Text);
120 lastXTextInfo = new XTextInfo(chunk);
121 m_TextLocationInfo.Add(lastXTextInfo);
122 }
123 }
124 lastChunk = chunk;
125 }
126 return sb.ToString();
127 }
128
129 /// <summary>
130 ///
131 /// </summary>
132 /// <param name="renderInfo"></param>
133 public override void RenderText(TextRenderInfo renderInfo)
134 {
135 LineSegment segment = renderInfo.GetBaseline();
136 XTextChunk location = new XTextChunk(renderInfo.GetText(), segment.GetStartPoint(), segment.GetEndPoint(), renderInfo.GetSingleSpaceWidth(), renderInfo.GetAscentLine(), renderInfo.GetDescentLine());
137 m_locationResult.Add(location);
138 }
139
140
141 }
142 public class XTextChunk : IComparable, ICloneable
143 {
144 string m_text;
145 Vector m_startLocation;
146 Vector m_endLocation;
147 Vector m_orientationVector;
148 int m_orientationMagnitude;
149 int m_distPerpendicular;
150 float m_distParallelStart;
151 float m_distParallelEnd;
152 float m_charSpaceWidth;
153
154 public LineSegment AscentLine;
155 public LineSegment DecentLine;
156
157 public object Clone()
158 {
159 XTextChunk copy = new XTextChunk(m_text, m_startLocation, m_endLocation, m_charSpaceWidth, AscentLine, DecentLine);
160 return copy;
161 }
162
163 public string Text
164 {
165 get { return m_text; }
166 set { m_text = value; }
167 }
168 public float CharSpaceWidth
169 {
170 get { return m_charSpaceWidth; }
171 set { m_charSpaceWidth = value; }
172 }
173 public Vector StartLocation
174 {
175 get { return m_startLocation; }
176 set { m_startLocation = value; }
177 }
178 public Vector EndLocation
179 {
180 get { return m_endLocation; }
181 set { m_endLocation = value; }
182 }
183
184 /// <summary>
185 /// Represents a chunk of text, it's orientation, and location relative to the orientation vector
186 /// </summary>
187 /// <param name="txt"></param>
188 /// <param name="startLoc"></param>
189 /// <param name="endLoc"></param>
190 /// <param name="charSpaceWidth"></param>
191 public XTextChunk(string txt, Vector startLoc, Vector endLoc, float charSpaceWidth, LineSegment ascentLine, LineSegment decentLine)
192 {
193 m_text = txt;
194 m_startLocation = startLoc;
195 m_endLocation = endLoc;
196 m_charSpaceWidth = charSpaceWidth;
197 AscentLine = ascentLine;
198 DecentLine = decentLine;
199
200 m_orientationVector = m_endLocation.Subtract(m_startLocation).Normalize();
201 m_orientationMagnitude = (int)(Math.Atan2(m_orientationVector[Vector.I2], m_orientationVector[Vector.I1]) * 1000);
202
203 // the two vectors we are crossing are in the same plane, so the result will be purely
204 // in the z-axis (out of plane) direction, so we just take the I3 component of the result
205 Vector origin = new Vector(0, 0, 1);
206 m_distPerpendicular = (int)(m_startLocation.Subtract(origin)).Cross(m_orientationVector)[Vector.I3];
207
208 m_distParallelStart = m_orientationVector.Dot(m_startLocation);
209 m_distParallelEnd = m_orientationVector.Dot(m_endLocation);
210 }
211
212 /// <summary>
213 /// true if this location is on the the same line as the other text chunk
214 /// </summary>
215 /// <param name="XTextChunkToCompare">the location to compare to</param>
216 /// <returns>true if this location is on the the same line as the other</returns>
217 public bool sameLine(XTextChunk XTextChunkToCompare)
218 {
219 if (m_orientationMagnitude != XTextChunkToCompare.m_orientationMagnitude) return false;
220 if (m_distPerpendicular != XTextChunkToCompare.m_distPerpendicular) return false;
221 return true;
222 }
223
224 /// <summary>
225 /// Computes the distance between the end of 'other' and the beginning of this chunk
226 /// in the direction of this chunk's orientation vector. Note that it's a bad idea
227 /// to call this for chunks that aren't on the same line and orientation, but we don't
228 /// explicitly check for that condition for performance reasons.
229 /// </summary>
230 /// <param name="other"></param>
231 /// <returns>the number of spaces between the end of 'other' and the beginning of this chunk</returns>
232 public float distanceFromEndOf(XTextChunk other)
233 {
234 float distance = m_distParallelStart - other.m_distParallelEnd;
235 return distance;
236 }
237
238 /// <summary>
239 /// Compares based on orientation, perpendicular distance, then parallel distance
240 /// </summary>
241 /// <param name="obj"></param>
242 /// <returns></returns>
243 public int CompareTo(object obj)
244 {
245 if (obj == null) throw new ArgumentException("Object is now a XTextChunk");
246
247 XTextChunk rhs = obj as XTextChunk;
248 if (rhs != null)
249 {
250 if (this == rhs) return 0;
251
252 int rslt;
253 rslt = m_orientationMagnitude - rhs.m_orientationMagnitude;
254 if (rslt != 0) return rslt;
255
256 rslt = m_distPerpendicular - rhs.m_distPerpendicular;
257 if (rslt != 0) return rslt;
258
259 // note: it's never safe to check floating point numbers for equality, and if two chunks
260 // are truly right on top of each other, which one comes first or second just doesn't matter
261 // so we arbitrarily choose this way.
262 rslt = m_distParallelStart < rhs.m_distParallelStart ? -1 : 1;
263
264 return rslt;
265 }
266 else
267 {
268 throw new ArgumentException("Object is now a XTextChunk");
269 }
270 }
271 }
272
273 public class XTextInfo
274 {
275 public Vector TopLeft;
276 public Vector BottomRight;
277 private string m_Text;
278
279 public string Text
280 {
281 get { return m_Text; }
282 }
283
284 /// <summary>
285 /// Create a XTextInfo.
286 /// </summary>
287 /// <param name="initialXTextChunk"></param>
288 public XTextInfo(XTextChunk initialXTextChunk)
289 {
290 TopLeft = initialXTextChunk.AscentLine.GetStartPoint();
291 BottomRight = initialXTextChunk.DecentLine.GetEndPoint();
292 //TopLeft = initialXTextChunk.StartLocation;
293 //BottomRight = initialXTextChunk.EndLocation;
294 m_Text = initialXTextChunk.Text;
295 }
296
297 /// <summary>
298 /// Add more text to this XTextInfo.
299 /// </summary>
300 /// <param name="additionalXTextChunk"></param>
301 public void appendText(XTextChunk additionalXTextChunk)
302 {
303 BottomRight = additionalXTextChunk.DecentLine.GetEndPoint();
304 //BottomRight = additionalXTextChunk.EndLocation;
305 m_Text += additionalXTextChunk.Text;
306 }
307
308 /// <summary>
309 /// Add a space to the XTextInfo. This will leave the endpoint out of sync with the text.
310 /// The assumtion is that you will add more text after the space which will correct the endpoint.
311 /// </summary>
312 public void addSpace()
313 {
314 m_Text += ' ';
315 }
316 }

.net通过iTextSharp.pdf操作pdf文件实现查找关键字签字盖章的更多相关文章

  1. linux所有文件中查找关键字的命令

     grep 192.168.1.1 * -r    在所有文件中查找192.168.1.1

  2. Linux - Shell - 在多个文件中查找关键字

    1. 概述 在多个文件中 查找内容 2. 想干啥 目的 在 多个文件 中, 查找内容 准备 之前在 单个文件里 查找过内容 工具 awk 前提 文件有固定格式 查找时有字段的要求 例子 # print ...

  3. sublime text多文件夹查找关键字

    Ctrl+shift+F 快捷键在文件夹内查找,与普通编辑器不同的地方是sublime允许添加多个文件夹进行查找 转自:http://www.douban.com/note/362268947/

  4. 操作PDF文档功能的相关开源项目探索——iTextSharp 和PDFBox

    原文 操作PDF文档功能的相关开源项目探索——iTextSharp 和PDFBox 很久没自己写写心得日志与大家分享了,一方面是自己有点忙,一方面是自己有点懒,没有及时总结.因为实践是经验的来源,总结 ...

  5. 【译】在Asp.Net中操作PDF - iTextSharp - 绘制矢量图

    原文 [译]在Asp.Net中操作PDF - iTextSharp - 绘制矢量图 在上一篇iTextSharp文章中讲述了如何将现有的图片插入PDF中并对其进行操作.但有时,你需要在PDF中绘制不依 ...

  6. 【译】在Asp.Net中操作PDF – iTextSharp - 操作图片

    原文 [译]在Asp.Net中操作PDF – iTextSharp - 操作图片 作为我的iTextSharp系列的文章的第七篇,开始探索使用iTextSharp在PDF中操作图片,理解本篇文章需要看 ...

  7. 【译】在Asp.Net中操作PDF – iTextSharp -利用块,短语,段落添加文本

    原文 [译]在Asp.Net中操作PDF – iTextSharp -利用块,短语,段落添加文本 本篇文章是讲述使用iTextSharp这个开源组件的系列文章的第三篇,iTextSharp可以通过As ...

  8. 【译】在Asp.Net中操作PDF - iTextSharp - 使用字体

    原文 [译]在Asp.Net中操作PDF - iTextSharp - 使用字体 紧接着前面我对iTextSharp简介博文,iTextSharp是一个免费的允许Asp.Net对PDF进行操作的第三方 ...

  9. 【转】使用iTextSharp在Asp.Net中操作PDF

    使用iTextSharp在Asp.Net中操作PDF操作 使用iTextSharp在Asp.Net中操作PDF系列文章目录 实战 iTextSharp iTextSharp 合并多个PDF文件 C#生 ...

随机推荐

  1. 第1.2节 Python学习环境的使用

    Python的环境安装好以后,可以通过IDLE(Python 3.7 64-bit)进入图形界面使用Python,也可以通过Python 3.7 64-bit进入命令行交互式界面,两者都可以使用,不过 ...

  2. PyQt(Python+Qt)学习随笔:使用QColorDialog.getColor交互设置部件的颜色

    专栏:Python基础教程目录 专栏:使用PyQt开发图形界面Python应用 专栏:PyQt入门学习 老猿Python博文目录 PyQt中的部件只要是QWidget的派生类都可以在Designer或 ...

  3. linux常用快捷键总结

    启动器:<super> 显示桌面:<super>D 文件管理器:<super>E 显示工作区:<super>S 打开终端:ctrl+alt+T 关闭窗口 ...

  4. 团队作业part6--复审与事后分析

    一.Alpha阶段项目复审:https://www.cnblogs.com/3Jax/p/13127401.html 二.事后诸葛亮分析:https://www.cnblogs.com/3Jax/p/ ...

  5. 事后Postmortem会议

    会议图片 一.设想和目标 1. 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我的软件是要建立一个失物招领网站,是一个为校园里的失误招领工作提供便利的平台.我们对 ...

  6. Panda 交易所热点关注:股权交易中心+区块链试点将开始

    近期,Panda 交易所注意到,中国证监会已同意北京.上海等5家区域性股权市场参与区块链建设试点工作.Panda 交易所获悉的具体情况是,北京股权交易中心曾联合其他单位共同推出区域性股权市场中介机构征 ...

  7. MySQL(一):MySQL数据库事务与锁

    基本概念 事务是指满足ACID特性的的一组操作,可以通过Commit提交事务,也可以也可以通过Rollback进行回滚.会存在中间态和一致性状态(也是真正在数据库表中存在的状态) ACID Atomi ...

  8. DVWA各等级sql注入

    sql全等级注入 level:low <?php if( isset( $_REQUEST[ 'Submit' ] ) ) { //判断submit是否存在 // Get input $id = ...

  9. Greenplum 性能优化之路 --(二)存储格式

    一.存储格式介绍 Greenplum(以下简称 GP)有2种存储格式,Heap 表和 AO 表(AORO 表,AOCO 表). Heap 表:这种存储格式是从 PostgreSQL 继承而来的,目前是 ...

  10. CSRF学习

    前提环境:网站存在CSRF漏洞(也就是过于相信访问请求,只判断了用户是否存在cookie,并未判断请求的发起者) CSRF攻击原理,用户A需要转账,用户A正常向银行网站发送请求登录,登录成功后银行网站 ...