0%

今天内心膨胀的以为自己是个人才,所以随心所欲的看了门课。以下记录了我觉得对我有意义的点。

根据业务布局和业务的洞察来理解和分析业务的变局时什么,再次基础上呢再来看搭建什么样的阵型,组织阵型来匹配业务,为了这样的一个人才搭建,需要准备怎样对人才进行赋能,这个赋能是系统性的赋能,不是对单个个体,甚至不是对团队,而是一个系统性的赋能。最后沉淀褚一套人才管理的机制,用这样的一套人才管理机制不断推动人才。

人才

业务布局=人才体积

人才体积:人才在多大的业务空间中去发挥作用,所以业务布局决定了人才体积

组织阵型=密度

人才分布的密度和人才分布的层次

人才赋能=人才质量

对组织化导向的人才赋能,我组织想对我整个的人才产生怎样的一个牵引力。核心是我是要提高我得人才适应未来业务成长需求,适应未来变局的整体的能力的拉伸。

密度=质量/体积

所以单独对人才进行培训和赋能是不够的,而是要对人才的密度进行一个拉升,即提升人才质量的同时,要让业务体积处于合适的范围内。

人才称重:人才在组织中的重量,发挥的作用。

人才称重的三种方式:

  1. 通过人才赋能提升人才质量
  2. 通过业务布局减少人才体积(冗余)
  3. 通过组织机构的调整来优化人才在不同组织单元中的密度,激活组织的有效性。

四大模型

主要说说人才管理机制模型:

汰换、激励、约束、牵引

汰换:这是一个必然的过程,当你想懈怠的时候觉得无趣的时候,问问自己你对于公司的价值是什么

牵引:企业文化价值的输入、企业向心力的培养等

业务变局

业务结构与人才结构

解释,在什么情况下先发展业务还是先发展人才。

依赖的手段则是进行业务摸底人才摸底(核心团队)

组织变阵

说明什么地方配置更加优秀的人才,合理的调整人才结构和组织结构

人才赋能

迎接未来面对变局的能力

人才管理机制

通过上述内容,沉淀出一套符合自己公司的人才管理机制

小结

从这个课我知道了人才管理不是我之前那么单纯的理解,很容易被这个词午到,人才管理其实不仅仅说的是人才管理,还包括业务布局、组织变阵、人才赋能、机制建立等因素。

所以我理解一下下,人才的定义是不是:专业技能过硬,有战略眼光且能适应业务未来的变局,且有组织向心力?

为什么要谈个人发展?

这个话题其实我很怕的….,因为我发育迟读书少,所以一直都懵懵懂懂+凄凄惨惨戚戚,自认为放荡不羁其实就是无知逗逼。我是从17年开始才意识到有个概念叫“以后的路咋走”,文气点就是个人发展。因为17年开始面临买房、小孩儿上学等一系列问题,深深感到自己的无力,表象就是缺钱,内里就是焦虑。那段时间常常展开双臂抬头望问天“咋整?我该咋整?”,夜深人静常常哭湿了我的小黄人枕巾。就想着不能这样,得变,得抗。

这两年其实我一直在摸索适合自己地发展路径,虽然现在个人发展长远地战略还不是非常明确,但是至少我认识到了个人发展地重要性,并且找到了最近3-5年自己地发展路径。

当然这篇不是来说我的,是结合之前老大的分享以及平时自己的学习来试着说明一下,个人发展的必要性。

**我们先聊聊几个点,看是否能达成共识。达不成共识可能这篇文章对你来说就没什么意义 **

工作压力大,没时间

不用我说大家都知道,个人发展是需要时间来铺垫的,同时个人发展肯定跟工作会有联系,这种联系根据个人的处境可强可弱。另外一个共识点是,工作肯定会压缩我们的可自由支配的时间。所以很多时候我们会得出一个结论,工作压力大,没时间。作为程序员我承认确实会存在这样的情况,究其原因:公司的业务压力大,需要堆人堆时间来跟上时代的节奏。另外一点则是个人的精力有限,就像电池一样,在工作上就把电耗尽了,就算会有一些休息时间,也已经没电了。但是我们可以这么想想:现在的工作强度是否有利于个人发展?得到报酬能否多到让我暂时放弃个人发展的程度?工作压力是否真的有我们想象得那么大,时间是否真的就没了?

所以核心的问题是,我们对个人发展这件事情有多重视?愿意投资多少时间和精力?

我的观点是:如果光感受到工作压力大让你没有时间,但又无法获得一些正反馈,那这不是你有没有时间得问题,而是先考虑这份工作是不是应该继续做下去的问题。如果有正反馈,我们再想想是否现在处境就算我想要的的”个人发展”。

怎么来判断,我通常是这样做的:

  1. 列出来公司的晋升制度,薪酬制度。
  2. 尽可能的根据各种反馈,试着评估一下公司的发展前景或者产品线的发展前景。
  3. 列出来所在职位的工作内容(在不晋升的情况下),以及如果晋升了的工作内容(从与领导交流,加上观察基本上能预估出80%左右)。
  4. 列出来如果因为某些因素,导致被动的离开公司,自己会怎样?

不出意外你列完之后,会说一句“卧槽xxxx”,这样就对了,好好考虑一下个人发展的重要性吧。

有点扯远了,其实我是想说,自己愿意的话,不会没有时间。

工作生活要平衡

我不想这么辛苦,我期望生活和工作平衡

很多人都期望活少钱多还开心,朋友圈那些天天晒美食,旅行,老婆孩子热炕头的生活就是理想人生。每个行业都有其辛苦的部分,这才是现实。有的要付出体力之苦,有的要付出健康的代价,有的要付出脑力之苦。既然选择了薪酬相对高一些的IT行业,持续学习新技术,新知识就是你必须面对的。你是想要今天的生活和工作平衡,还是中年油腻后的生计艰难?

假如你不是富二代,官二代,还是更现实,更长期地看待工作和生活平衡的事情。如果你从事的工作是你热爱的工作,不存在平衡问题,从中获得的成就感就是你生活的一部分。如果你从事的工作是你谋生的手段,想持续获得这个收入,就遵循这个工作的本质规律。

以上是老大的原话我觉得已经说的很透彻了,我认为要想追求工作生活的平衡是不可能的,当然我不是说工作和生活就必须得纠缠在一起才好,我是觉得平衡这词应该是心理上的划分而不是物理上的划分。

个人发展战略

然后我们再来说怎么找到属于自己的个人发展战略,内容总结来自圈外商学院的个人发展战略主体纲要,因为平台不让看了,我就直接不要脸的引用有识之士的了。大家如果觉得有用请去链接点赞,就当我对其微薄的回报了。

一、一个模型,帮你找到真正热爱的工作

  • 冰山模型;美国著名心理学家麦克利兰

    • 显性30%;

      • 知识(专业,书籍),慌乱
      • 技能(编程,技术),焦虑
      • 能力(思考能力,学习能力),挫败低效
    • 隐性70%;

      • 价值观(判断事物标准),矛盾纠结
      • 性格特质(行为偏好),心累
      • 动机(成就动机,权力动机),没热情
    • 四个步骤找到适合的工作

      • 找专业;找自己喜欢的专业和爱好的相关岗位
      • 搜索岗位;在招聘网上,搜索岗位的要求
      • 分析需求;用冰山模型,分析这份岗位需要的知识,技能和能力
      • 对比自己;用冰山模型匹配自己的价值观,性格特质和动机,确定适不适合。

二、四大要素,决定你的市场价值

  • 用冰山模型说明该投入到哪里,才能提升自己的市场价值

    • 投入知识;容易获取,但如果不能运用来解决问题,几乎没有竞争力。
    • 投入技能;有门槛,但市场价值取决于稀缺程度,所有技能都会走向供需平衡,高收入不可持续,需要不断持续学习新技能。
    • 投入能力;能力可迁移,积累到一定高度,行业不对称,也能发挥作用。
    • 投入自我发现;难以改变和发现,但如有清晰的认识,找到相匹配的工作也能提升价值。
  • 为什么大多数人都选择投入到知识和技能,不断碎片化学习,练习并不稀缺的技能。

    • 因为知识和技能容易习得
    • 惰性让自身受限于岗位
    • 没明白工作本质是解决问题
  • 汇总;我们的时间,永远应该花在正确的事情上,而不是容易的事情上。

三、四类迹象,发现你的隐藏能力

    • 虽然成功的道路有千万条,但成功人士基本都在遵循了一个原则,就是将自己的自身天赋发挥到了极致。
    • 天赋就是隐藏的能力,让一个人可以在同样起点的情况下,更加加速成长。
  • 天赋探索方法;“SIGN”

    • 自我效能(Self-efficacy);对某类事情非常有信心,觉得自己肯定能做好。
    • 本能(Instinct);迫不及待想要尝试的事情
    • 成长(Growth);发现自己明显比别人进步的快一些
    • 满足(Needs);做完后及时很累,也会觉得满足
  • 如何在日常中发现自己的SIGN特质

    • 围绕SIGN特质问自己

      • 自我效能;认为自己能教别人什么?别人向你请教什么?跟别人聊天倾向聊什么?聊什么话题会让你感到自信?做什么事情不会感到焦虑和担心?
      • 本能;做什么事的时候很少拖延?长时间休息后,你最想念工作的哪个方面?你宁愿放弃休息时间也要做的事情是什么?
      • 成长;什么事情会让你沉浸其中忘记时间?不容易感到疲累和厌烦?
      • 满足;过去工作和生活中,有什么让你获得巨大的成就感和满足感?
    • 围绕SIGN特质问他人

      • 你觉得我身上有什么不同于别人的特质?

      • 你最欣赏或者佩服我的方面是什么?

      • 在你看来,我做什么的时候最兴奋?

      • 我做过什么让你印象深刻的事情么?

      • 在以下方面,你觉得我那些更加擅长?

        • 思维方式;条理清晰,逻辑严密,脑洞很大,专注专业
        • 沟通协调;化解冲突,争取资源,知人善用
        • 计划执行;执行力强,强求完美,目标导向

四、三种方法,将知识内化成能力

  • 惰性知识;不能及时有效调用,占用大脑内存的知识

  • 如何把知识内化成能力,让自己的能力快速提升的三个方法

    • 1.掌握20%的核心;二八原则,一个领域的20%的核心内容,能够解决80%的问题

      • 如结构化思维20%的三个特征;主题鲜明,归类分明,逻辑递进。
      • 擅长找师傅,找领域内专业人士,了解领域的核心20%
    • 2.知识和问题相互靠

      • 让知识和问题链接起来,知识向问题靠,问题向知识靠。

      • 知识(想应用)问题;看到一个知识就去思考这个知识可以用来解决什么问题?

        • 如;马斯洛的五层次需求,可以用来分析奢侈品为什么这么贵?
      • 问题(找解释)知识,遇到问题,抛弃第一反应,去想想有什么方法论模型可以用

        • 看书遇到有趣的理论,先记下思考3个可以用该模型解决的问题。下次不要下意识反应,学会去翻一下以前的笔记,把问题补充到笔记里。
    • 3.做系统化训练

      • 刻意练习;一个月中,显示每天练习三段式,再提炼主题,然后积累结构
      • 我们的习惯,是高估几天的变化,而低估几个月的变化。
  • 汇总;如何具体到行动中;

  • 1.找专业人士了解领域的核心20%,先去学习20%

  • 2.在学习的过程中,思考各个知识点的应用场景,并记录下来,遇到问题再回去找

  • 3.能力提升是一个系统性过程,所以需要坚持至少1个月。

五、三大系统,让学习不靠意志力

  • 为什么我们如此难坚持

    • 意志力是有限的,并不是学习这件事情类到你,而是“坚持”
    • 移动互联网时代,诱惑越来越多
    • 威尔海姆。霍夫曼主持的一项研究表明;人们醒着的时候,把大约1/4的时间用来抵制欲望。
  • 意志力不够用,就用行为改变来学习。

    • 决定事情时起作用的大脑内部的两个系统;理性和感性。
  • 如何让我们坚持做一件事的三个关键因素;

    • 理性上;知道要学习(树立明确的目标)

      • 明确的目标是坚持学习的第一步
    • 感性上;愿意去学习(利用情绪冲动)

      • 截止日期的紧迫感
      • 比较产生的焦虑感
      • 鼓励带来的成就感
    • 情景上;制造适合学习的场景(创造学习环境)

      • 人是社会动物,我们会观察周围人的行为来调整自己的行为。
      • 如果可以利用环境,就不要用你的意志力来抵制欲望

六、三个建议,让你不做“定制化人才”

  • 1.调整主体,给自己定好发展方向
  • 2.提升能力,让自己成为横向可迁移的人才
  • 3.提升认知高度,让自己成为纵向可拓展的人才

源引感谢:

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

最近看了《抉择》,虽说本身是一本企业管理的书,但是对人自身也是特别有指导意义的…

背景

由于某些客户现场以及我们公司自身的要求,产品上线之前都会有一轮的安全扫描,用AppScan等这类工具扫一扫,最近就处理了几个安全扫描的问题,虽说处理的比较原始但是还是需要记录一下,你晓得哇,因为比较折磨人。

工作原理

什么交XSS、SQL注入网上一大堆我就不出丑了哈,要解决这块问题那就先得搞清楚AppScan这类软件的工作原理。

AppScan 工作原理小结如下:

  • 通过爬行配置的整个 Web 应用,了解其结构找到接口等信息
  • 根据分析的结果,发送修改后的Request 进行攻击尝试(扫描规则库模拟XSS、SQL注入等操作)
  • 最后分析 Respone ,验证是否符合预期是否存在安全漏洞

所以由此看出处理XSS、SQL注入等问题只能在第二个和第三个环节出手。

处理姿势

处理这类问题我所知道的通常就3种姿势:

  1. 拦截

    触发了这类校验直接拦截掉并提示不让其做任何操作,简单粗暴但是不人性化。经常让人很懵逼。

  2. 替换

    把可触发这类校验的关键字全部替换为其它字符或者转换为字符串等。这种容易破坏原有的表达。

  3. 加密

    这是种欺骗扫描软件的方式,直接前后端约定加密方式,对所有的输入进行统一加密,后端再统一解密,这样扫描软件识别不了任何关键字。我同事就这样干过,虽然说性能上会有一点问题但是好在不用动任何代码。嗯,这种只能说骚操作。

实操

最后和架构师定的方式是通过filter+正则这种最原始最简单的方式来做,我们这是toB的运维系统所以让大家失望了没上高大上的安全策略。

输入流

需要先搞清楚为什么需要处理输入流,因为 reqeust.getInputStream 方法只能读取一次。我们可以大概捋一下是咋回事。

我们需要输入流所以需要调用reqest.getInputStream(),getInputStream返回值为ServletInputStream,所以我们先看看ServletInputStream。

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
public abstract class ServletInputStream extends InputStream {
protected ServletInputStream() {
}

public int readLine(byte[] b, int off, int len) throws IOException {
if (len <= 0) {
return 0;
} else {
int count = 0;

int c;
while((c = this.read()) != -1) {
b[off++] = (byte)c;
++count;
if (c == 10 || count == len) {
break;
}
}

return count > 0 ? count : -1;
}
}

public abstract boolean isFinished();

public abstract boolean isReady();

public abstract void setReadListener(ReadListener var1);
}

然后知道是继承自InputStream,所以我们先看InputStream,注意read和reset方法。

read方法告诉我们会从输入流一直读取下一个字节、如果以达到末尾侧返回-1。

reset告诉我们可以重置读取的位置。

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
public abstract class InputStream implements Closeable {

// MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
// use when skipping.
private static final int MAX_SKIP_BUFFER_SIZE = 2048;

/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned. This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
*
* <p> A subclass must provide an implementation of this method.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
*/
public abstract int read() throws IOException;

/**
* Repositions this stream to the position at the time the
* <code>mark</code> method was last called on this input stream.
*
* <p> The general contract of <code>reset</code> is:
*
* <ul>
* <li> If the method <code>markSupported</code> returns
* <code>true</code>, then:
*
* <ul><li> If the method <code>mark</code> has not been called since
* the stream was created, or the number of bytes read from the stream
* since <code>mark</code> was last called is larger than the argument
* to <code>mark</code> at that last call, then an
* <code>IOException</code> might be thrown.
*
* <li> If such an <code>IOException</code> is not thrown, then the
* stream is reset to a state such that all the bytes read since the
* most recent call to <code>mark</code> (or since the start of the
* file, if <code>mark</code> has not been called) will be resupplied
* to subsequent callers of the <code>read</code> method, followed by
* any bytes that otherwise would have been the next input data as of
* the time of the call to <code>reset</code>. </ul>
*
* <li> If the method <code>markSupported</code> returns
* <code>false</code>, then:
*
* <ul><li> The call to <code>reset</code> may throw an
* <code>IOException</code>.
*
* <li> If an <code>IOException</code> is not thrown, then the stream
* is reset to a fixed state that depends on the particular type of the
* input stream and how it was created. The bytes that will be supplied
* to subsequent callers of the <code>read</code> method depend on the
* particular type of the input stream. </ul></ul>
*
* <p>The method <code>reset</code> for class <code>InputStream</code>
* does nothing except throw an <code>IOException</code>.
*
* @exception IOException if this stream has not been marked or if the
* mark has been invalidated.
* @see java.io.InputStream#mark(int)
* @see java.io.IOException
*/
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}

所以从上面可以得知ServletInputStream是没有重写r关键的reset方法,所以行为是与InputStream保持一致的即输入流读取一遍之后就没了后续就一直返回-1。

所以解决办法就是找到重写reset的方法的类,即就找到我们常用的ByteArrayInputStream也是继承自InputStream,但是其重写了reset等方法。

我们看下源码:

注意read里面的pos变量,它是标识现在读取的流的位置,所以如果我们想多次读取输入流,需要调用上面说的reset方法重置pos为0。

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
public
class ByteArrayInputStream extends InputStream {
/**
* The currently marked position in the stream.
* ByteArrayInputStream objects are marked at position zero by
* default when constructed. They may be marked at another
* position within the buffer by the <code>mark()</code> method.
* The current buffer position is set to this point by the
* <code>reset()</code> method.
* <p>
* If no mark has been set, then the value of mark is the offset
* passed to the constructor (or 0 if the offset was not supplied).
*
* @since JDK1.1
*/
protected int mark = 0;

/**
* The index of the next character to read from the input stream buffer.
* This value should always be nonnegative
* and not larger than the value of <code>count</code>.
* The next byte to be read from the input stream buffer
* will be <code>buf[pos]</code>.
*/
protected int pos;
/**
* Reads the next byte of data from this input stream. The value
* byte is returned as an <code>int</code> in the range
* <code>0</code> to <code>255</code>. If no byte is available
* because the end of the stream has been reached, the value
* <code>-1</code> is returned.
* <p>
* This <code>read</code> method
* cannot block.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream has been reached.
*/
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}

/**
* Resets the buffer to the marked position. The marked position
* is 0 unless another position was marked or an offset was specified
* in the constructor.
*/
public synchronized void reset() {
pos = mark;
}

这些搞清楚之后就是看怎么能在到咱们的filter的时候得到的request是可以读取多次同时又不影响其它地方的读取(比如controller),刚好severlet.api提供了一个叫HttpServletRequestWrapper的东西,刚好提供一种包装(专业名词:装饰器模式)的手法让我们可以包装request请求对象使其可扩展其它能力。包装高富帅哪里都吃得开。

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
/**
* @author Gamehu
* @date 2019/5/9 18:32
* @description sql注入问题,前置处理输入流,避免输入流获取一次以后失效导致系统异常
*/
public class xxxHttpServletRequestWrapper extends HttpServletRequestWrapper {
private static final Logger LOGGER = LoggerFactory.getLogger(SqlInjectHttpServletRequestWrapper.class);
/**
* 存储请求输入流
*/
private byte[] body;


public SqlInjectHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
try {
body = inputStreamToByte(request.getInputStream());
} catch (IOException e) {
throw RiilExceptionUtils.bizException(e, BaseWebErrorCode.SecurityConstant.ERROR, BaseWebErrorMsg.SecurityConstant.ERROR_MSG);
}
}

/**
* 流转 字节数组
*
* @param is
* @return
* @throws IOException
*/
private byte[] inputStreamToByte(InputStream is) throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int ch;
while ((ch = is.read(buffer)) != -1) {
byteStream.write(buffer, 0, ch);
}
byte[] data = byteStream.toByteArray();
byteStream.close();
return data;
}

@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(getInputStream()));
}

/**
* 重写方法用于多次获取流,防止读取用于校验过后,后面服务无法获取参数的情况
*
* @return
*/
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {

@Override
public boolean isFinished() {
return false;
}

@Override
public boolean isReady() {
return false;
}

@Override
public void setReadListener(ReadListener readListener) {
LOGGER.info("setReadListener");
}

@Override
public int read() {
return bais.read();
}
};


}

OK,到这儿输入流总算搞定了,nice,然后开始Filter上场了。

Filter

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

/**
* @author Gamehu
* @date 2018/12/20 15:18
* @description XSS、SQL注入校验
*/
@WebFilter(urlPatterns = "/*", filterName = "xxFilter")
public class xxFilter implements Filter {


@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
...
//安全扫描问题(sql注入、xss)处理,对参数进行校验
securityScanParamsValidate(filterChain, response, request);
}


/**
* 安全扫描问题(sql注入、xss)处理,对参数进行校验
*
* @param filterChain
* @param response
* @param request
* @throws IOException
* @throws ServletException
*/
private void securityScanParamsValidate(final FilterChain filterChain, final HttpServletResponse response, final HttpServletRequest request) throws IOException, ServletException {
final String paramsAndValues = SecurityScanUtil.extractPostRequestBody(request);
if (StringUtils.isEmpty(paramsAndValues)) {
filterChain.doFilter(request, response);
} else {
//根据参数是否能转换为json,执行不同的校验
parseParamAndValidate(filterChain, response, request, paramsAndValues);
}

}

private void parseParamAndValidate(final FilterChain filterChain, final HttpServletResponse response, final HttpServletRequest request, final String paramsAndValues) throws IOException, ServletException {
try {
final JSONObject paramsObj = JSONObject.parseObject(paramsAndValues);
if (paramsObj.size() == 0) {
filterChain.doFilter(request, response);
} else {
//对参数进行拆分校验,只校验每个参数值
jsonParamValidate(filterChain, response, request, paramsAndValues);
}

} catch (JSONException ex) {
LOGGER.error("isJSONValid,不是有效的JSON字符串,{}", ex.getMessage());
//参数不是合法地json格式则进行整句校验(不进行任何拆分)
notJsonParamValidate(filterChain, response, request, paramsAndValues);
}
}

/**
* 非json格式参数安全问题校验
*
* @param filterChain
* @param response
* @param request
* @param paramsAndValues
* @throws IOException
* @throws ServletException
*/
private void notJsonParamValidate(final FilterChain filterChain, final HttpServletResponse response, final HttpServletRequest request, final String paramsAndValues) throws IOException, ServletException {
final String uri = request.getRequestURI();
// sql注入校验
if (SecurityScanUtil.execSqlInjectValidate(uri, NO_KEY, paramsAndValues)) {
response.sendRedirect(SQL_INJECT_ERROR);
} else if (XssUtil.xssMatcher(uri, NO_KEY, paramsAndValues)) {
//xss校验
response.sendRedirect(XSS_ERROR);
} else {
filterChain.doFilter(request, response);
}
}

/**
* json格式参数安全问题校验(sql注入、xss)
*
* @param filterChain
* @param response
* @param request
* @param paramsAndValues
* @throws IOException
* @throws ServletException
*/
private void jsonParamValidate(final FilterChain filterChain, final HttpServletResponse response, final HttpServletRequest request, final String paramsAndValues) throws IOException, ServletException {
//防sql注入校验
if (SecurityScanUtil.sqlInjectValidate(request.getRequestURI(), paramsAndValues)) {
response.sendRedirect(SQL_INJECT_ERROR);
} else if (SecurityScanUtil.xssValidate(request.getRequestURI(), paramsAndValues)) {
//xss校验
response.sendRedirect(XSS_ERROR);
} else {
filterChain.doFilter(request, response);
}
}

@Override
public void destroy() {
LOGGER.info("destroy");
}


}
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
/**
*
* @author Gamehu
* @description Xss和Sql注入检查 工具类
* @date 2019/5/10 9:54
*/
public class SecurityScanUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityScanUtil.class);
private static final String GRAPHQL = "graphql";

private SecurityScanUtil() {
}

/**
* 单个参数的sql注入校验
*
* @param paramName
* @param value
* @return
*/
public static String sqlInjectionValidate(final HttpServletRequest request, final String paramName, final String value) {
if (StringUtils.isEmpty(value)) {
return null;
}
/*
* 防sql注入,如果存在抛出校验异常
*/
if (SqlInjectUtil.haveSqlInject(request.getRequestURI(), paramName, value)) {
return "sqlError";
}

return value;
}


/**
* 读取输入流中参数
*
* @param request
* @return
*/

public static String extractPostRequestBody(final HttpServletRequest request) {

final Scanner s;
try {
s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
} catch (IOException e) {
throw RiilExceptionUtils.bizException(e, BaseWebErrorCode.SecurityConstant.INPUTSTREAM_ERROR, BaseWebErrorMsg.SecurityConstant.INPUTSTREAM_ERROR_MSG);
}
return s.hasNext() ? s.next() : "";

}


/**
* 效验sql注入问题
* <h3>SQL注入的几种方式:</h3>
* <pre>
* 1) 使用 ' or 语句,将查询条件扩大,实现破坏性查询(操作)
* 2) 使用 ; 将SQL分成两部分,在后面一部分实现破坏性操作
* 3) 使用注释,将后面的条件取消掉,将查询条件扩大,注意MySQL有三种注释的方法,都需要处理
*
* 为了简化处理,这里只考虑字符串类型参数注入情况(整型等其它类型在应用内部类型转换会失败,所以基本可以忽略)
* </pre>
*
* @return
* @throws IOException
*/
public static boolean sqlInjectValidate(final String uri, final String paramsAndValues) {
//获取参数列表以及参数值
final JSONObject paramsObj = JSONObject.parseObject(paramsAndValues);
final Set<String> keys = paramsObj.keySet();
return keys.stream().anyMatch(key -> haveSqlInjectCondition(uri, paramsObj, key));
}

/**
* 判断是否存在sql注入的表达式
*
* @param uri
* @param paramsObj
* @param key
* @return
*/
private static Boolean haveSqlInjectCondition(final String uri, final JSONObject paramsObj, final String key) {
//graphql 不进行校验
if (key.equalsIgnoreCase(GRAPHQL)) {
return false;
}
final String value = convertParamToString(paramsObj, key);
return SqlInjectUtil.haveSqlInject(uri, key, value);
}

/**
* 效验XSS问题
*
* @param uri
* @param paramsAndValues
* @return
*/
public static boolean xssValidate(final String uri, final String paramsAndValues) {
//获取参数列表以及参数值
final JSONObject paramsObj = JSONObject.parseObject(paramsAndValues);
final Set<String> keys = paramsObj.keySet();
return keys.stream().anyMatch(key -> haveXssCondition(uri, paramsObj, key));
}

/**
* 判断是否存在xss的表达式
*
* @param uri
* @param paramsObj
* @param key
* @return
*/
private static boolean haveXssCondition(final String uri, final JSONObject paramsObj, final String key) {
final String value = convertParamToString(paramsObj, key);
//判断是否存在xss攻击问题
return XssUtil.haveXss(value, key, uri);
}

/**
* 处理参数为字符串
*
* @param paramsObj
* @param key
* @return
*/
private static String convertParamToString(final JSONObject paramsObj, final String key) {
final Object obj = paramsObj.get(key);

String value = null;

if (obj instanceof JSONObject) {
value = ((JSONObject) obj).toJSONString();
}

if (obj instanceof JSONArray) {
value = ((JSONArray) obj).toJSONString();
}

if (obj instanceof String) {
value = (String) obj;
}
return value;
}

}
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

/**
* @author Gamehu
* @date 2019/9/26 12:12
* @description XSS校验的工具类
*/
public class XssUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(XssUtil.class);
private static final String GRAPHQL = "graphql";
private static Pattern[] patterns = new Pattern[]{
// Script fragments
Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE),
// src='...'
Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// lonely script tags
Pattern.compile("</script>", Pattern.CASE_INSENSITIVE),
Pattern.compile("<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// eval(...)
Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// expression(...)
Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL),
// javascript:...
Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE),
// vbscript:...
Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE),
// onload(...)=...
Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)
};


/**
* 判断是否存在xss攻击
*
* @param value
* @param key
* @param uri
* @return true表示存在,false表示不存在
*/
public static Boolean haveXss(final String value, final String key, final String uri) {
if (key.equalsIgnoreCase(GRAPHQL)) {
return false;
}
return xssMatcher(value, key, uri);
}

public static boolean xssMatcher(final String value, final String key, final String uri) {
if (StringUtils.isNotBlank(value)) {
Matcher matcher;
for (Pattern pattern : patterns) {
matcher = pattern.matcher(value);
// 匹配
if (matcher.find()) {
LOGGER.error("存在xss风险,URI:{},参数:{},参数值:{}", uri, key, value);
return true;
}
}

}
return false;
}

}
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

/**
* @author Gamehu
* @date 2019/9/26 12:12
* @description SQL注入校验的工具类
*/
public class SqlInjectUtil {
/**
* SQL语法检查正则:只检查一个关键字可能存在误判情况,这里要求必须符合两个关键字(有先后顺序)才算匹配
* 第一组关键字
*/
final static String sqlInjectGroup = "select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|drop|execute";

/**
* 构造SQL语法检查正则
* (?i)忽略字母的大小写,\s.*空白+字符
*/
final static Pattern sqlSyntaxPattern = Pattern.compile("(?i)(.*)\\b(" + sqlInjectGroup + " )\\b\\s.*", Pattern.CASE_INSENSITIVE);


/**
* 读取输入流中参数
*
* @param request
* @return
*/

public static String extractPostRequestBody(final HttpServletRequest request) {

final Scanner s;
try {
s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
} catch (IOException e) {
throw RiilExceptionUtils.bizException(e, BaseWebErrorCode.SecurityConstant.INPUTSTREAM_ERROR, BaseWebErrorMsg.SecurityConstant.INPUTSTREAM_ERROR_MSG);
}
return s.hasNext() ? s.next() : "";

}

/**
* 执行SQL注入校验
*
* @param uri
* @param key
* @param oldValue
* @return
*/
public static boolean haveSqlInject(final String uri, final String key, final String oldValue) {
if (StringUtils.isNotBlank(oldValue)) {
//统一转为小写
final String newValue = oldValue.toLowerCase();
final String logStr = "存在sql注入风险,URL:{},参数:{},参数值:{}";
// 检查是否包含SQL注入敏感字符
if (sqlSyntaxPattern.matcher(newValue).find()) {
LOGGER.error(logStr, uri, key, newValue);
return true;
}
}
return false;
}

}

因为我们用了graphql,有些地方还用了dsl,所以正则是魔鬼,我写崩溃了差点,当然如果有更好的方法请告诉我,万分感谢。

长出一口气…..

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

高难度沟通发生的三个场景

矛盾、争取、推进

近段时间刚好,做的分享和听的分享较多。刚好缘分就是那么奇妙,在一个网课平台刚好看到了这个网课《如何提高Presentation的展现能力》,在此做个记录。

图1所阐述的观点,是我个人觉得做得最契合的。确实我一直认为,Presentation别弄太多文字类或者花哨的东西,PPT应该是辅助我们说明白一个事情,所以主次需要分清。

图2所说几个点

  • 底稿我觉得如果是很正式或者很重要的Presentation是个很好的减少风险的点。

  • 另外关于互动,这点是很重要的,不管是眼神还是肢体还是语言,他能让讲解人和听众建立联系,如果没有互动,那就感觉像是下命令了,你们只管听我只管讲,不能有异议这样的场景特别糟糕。

  • 时间管理特别重要,我特别厌烦那种拖沓的,慢条斯理的Presentation,让氛围很疲惫,这种效果很差,这点我也需要改进,虽然我很注意时间控制,但是也没有做到掐表的程度,但是我认为有些情况是可以掐表,比如做分享。

图3是我最没有底气的点,Presentation事后的修正和反思做得比较少,特别是修正,要想整个Presentation比较有质量,确实这块是不可或缺的,尽量不因此留下遗憾。

Presentation:我觉得可解释为:具有分享、议题、同步等性质的会议。

图片摘自:

嗯,跟前端暧昧的时间还是挺长了,是时候展现真正的技术了。

常在写代码哪有不bug的道理,那对于前端朋友来讲chrome的工具栏基本上能满足大多数场景下的bug排查,对于奉行假如你暂时不够牛逼,那就善用工具的我来说,我心荡漾啊。必须记录一下,当然chrome提供的工具太多了,今天暂且主要聊聊Sources,也是我使用频率Top 2,结合一些网上的一些说明加自身的实践来说一说。

不用再于数字不好看这些细节,我们走的是放荡不羁的路线,咱们挨个说啊。

数字 表头
1 点击该箭头,移动鼠标到页面上定位到页面元素,跳转到Elements 工具栏。
2 用于模拟移动设备上的效果。
3 必用的模块,已加载的全部资源,以域名划分文件夹,调试时从这儿下手找源码,当然我通常直接快捷键Ctrl+P定位文件。
4、5 Filesystem & Overrides 可以加载本地文件夹,把Chrome当成IDE用。
6 Content scripts 扩展工具的脚本,比如百度翻译插件等
7 Snippets 代码片段,不会因为刷新丢失,使用:添加=>保存(ctrl+s)=>运行(Run)=>不用则移除(Remove)
8 源码面板,在此处进行打断点、修改端点等操作
9 调试的快捷键面板
10 变量监察:添加个变量后会一直监察这个变量的值,当前作用域无值时显示< not availble >
11 Call Stack 函数调用栈,会列出断点的调用堆栈列表。
12 Scope 断点所在作用域列表,级别划分如下:
  • Local 当前作用域 展示作用域下的变量
  • Closure (x) 闭包作用域,x是函数名称
  • Script 标签作用域
  • Global 全局作用域Window
13 Breakpoints 源码的断点列表。
14 XHR/fetch Breakpoints 请求断点:ajax和fetch请求都可以在这里打断点并在Call Stack显示调用栈,很方便追踪。
15 DOM Breakpoints 这里列出html的断点。
16 Global Listeners 全局监听器:指的是绑定在 window 对象上的事件。
17 Event Listeners Breakpoints 所有事件的断点:勾选指定类型,比如Mouse/click,则所有的click事件都会被断住。

markdown搞出这个表格太费劲,anyway看着舒服就好,下面聊聊对应上面表格中的一些具体的使用场景。

  • Filesystem Chrome式的IDE,可以编辑各种文件并且在当前页就可以实时刷新看到效果,如果写单个模块时蛮有用的,比较快。修改后ctrl+s保存,修改的是本地文件,刷新可看到效果。
  • Overrides 覆盖网络请求的资源,即html、js、css、图片等资源,注意一定是同域名同路径同名的文件才能产生覆盖效果。勾选,Enable Local Overrides,修改文件后ctrl+s保存,修改的是Save as Overrides到本地的文件,刷新可看到效果。

  • Snippets,也是我使用比较多的功能,以前不知道的时候都是找网上现场的js编辑器,但是受网络和维护的影响,有些时候不好用,而且很多还要区分ES5、ES6,自从有了Snippets,随便写各种代码片段超级好用。 但是要注意变量的作用域问题,所以最好用IIFE方式写代码,避免出现错误。

源码面板

  • 这也是调试的时候使用频率超级高的区域,ctrl+p定位到文件,行号处右键出现对断点操作的一些选项,分新增和编辑两类。
  • Add conditional breakpoint/edit breakpoint ,添加/修改带条件的断点,比如写a===”a”,则表示当a等于”a”时才触发断点,如下图。当条件表达式为真时,触发断点(条件性行断点的颜色为橙色)
  • Blackbox Script,黑盒脚本,很多时候是要引用第三方库或框架的,当我们调试时,调试的对象应该是我们自己写的代码,但很多时候,我们经常在焦灼地进行下一步下一步时,突然代码跳到了第三方库或框架的源码上去,多数情况下我们不会关注这些地方的内容,但是它就要跳过去,这种是比较蛋疼的。黑盒脚本就是用来解决这个问题的,它能够把一个脚本文件标记为 “Blackbox Script”,那么我们就永远不可能进入这个文件内部,这个文件对我们来讲就是一个黑盒子。为什么要强调“永远”呢?因为不仅普通的断点不能访问这个被标记了的脚本,其他的,比如说 DOM 断点、事件断点等等都无法访问那个脚本文件内部。

  • 调试面板:Pause on exceptions,在发生异常的地方停顿

Watch面板

  • 用于监视变量的值。

点击1处,将打开一个内联输入框,您可以在输入框中输入要监视的变量名称。输入完毕,按Enter键,即可将其添加到列表中。监视器将显示添加时变量的当前值。如果变量未设置或找不到,值将显示为

点击2手动刷新变量。注:监视列表不是变量的实时视图,除非逐步执行。当使用断点逐步执行时,监视列表中的值将自动更新

点击3,删除变量。

Call stack

代码暂停时,可以在Call Stack窗口查看当前的调用栈。展示了代码到暂停处的完整执行路径,这让我们能够深入代码去找出导致错误的原因。最后调用函数在最顶上,所以最好别用匿名函数,不利于调用栈查看。

Scope

作用域:显示断点所在的作用域,级别划分如下:

Local 当前作用域 展开作用域下的变量
Closure (x) 闭包作用域,x是函数名称
Script 标签作用域
Global 全局作用域Window

DOM Breakpoints

当改变一个节点或者其子元素时,可以设置一个DOM断点:

  1. 点击Elements面板

  2. 找到想要设置断点的元素

  3. 在此元素上右键

  4. Break on –> Subtree modifications / Attribute modifications / Node removal

    Subtree modifications(子树修改):当前选中的元素,删除、增加其子代或者改变其子代的内容。修改子元素的属性或者当前选中元素有任何改变都不会触发此类型断点
    Attributes modifications(属性修改):当前选中的元素,增加、删除其属性,或者修改某个属性值
    Node Removal(节点移除):移除当前选中的元素

XHR/Fetch Breakpoints

当XHR的请求URL包含某一特定的字符串时,可以暂停调试代码。DevTools会在XHR调用send()那行代码的地方暂停。

  1. 点击Sources面板
  2. 展开XHR Breakpoints小窗口
  3. 点击“增加断点(Add breakpoint)”
  4. 输入一个字符串,只要在某个XHR的请求URL中包含此字符串, 会进入断点(暂停),如下图。

Event Listeners Breakpoints

这个断点类型也算是比较常用的一个了,特别是当我们调试别人的代码时,触发某个事件,想找到对应的代码。事件类型可以是很具体的,比如click事件,也可以是某一类别的事件,比如“鼠标事件”。

  1. 点击Sources面板
  2. 展开Event Listener Breakpoints小窗口
  3. 勾选某一类别的事件或者是某一具体的事件

PS:

感谢:

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

今天看到一篇文章,是以我媳妇儿的男神马云老师为引得文章。

所有为了能更完美的适配我媳妇儿的诉求,当然我就毫不犹豫的要点进去的看看。

我心甚慰啊,是这两天引发我思考最多的文章,特此记录一下。

文章指出底层系统定义:那些驱动你的核心动力。

体现在两个方面:大局观和是非观。

是非观决定你能走多久。什么事能做什么事不能做,自己心里衡量。有界限。

大局观决定你能走多远。你想进步你的格局就要大于你的现状。

所以前者指引方向,后者决定边界。

所以当你想做什么的时候,先自省一下,自己的底层系统,看有没有bug。

其中大局观是我感慨最多也是我认为自己最需要严肃对待的。

虽然我这两年都在有意识的往这方面努力,但是还远远不够。

记录下我粗糙的认识

我认为的大局观,首先需要对抗的就是,老守着自己的一亩三分地,事不关己高高挂起,满足于现状的状态(这也是我在正在经历的阶段),需要跳脱出来,换角度,纵向、横向的看自己看周围看世界。

说起来有点虚,我一开始看别人动不动用跳脱、升华、灵魂这类词语我就特别不屑,总觉得的是吹牛x。但是亲测真的有效,很多时候做某件事的时候我就强迫自己,重新审视一下要做的事。

那怎么才能重新审视呢,那就涉及到要需要换角色换角度了,那怎么才能换角色换角度呢,这个时候就会突然在头脑中会出现,自己扮演产品经理、研发总监(公司副总)、总经理的角色,另外还会出现一个专门发出质疑的“自己”。这时候你就会萌发很多很多想法,这时候你会发现你考虑事项的方式以及处理事项的方式往往会和你一开始你想的有出入(当然这里肯定有一个边界,那就是你得从你的现状出发),这种出入不见得一定优于之前的想法,但是它肯定会带来附加效果,这种附加效果有可能会来的快也可能来得晚,有可能好有可能坏,但是正因为这样我们才能不断修正自己,我想这种修正应该会让我向大局观进一步。

最近在看一本书《系统之美》,看起来有些艰涩,但是真的很受用,我觉得其中有句话就很符合我对大局观的理解

重塑系统,发现更大的世界

对于怎么才叫有大局观,我其实也没摸到精确的定义,但是这不妨碍我想得到她。人神奇的地方不就是能不断的折腾吗。能捏碎也能重塑。

最后

我觉得作为程序员,别顽固(坚守哪个语言第一、接受不了改变…)、别看谁都不顺眼(你看那说话的SB、你看那写代码的SB、你看那群里发图的SB….)、别老想着对抗(产品、ucd、测试、运维….,感觉他么的一个公司全是你的敌人),我们应该多想想如果我保持现状到底跟自己的职业生涯、跟同事、跟领导、跟公司能带来什么。

我们是不是得抽时间找找自己?

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。

当你不够牛逼时,那就善用工具

我简单整理了一下我最近比较常用的工具,也会摘一些做一些分析,多偏向于前端,因为最近前端写得多。

chrome devtools

由于尽量不想让自己在编码路上留下的足迹会让后人觉得像屎一样…年初到现在断断续续的都在想办法提升个人的工程能力

关于Lint最早是前两年写了一段时间的nodejs,然后知道了一个叫ESLint的玩意…这东西好用啊,让代码可以不太像屎一样了所以最近就捡起来了,当然工具是辅助最重要的还是基础。

伴随着格言 do not BB,show me code。刚好同事让我帮忙跟他弄一下ESLint,那我想着干脆整个文档,那不就简单了,大家都能用。

以下就是跟我们项目贴合度比较高的ESLint简单的配置教程出来(为博客凑个数,话说确实太懒了文章写的少),然后有时间再分析其原理的东西。

ESLint 配置

安装ESLint相关库

npm install -g eslint 
// 用了 React 需要再安一个babel-eslint  
npm install -D eslint babel-eslint
//如果要用大厂的需安装对应的库,比如用airbnb的
npm install -D eslint-config-airbnb
// 因为要校验 Reac 语法,所以这里需要下载一个 React 语法规则的包  
npm install -D eslint-plugin-react

可能不全,但是总之一句话,别想太多提示差什么包就装什么包就对了。

生成配置文件

node_modules/.bin/eslint --init
//如果全局安装了 可以直接
eslint --init  

会出现一个界面,根据模板创建ESLint的配置文件,最终填完之后大概就是下面这样

演示一把:

选一种方式生成配置文件,可根据需求选择,建议第一个问题选第二种Use a popular style guide(使用大厂的),然后选一个,通常用airbnb的. 跟着界面依次填写下面的内容,最终会在你根目录下生成一个文件名为.eslintrc.js的配置文件 。

根据模板创建配置文件,可以选个模板,也可以自己回答问题

PS E:\eslint> eslint --init
? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? (Use arrow keys)
> Airbnb (https://github.com/airbnb/javascript)
  Standard (https://github.com/standard/standard)
  Google (https://github.com/google/eslint-config-google)

比如选airbnb

module.exports = {
"extends": "airbnb"
};

也可以自己回答问题来创建

> eslint --init

? How would you like to configure ESLint? Answer questions about your style
// 是否校验 Es6 语法
? Are you using ECMAScript 6 features? Yes  
// 是否校验 Es6 模块语法
? Are you using ES6 modules? Yes   
// 代码运行环境,Browser 指浏览器 
? Where will your code run? Browser   
// 是否校验 CommonJs 语法  
? Do you use CommonJS? Yes  
// 是否校验 JSX 语法
? Do you use JSX? Yes   
// 是否校验 React 语法
? Do you use React? Yes 
// 首行空白选择 Tab 键还是 Space
? What style of indentation do you use? Tabs
// 字符串使用单引号 'string' 还是双引号 "string"
? What quotes do you use for strings? Double
// 操作系统
? What line endings do you use? Windows 
// 每行代码结尾是否校验加分号 ;
? Do you require semicolons? Yes
// 以 .js 格式生成配置文件
? What format do you want your config file to be in? JavaScript   

PS:也可以直接在根目录下手动建一个文件.eslintrc.js(后缀也可以是JSON、YAML等),然后手动写配置

贴一个我的(手动写的)

如果只用于JS语法类扫描,可参考但不建议直接使用,因为配置的东西有点多

module.exports = {
"extends": ["airbnb", "prettier","prettier/react"],
"parser": "babel-eslint",
//如果我们想对一些非标准 JS 语法添加 Lint 怎么办呢?有办法,ESLint 还支持我们自定义 parser。 parser是为了非标准语法能生的,plugin是针对符合js语法的规则集合的扩展。
"plugins": [
    "prettier",
    "react",
    "jsx-a11y",
    "import"],
//自定义规则,可以覆盖掉extends的配置。
"rules": {
    "jsx-a11y/click-events-have-key-events":0,
    "jsx-a11y/interactive-supports-focus":0,
    "jsx-a11y/no-static-element-interactions ":0,
    // 'one-var':2,//"error" 或 2 开启规则,使用错误级别的错误
    // 强制驼峰命名规则
    "camelcase": [0, {
        "properties": "never"
    }],
    "prettier/prettier": ["error"],
    "react/prop-types": ["warn"],
    "react/jsx-uses-react": "error",
    "react/jsx-uses-vars": "error",
    "no-unused-vars": 1,//"warn" 或 1 - 开启规则,使用警告级别的错误
    "global-require": 0,//"off" 或 0 - 关闭规则
    "prefer-destructuring": 0,
    "class-methods-use-this": 0,
    "react/no-unused-state": 1,
    "jsx-a11y/no-static-element-interactions":0,
    "import/extensions":0
},
"env":{
    //定义env会带进来一些全局变量,browser会添加所有的浏览器变量比如Windows
    "browser": true,
    "es6": true
},
//当我们将默认的解析器从Espree改为babel-eslint的时候,我们需要指定parseOptions,这个是必须的。parserOptions ESLint 允许你指定你想要支持的 JavaScript 语言选项。默认情况下,ESLint 支持 ECMAScript 5 语法。你可以覆盖该设置,以启用对 ECMAScript 其它版本和 JSX 的支持。
"parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
        "jsx": true// 启用 JSX
    },
    "ecmaVersion": 6
},

};`

配置IDE

VSCode配置需自行上网搜索

启用ESLint插件

配置触发ESLint自动修复的快捷键

ESLint的使用方法

本文引用的内容,如有侵权请联系我删除,给您带来的不便我很抱歉。