springSecurity Bcrypt hash password

SpringSecurity之加密篇—BCryptpassword类

  1. 要点概括
    BCryptpassword类是SpringSecurity的加密工具,封装了对密码混淆加密的方法,主要是采用盐(salt)对原始密码进行混淆。
    本篇介绍的是利用BCryptpassword随机生成盐(salt),使用该盐值对原始密码进行混淆加密。
    这种加密方式有两个特点:(1)将随机生成的盐(salt)存到混淆后的代码中;(2)对于相同的明文每一次加密,加密之后的密文都是不一样的;因为盐值(salt)不同。这样的好处就是更增加了密码的安全性。

SpringSecurity中的BCryptPassword采用Hash处理,其过程是不可逆的。

BCryptPassword的加密过程:
(1)加密(encode):用户注册时,使用SHA256+盐(salt)把用户输入的密码进行hash混淆处理,得到密码的hash值,然后将其存入数据库中。
(2)密码校验(matches):用户登录时,密码匹配阶段并没有进行密码解密(因为密码经过Hash处理,是不可逆的),而是使用相同的算法结合盐值salt把用户输入的密码进行hash处理,得到密码的hash值,然后将其与从数据库中查询到的密码hash值进行比较。如果两者相同,说明用户输入的密码正确,这里的盐值(salt)是从数据库中查询到的密码hash值中解析出来的。

  1. 核心代码分析
    //1. BCryptPasswordEncoder类,生成盐salt并混淆rawpassword的代码;
    //涉及到两个问题,生成salt盐gensalt(),混淆密码hashpw()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public String encode(CharSequence rawPassword) {
    String salt;
    if (this.strength > 0) {
    if (this.random != null) {
    salt = BCrypt.gensalt(this.strength, this.random);
    } else {
    salt = BCrypt.gensalt(this.strength);
    }
    } else {
    salt = BCrypt.gensalt();
    }

    return BCrypt.hashpw(rawPassword.toString(), salt);
    }

2.1 生成盐(salt):
:包名:org.springframework.security.crypto.bcrypt;类名:BCrypt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//生成salt盐gensalt() 
public static String gensalt(int log_rounds, SecureRandom random) {
if (log_rounds >= 4 && log_rounds <= 31) {
StringBuilder rs = new StringBuilder();
byte[] rnd = new byte[16];
random.nextBytes(rnd);
rs.append("$2a$");
if (log_rounds < 10) {
rs.append("0");
}

rs.append(log_rounds);
rs.append("$");
encode_base64(rnd, rnd.length, rs);
return rs.toString();
} else {
throw new IllegalArgumentException("Bad number of rounds");
}
}

2.2 混淆方法:
包名:org.springframework.security.crypto.bcrypt;类名:BCrypt

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
//混淆密码hashpw()
public static String hashpw(String password, String salt) throws IllegalArgumentException {
char minor = 0;
int off = false;
StringBuilder rs = new StringBuilder();
if (salt == null) {
throw new IllegalArgumentException("salt cannot be null");
} else {
int saltLength = salt.length();
if (saltLength < 28) {
throw new IllegalArgumentException("Invalid salt");
} else if (salt.charAt(0) == '$' && salt.charAt(1) == '2') {
byte off;
if (salt.charAt(2) == '$') {
off = 3;
} else {
minor = salt.charAt(2);
if (minor != 'a' || salt.charAt(3) != '$') {
throw new IllegalArgumentException("Invalid salt revision");
}

off = 4;
}

if (saltLength - off < 25) {
throw new IllegalArgumentException("Invalid salt");
} else if (salt.charAt(off + 2) > '$') {
throw new IllegalArgumentException("Missing salt rounds");
} else {
int rounds = Integer.parseInt(salt.substring(off, off + 2));
String real_salt = salt.substring(off + 3, off + 25);

byte[] passwordb;
try {
passwordb = (password + (minor >= 'a' ? "\u0000" : "")).getBytes("UTF-8");
} catch (UnsupportedEncodingException var13) {
throw new AssertionError("UTF-8 is not supported");
}

byte[] saltb = decode_base64(real_salt, 16);
BCrypt B = new BCrypt();
byte[] hashed = B.crypt_raw(passwordb, saltb, rounds);
rs.append("$2");
if (minor >= 'a') {
rs.append(minor);
}

rs.append("$");
if (rounds < 10) {
rs.append("0");
}

rs.append(rounds);
rs.append("$");
encode_base64(saltb, saltb.length, rs);
encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
return rs.toString();
}
} else {
throw new IllegalArgumentException("Invalid salt version");
}
}
}

2.3 密码的校验
提供的密码与加密之后的密码的校验,使用是BCryptpassword的matches()接口;

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (encodedPassword != null && encodedPassword.length() != 0) {
if (!this.BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
this.logger.warn("Encoded password does not look like BCrypt");
return false;
} else {
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
} else {
this.logger.warn("Empty encoded password");
return false;
}
}

/**
 * 密码比较入口,同样是调用了hashpw()方法: 使用的是hashpw()方法,有两个参数:原始明文与数据库中获取的密文。
 * hashpw()方法是从密文中解析出藏在其中的盐salt值,用此值混淆明文,与密文做比较。
 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   public static boolean checkpw(String plaintext, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
}
//这个方法就是比较是否相同而已,无他。
static boolean equalsNoEarlyReturn(String a, String b) {
char[] caa = a.toCharArray();
char[] cab = b.toCharArray();
if (caa.length != cab.length) {
return false;
} else {
byte ret = 0;

for(int i = 0; i < caa.length; ++i) {
ret = (byte)(ret | caa[i] ^ cab[i]);
}

return ret == 0;
}
}
  1. 总结
    以上是SpringSecurity的BCryptPassword加密方式,上面是介绍其SHA256+随机salt生成密文的基本点。应该还有其他的一些用法,容当后研究。

  2. 其它
    Springboot框架整合SpringSecurity组件,使用需要使用该加密方式,有一点需要注意,就是在springSecurity的配置文件中注入PasswordEncode的bean

    //SpringSecurityConfigutarion配置类中加入
    @Bean
    public PasswordEncoder passwordEncoder() {

    return new BCryptPasswordEncoder();
    

    }
    代码示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void main(String[] args) {
    String password = "leo_epam";
    System.out.println(password + ": encrypt");
    int i = 0;
    while(i < 10){
    BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
    String hashedPassword = passwordEncoder.encode(password);
    System.out.println("encryptPassword:" + hashedPassword);
    System.out.println("match result:" + passwordEncoder.matches(password,hashedPassword));
    i ++;
    }
    }

代码输出:

leo_epam: encrypt
encryptPassword:$2a$10$/WIa4YbBMGQX7dBbYwRKx.AmQ3sJ8Ta5wYHh8a7jPlYDbNgMSD/hC
match result:true
encryptPassword:$2a$10$VryRC1lFaTEqUbOUyvu18ulSSGH5hK7JPzAG17ehtkL8aV6fot1ru
match result:true
encryptPassword:$2a$10$0SVRXbfgsqMvpmNDeeqNl.eFPvQ0ojdazk5.x8YDZxIOOP1D4xXge
match result:true
encryptPassword:$2a$10$yWdI4q6DXDZhknTxiT/9ROdhcAARAEY5q4L2YI5uQ3C52Q7Lt/IAa
match result:true
encryptPassword:$2a$10$8yy0.NdQSD2Cig5yIri2eu1XmDRpZSPjsmxDyRg9CZ5afwp/36H2S
match result:true
encryptPassword:$2a$10$QTJpm3jYUCjAoHPVI/uon.FDusm.9tPSc.mk6m.l/kx8aKbIzov3i
match result:true
encryptPassword:$2a$10$CkoGrZqDE86LM1yiX89cpuNDAMdDqemEKMSS3/jquFsxocizgBbX2
match result:true
encryptPassword:$2a$10$6M0N6cQp6kKeXgRl8ftqyOlikAV9YwfMS93xlqTXbd/tmDjFDv3iG
match result:true
encryptPassword:$2a$10$LboMUNAF7vOmucxJI3G/w.cliDRxH1exOmsfy2IlPCUpZU8N7XoO.
match result:true
encryptPassword:$2a$10$ezB0okY.JHv1gt4Y3chiu.5e5R9.vXqd7kNaYH14Vigm9wZ02D8Pe
match result:true
以上代码就是BCryptPassword的代码简单效果示例,相同的明文,每次生成的密文都是不同的,但是和明文做密码校验都是通过的。
还有一个就是密码字段的长度,如果打算采用bcrypt加密存储,字段长度不得低于60.

Hibernate

  1. Hibernate的工作原理是什么?为什么要用hibernate?

(1)Hibernate可以理解为一个中间件。它负责把java程序的SQL语句接收过来并发送到数据库,而数据库返回的信息由Hibernate接收后直接生成一个对象传给java.

在Hibernate中有两个特有的文件,一个是以.hbm.xml结尾的映射文件,一个是以.cfg.xml结尾的配置文件。.cfg.xml文件的作用是连接数据库,文件内部其实就是一个由user,password,url,driver组成的链接库的基本信息。.hbm.xml文件是对数据库中表的映射文件。

(2)Hibernate工作原理

①读取并解析hibernate.cfg.xml配置文件;

②由hibernate.cfg.xml中的读取并解析映射信息;

③.通过SessionFactory sf = config.buildSessionFactory();创建SessionFactory

④Session session = sf.openSession();//打开Sesssion

⑤Transaction tx = session.beginTransaction();//创建并启动事务Transation

⑥persistent operate操作数据,持久化操作

⑦tx.commit();//提交事务

⑧关闭Session

⑨关闭SesstionFactory

3)使用Hibernate的原因如下:

① 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。

②HIbernate是基于JDBC的主流持久化框架,是一个优秀的对象关系映射实现,它在很大程度上简化了DAO层的编码工作;

③Hibernate使用java反射机制,而不是字节码增强程序来实现透明性;

④Hibernate性能非常好,因为它是一个轻量级框架,映射的灵活性很出色,它支持各种关系数据库,从一对一到多对多的各种复杂关系。

为什么要持久化? 持久化技术封装了数据访问细节,为大部分业务逻辑提供面向对象的API。 ● 通过持久化技术可以减少访问数据库数据次数,增加应用程序执行速度; ● 代码重用性高,能够完成大部分数据库操作; ● 松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码。

被amazon开出两次的经验总结

1.政治斗争: fight all days, impossible to work

  1. status: hardworking, burn the middnight oil

收获: 学会了亚马逊的生存法则
为什么离开: 需要pip, ??
进去三个月被dev, 第二次因为组里launch新的项目,没有和其他组员抢活,被安排做operating&& bug之类的活,导致没有大项目来support 年度review

  1. 把项目技术挖深了,确保工作安全。
    和老板沟通,确定目前最重要的活是什么,focus在老板最关心的活上。确定掌握核心资源的是谁,积极主动去合作,拿可见度最高的活儿。
  2. 学会说不,组里同事塞给你,一般是知道是坑,学会优雅避开,比如我觉得xxx优先级比较高,我先focus on做完x,别的组找你帮忙,尽量给点帮助,别花太多时间,如果需要花很多时间,赵老板,老板会帮你退掉。
    4.留证据
    每周把做过的事给老板总结,要feedback,可以提高自己,也可以留证据,别人要开你的话,你都有wittern证据。
  3. 别的组问问题,不要给书面承诺,比如我会帮你debug之类,第一对方可能得寸进尺。第二,等别人要你帮忙的时候你可能没时间了真,问别人组的问题,尽量要书面email node, 或者可以开个简会马上把meeting note发出来,没人能赖账。

  4. 后来加一点,能不去tech debt重的组就就尽量不去,天天修东西,没时间学习,没时间做项目。比如某语音助手的内核组。

March/30/2019 Study plan

leetcode:

  1. 3 union find
  2. 3 dp
  3. 3 breadfirst search
  4. binary index tree & segment tree

Resume: finish 4 project and get some stuff of the compony.

React: Redux review

System design one question: grokking the System design

Using formik library

react factory and functional component

React factory pattern: react.createClass();
functioal component only return component while it is opposite of factory pattern. can combination with Redux data flow.
such as container component and presental component, which is pretty much like factory pattern, but the data is connect to the redux status.

factory pattern

1
Factory  react example:

export default class SlideFactory {
static build(data) {
switch (data.source) {
case ‘brand’:
return ;
case ‘article’:
return ;
default:
return undefined;
}
}
}

1
2
3
4
5
6
7
8
```
const items = [
{
source: ‘brand’,
title: ‘title’
}
]
const renderedItems = items.map((item) => SlideFactory.build(item));

the items can have properties for different kinds of slider.

// A design without factory pattern

#include
using namespace std;

// Library classes
class Vehicle {
public:
virtual void printVehicle() = 0;
};
class TwoWheeler : public Vehicle {
public:
void printVehicle() {
cout << “I am two wheeler” << endl;
}
};
class FourWheeler : public Vehicle {
public:
void printVehicle() {
cout << “I am four wheeler” << endl;
}
};

// Client (or user) class
class Client {
public:
Client(int type) {

    // Client explicitly creates classes according to type 
    if (type == 1) 
        pVehicle = new TwoWheeler(); 
    else if (type == 2) 
        pVehicle = new FourWheeler(); 
    else
        pVehicle = NULL; 
} 

~Client()   { 
    if (pVehicle) 
    { 
        delete[] pVehicle; 
        pVehicle = NULL; 
    } 
} 

Vehicle* getVehicle() { 
    return pVehicle; 
} 

private:
Vehicle *pVehicle;
};

// Driver program
int main() {
Client pClient = new Client(1);
Vehicle
pVehicle = pClient->getVehicle();
pVehicle->printVehicle();
return 0;
}

1
2
3
4
5
6
7
8
 What is the problems with above design?
As you must have observed in the above example, Client creates objects of either TwoWheeler or FourWheeler based on some input during constructing its object.
Say, library introduces a new class ThreeWheeler to incorporate three wheeler vehicles also. What would happen? Client will end up chaining a new else if in the conditional ladder to create objects of ThreeWheeler. Which in turn will need Client to be recompiled. So, each time a new change is made at the library side, Client would need to make some corresponding changes at its end and recompile the code. Sounds bad? This is a very bad practice of design.



How to avoid the problem?
The answer is, create a static (or factory) method. Let us see below code.

// C++ program to demonstrate factory method design pattern

#include
using namespace std;

enum VehicleType {
VT_TwoWheeler, VT_ThreeWheeler, VT_FourWheeler
};

// Library classes
class Vehicle {
public:
virtual void printVehicle() = 0;
static Vehicle* Create(VehicleType type);
};
class TwoWheeler : public Vehicle {
public:
void printVehicle() {
cout << “I am two wheeler” << endl;
}
};
class ThreeWheeler : public Vehicle {
public:
void printVehicle() {
cout << “I am three wheeler” << endl;
}
};
class FourWheeler : public Vehicle {
public:
void printVehicle() {
cout << “I am four wheeler” << endl;
}
};

// Factory method to create objects of different types.
// Change is required only in this function to create a new object type
Vehicle* Vehicle::Create(VehicleType type) {
if (type == VT_TwoWheeler)
return new TwoWheeler();
else if (type == VT_ThreeWheeler)
return new ThreeWheeler();
else if (type == VT_FourWheeler)
return new FourWheeler();
else return NULL;
}

// Client class
class Client {
public:

// Client doesn't explicitly create objects 
// but passes type to factory method "Create()" 
Client() 
{ 
    VehicleType type = VT_ThreeWheeler; 
    pVehicle = Vehicle::Create(type); 
} 
~Client() { 
    if (pVehicle) { 
        delete[] pVehicle; 
        pVehicle = NULL; 
    } 
} 
Vehicle* getVehicle()  { 
    return pVehicle; 
} 

private:
Vehicle *pVehicle;
};

// Driver program
int main() {
Client pClient = new Client();
Vehicle
pVehicle = pClient->getVehicle();
pVehicle->printVehicle();
return 0;
}
`

In the above example, we have totally decoupled the selection of type for object creation from Client. The library is now responsible to decide which object type to create based on an input. Client just needs to make call to library’s factory Create method and pass the type it wants without worrying about the actual implementation of creation of objects.

Design high quality React component

Design high quality React component

作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序能够运行,还要让自己的代码可读而且易于维护。这样才能开发出高质量的软件.

构建高质量React组件的原则和方法

### 划分组件边界的原则 ###

### React组件的数据种类 ###

### React组件的生命周期 ###

React组件设计的基础知识,因为React应用都是围绕组件的设计,所以关于组件的设计介绍将贯穿全书.

我会继续用react,四点理由:第一,“继续”说明我17年就在用react,最熟悉。虽然vue我也熟悉,毕竟放了一年了。angular我确实不太熟悉。第二,react生态现在确实非常好。而且前端生态的下一步进展(全链路,设计开发打通,组件市场,在线ide什么的),目前看来基于react的探索居多,未来会有技术红利。第三,不得不承认jsx真是好用,模板scope和js scope合一,而且可以面向过程任意分解render函数。不得不说,jsx的设计思想我当时花了几个月才真正理解其高明。第四,react有一个3k大小的小兄弟叫preact,这个非常有用。因为我们有时候希望把组件打包成不依赖任何框架的内嵌模块发布,但是把框架打包进去,会很大。这时把组件适配到preact,把preact打包进去就好了。
作者:匿名用户
链接:https://www.zhihu.com/question/266823404/answer/409100494
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

props and state new example

[转载]https://www.jianshu.com/p/3cb5026edee8

  1. props传值,直接在组件初始化的时候赋予参数

    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://zhangpeiyue.com/wp-content/uploads/2018/08/react.development.js"></script>
    <script src="https://zhangpeiyue.com/wp-content/uploads/2018/08/react-dom.development.js"></script>
    <script src="https://zhangpeiyue.com/wp-content/uploads/2018/08/babel.min_.js"></script>
    </head>
    <body>
    <div id="wrap"></div>
    </body>
    <script type="text/babel">
    class MyComponent extends React.Component {
    //此处的构造器是可以省略的
    constructor(props){
    super(props);
    }
    render() {
    return <div>好神奇! {this.props.siteName}!</div>;
    }
    }
    var element = <MyComponent siteName="zhangpeiyue.com"/>;
    ReactDOM.render(
    element,
    document.querySelector("#wrap")
    );
    </script>
    </html>
  2. 通过defaltProps 添加默认的props

defaultProps是一个对象,放添加的值到defaultProps的属性中即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyComponent extends React.Component {
render() {
//此处返回 <div>小张今年18岁了</div>
return <div>
{this.props.userName}今年{this.props.age}岁了!
</div>;
}
}
//为组件添加默认属性 userName与age
MyComponent.defaultProps={
userName:"小张",
age:18
}
var element = <MyComponent/>;
ReactDOM.render(
element,
document.querySelector("#wrap")
);

  1. 可以通过父组件设置state,然后子组件通过props接收父组件的state值
    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
    //父组件
    class MyComponent extends React.Component {
    constructor(){
    super();
    //设置sate,添加name与age属性
    this.state={
    name:"张培跃",
    age:18
    }
    }
    render() {
    return <div>
    <Name name={this.state.name}/>
    <Age age={this.state.age}/>
    </div>;
    }
    }
    //子组件Name
    class Name extends React.Component{
    render(){
    return <div>{this.props.name}</div>
    }
    }
    //子组件Age
    class Age extends React.Component{
    render(){
    return <div>{this.props.age}</div>
    }
    }
    var element = <MyComponent/>;
    ReactDOM.render(
    element,
    document.querySelector("#wrap")
    );

4.使用propTypes进行类型检测

React提供了可以对Props进行验证的功能PropTypes。PropTypes为组件类自身的属性,提供了很多验证器,来验证传入的数据是否有效。当传入的数据无效时,JavaScript控制台会抛出警告。
另外需要注意的是,在开发环境下,当你使用了一个无效的值作为prop时,控件台会出现警告;在生产环境下,为了性能考虑会将PropTypes忽略掉!
要想使用propTypes,首先要对prop-type.js文件进行引入。

1
<script src="https://zhangpeiyue.com/wp-content/uploads/2018/08/prop-types.js"></script>

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var name="老大"
var age=18;
class MyComponent extends React.Component {
render() {
return <div>
{this.props.name}今年{this.props.age}岁了!
</div>;
}
}
//propTypes是组件类的静态属性
MyComponent.propTypes={
//将name设置为string类型
name:PropTypes.string,
//将age设置为number类型
age:PropTypes.number
};
var element = <MyComponent name={name} age={age} />;
ReactDOM.render(
element,
document.querySelector("#wrap")
);

以上示例当中的name与age不合法时,会弹出类型不符的警告!所以在项目开发中使用PropTypes进行对props的验证还是很有好处的!

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
More Validator:
MyComponent.propTypes = {
// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,

// 可以被渲染的对象 numbers, strings, elements 或 array
optionalNode: React.PropTypes.node,

// React 元素
optionalElement: React.PropTypes.element,

// 用 JS 的 instanceof 操作符声明 prop 为类的实例。
optionalMessage: React.PropTypes.instanceOf(Message),

// 用 enum 来限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

// 可以是多个对象类型中的一个
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),

// 指定类型组成的数组
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

// 指定类型的属性构成的对象
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

// 特定 shape 参数的对象
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),

// 任意类型加上 `isRequired` 来使 prop 不可空。
requiredFunc: React.PropTypes.func.isRequired,

// 不可空的任意类型
requiredAny: React.PropTypes.any.isRequired,

// 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
}
}

现在我们来总结下,props与state的区别:

props是指组件间传递的一种方式,props自然也可以传递state。由于React的数据流是自上而下的,所以是从父组件向子组件进行传递;另外组件内部的this.props属性是只读的不可修改!
state是组件内部的状态(数据),不能够直接修改,必须要通过setState来改变值的状态,从而达到更新组件内部数据的作用。

props和state是经常要结合使用的,父组件的state可以转化为props来为子组件进行传值。在这种情况下,子组件接收的props是只读的,想要改变值,只能通过父组件的state对其进行更改。