This is my thrid blog web site.

0%

Nginx 反向代理支持websocket

记录一下域名反向代理的nginx配置

环境

1
2
uname -a # Linux VM-8-3-ubuntu 5.4.0-77-generic #86-Ubuntu SMP Thu Jun 17 02:35:03 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
nginx -v # nginx version: nginx/1.18.0 (Ubuntu)

nginx配置

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

upstream wss_svr {
server 127.0.0.1:8080;
}

map $http_upgrade $connection_upgrade {
default keep-alive;
'websocket' upgrade;
}

server {
listen 80;
server_name aaa.bbb.ccc;

location / {
proxy_http_version 1.1;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_pass http://wss_svr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwared-For $proxy_add_x_forwarded_for;
}
}

Java-apache.shiro.crypto.hash SimpleHash

想仔细了解一下后端认证的做法,于是来看下相关源码。

基本的做法是。网络请求得到明文的password和salt,然后将salt转成hash与数据库中的hash比对。

将password和salt转成hash的过程中,用到了Sha256Hash这个类。所以想看下这个类做了什么。

1
2
3
public class Sha256Hash extends SimpleHash {}

public class SimpleHash extends AbstrachHash {}

SimpleHash 继承 AbstrachHash,那么直觉上AbstractHash应该是官方提供的一个实现hash的抽象类。

Sha256Hash

Sha256Hash的源码不长,基本就是创建了一个默认algorithm=SHA-256的一个SimpleHash,那么还是得看一下SimpleHash做了什么。

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
public class Sha256Hash extends SimpleHash {
public static final String ALGORITHM_NAME = "SHA-256";

public Sha256Hash () {super("SHA-256");}

public Sha256Hash (Object source) { super("SHA-256", source) }

public Sha256Hash (Object source, Object salt) {
super("SHA-256", source, salt);
}

public Sha256Hash(Object source, Object salt, int hashIterations) {
super("SHA-256", source, salt, hashIterations);
}

public static Sha256Hash fromHexString(String hex) {
Sha256Hash hash = new Sha256Hash();
hash.setBytes(Hex.decode(hex));
return hash;
}

public static Sha256Hash fromBase64String(String base64) {
Sha256Hash has = new Sha256Hash();
hash.setBytes(Base64.decode(base64));
return hash;
}
}

SimpleHash

SimpleHash的构造方法最终都会调用这个构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public SimpleHash(String algorithmName, Object source, Object salt, int hashIterations) throws CodeException, UnknownAlgorithmException {
this.hexEncoded = null;
this.base64Encoded = null;
if (!StringUtils.hasText(algorithmName)) {
throw new NullPointerException("algorithmName argument cannot be null or empty.");
} else {
this.algorithmName = algorithmName;
this.iterations =Math.max(1, hashIterations);

ByteSource saltBytes = null;
if (salt != null) {
saltBytes = this.convertSaltToBytes(salt);
this.salt = saltBytes;
}

ByteSource sourceBytes = this.convertSourceToBytes(source);
this.hsah(sourceBytes, saltBytes, hashIterations);
}
}

在构造方法中,主要做的是

  • 判断参数是否为空
  • 将Object类型的参数转化为ByteSource类型
  • 调用 hash方法

ByteSource

先来看看ByteSource,他是做什么的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ByteSource {
byte[] getBytes();

String toHex();

String toBase64();

boolean isEmpty();

public static final class Util {
//...
//用于从各种类型的实例中中构造SimpleByteSource实例
}
}

这样看好像看不出什么东西。那就去看看转化为ByteSource的方法中做了什么事情.

convertSaltToBytes & convertSourceToBytes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected ByteSource convertSaltToBytes (Object salt) {
return this.toByteSource(salt);
}

protected ByteSource convertSourceToBytes (Object source) {
return this.toByteSource(source);
}

protected ByteSource toByteSource(Object o) {
if (o == null) {
return null;
} else if (o instanceof ByteSource) {
return (ByteSource)o;
} else {
byte[] bytes = this.toBytes(o);
return Util.bytes(bytes);
}
}

上面的两个核心部分是

  • this.Bytes(o)
  • Util.Bytes(bytes)

this.Bytes(o)

toBytes 这个方法是在CodeSupport这个类里面的

该方法做的事情有:

  • 判断传入是否为空
  • 如果传入为 byte[], ByteSource, char[], String, File, InputStream, 则执行对应的转换为bytes的操作
  • 大致上最终是通过方法进行获取bytes
    • 方法
      • String.getBytes(encoding)
    • 默认
      • encode是utf-8

Util.Bytes(bytes)

这里的Util是[ByteSource](‘### ByteSource’)中的静态工具类

1
2
3
public static ByteSource bytes(byte[] bytes) {
return new SimpleByteSource(bytes);
}

可以看到这也是返回了一个SimpleByteSource实例,先不展开。

this.hash

得到ByteSource的实例之后,调用hash。

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
private void hash(ByteSource source, ByteSource salt, int hashIterations) throws CodeException, UnknownAlgorithmException {
byte[] saltBytes = salt != null ? salt.getBytes() : null;
byte[] hashedBytes = this.hash(source.getBytes(), saltBytes, hashIterations);
this.setBytes(hashedBytes);
}

protected byte[] hash(byte[] bytes, byte[] salt, int hashIterations) throws UnknownAlgorithmException {
MessageDigest digest = this.getDigest(this.getAlgorithmName());
if (salt != null) {
digest.reset();
digest.update(salt);
}

byte[] hashed = digest.digest(bytes);
int iterations = hashIterations -1;

for(int i =0; i < iterations; ++i) {
digest.reset();
hashed = digest.digest(hashed);
}

return hashed;
}

public void setBytes(byte[] alreadyHashedBytes) {
this.bytes = alreadyHashedBytes;
this.hexEncoded = null;
this.base64Encoded = null;
}

protected MessageDigest getDigest(String algorithmName) throws UnknownAlgorithmException {
try {
return MessageDigest.getInstance(algorithmName);
} catch (...) {
...
}
}

在这部分中可以看见,最终实现从source + salt => byte 是通过MessageDigest这个类来实现的,于是我们来看看MessageDigest这个类

MessageDigest

MessageDigest 是 java.security这个包中的类。相关源码先不看,只看看这几个方法做了什么

  • MessageDigest.getInstance(algorithmName)
  • digest.reset();
  • digest.update();
  • digest.digest(bytes);

getInstance

获取单例,但是具体怎么获取的,挺复杂的。
最终通过main方法调试debug的方式获取到了SHA-256对应的MessageDigest实现类的className

1
2
3
4
5
6
7
public static void main(String args) {
Sha256Hash sha256Hsah = new Sha256Hash();
MessageDigest MessageDigest = sha256Hsah.getDigest(sha256Hsah.getAlgorithmName());
//这里输出的也不是具体的实现结果
System.out.println(digest.getClass().getName())
}
//最终找到当AlgorithmName=SHA-256时,加载了sun.security.provider.SHA2$SHA256这个类
1
2
3
4
5
6
7
8
9
abstract class SHA2 extends DigestBase {


public static class SHA256 extends SHA2 {
private static final int[] INITIAL_HASHES = new int[]{....};

public SHA256() { super("SHA-256", 32, INITIAL_HASHES)}
}
}

其他部分的实现

其他部分最终会在DigestBase 和 SHA2这个两个类中得到实现

DigestBase

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
abstract class DigestBase extends MessageDigestSpi implements Cloneable {
private byte[] oneByte;
private final String algorithm;
private final int digestLength;
private final int blockSize;
byte[] buffer;
private int bufOfs;
long bytesProcessed;
static final byte[] padding = new byte[136];

DigestBase (String var1, int var2, int var3) {
this.algorithm = var1;
this.digestLength = var2;
this.blockSize = var3;
this.buffer = new byte[var3];
}

protected final int engineGetDigestLength() { return this.digestLength;}

protected final void engineUpdate(byte var1) {
if(this.oneByte == null) {
this.oneByte = new Byte[1];
}

this.oneByte[0] = var1;
this.engineUpdate(this.oneByte, 0,1);
}

protected final void engineUpdate(byte[] var1, int var2, int var3) {
if (var3 != 0) {
if (var2 >= 0, && var3 >= 0 && var2 <= var1.length - var3) {

}
}
}
}

SHA2

AbstrachHash

如何实现登陆认证

  • 后端
  • 前端

后端

  • 相关api
  • 登陆明文与密码hash匹配
  • 登陆逻辑
    • 从数据库获取该用户
    • 判断验证码
    • 根据source和salt生成hash
      ·

      前端

面试-头条-Android

  • 实习介绍
    • miui
    • 商汤
  • 算法
    • 数字、字母混合串,无正负号, 找出其中的最大数字, 最大数字不超过int; “abc123xyz34567ab” –> 34567
  • Activity生命周期
    • onNewIntent什么时候
    • onStart onResume onPause onStop
    • 有onStop吗
  • SharedPreference
    • 单个文件, io操作
    • put get commit
  • 进程和线程
    • 跨线程安全问题
      • 原子 valite
      • asynchron
  • ClassLoader 双亲
    • 怎么去除加载拦截
      • 继承根的ClassLoader,然后加载
  • 网络
    • 5层
    • tcp udp
      • 三次握手
  • 怎么学习
  • 算法
    • 求两个单链表是否有交叉节点,找出交叉点
1
2
// 数字、字母混合串,无正负号, 找出其中的最大数字, 最大数字不超过int;  “abc123xyz34567ab” --> 34567
//正则然后map, sort就好了 简单
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
// 求两个单链表是否有交叉节点,找出交叉点
import java.util.Scanner;

//{1} -> {2} -> {3} -> {4} -> {5} -> null
// {7} -> {4} -> {5} -> null

// 1 7;
// 2 4;
// 3 6;
// 4 1;
// 5 2;
// 7 3;
// 4 4;
// 6 5;
// null null;
// 7 1;
// 4 2;
// 6 3;
// null 4;
// 7

class Node {
int val;
Node next;


}



public class Main {
public static void main(String[] args) {


}

public Node findX(Node l1, Node l2) {
Node cl1 = l1;
Node cl2 = l2;
boolean hasChange1 = false;
boolean hasChange2 = false;

while(cl1 != null
&& cl2 != null
&& cl1.equals(cl2)) {
cl1 = cl1.next;
cl2 = cl2.next;

if (cl1 == null && !hasChange11) {
cl1 = l2;
hasChange1 = !hasChange11;
}

if (cl2 == null && !hasCange2) {
cl2 = l1;
hasChange2 = !hasChange2;
}
}

return cl1;
}
}

  • 一面
    • 启动模式
      • stackTop 生命周期
    • handler
    • handler机制
    • handler原理
    • looper
    • 线程
    • 协程
      • kotlin js里面有 其他不知道
    • 自定义view
    • 动画
    • 修改颜色的动画
    • list
    • hashmap hashtable
      • hashmap 原理啥的
    • 开源库
      • retrofit
      • okhttp
    • activity 通信
    • fragment 创建
    • fragmentmanager trasation
    • fragment之间通信
    • activity通信
    • 我说了一下不用fragment直接上view 问了一下
    • thread里面looper
    • looper.prepare
      • 只说了单例 其他没说出来
    • thread sleep wait 区别
      • 3个线程依次执行
    • 算法
      • [“a”,”b”,”c”] [“c”,”d”,”e”,”f”] 找到 多的 少的 重复的
  • 二面
    • 提到了launcher
      • launcher启动的时候有个广播
        • 这个广播是在启动前还是启动后
    • launcher的实现
    • 进程和线程
      • 多进程用处
        • 保活
        • 已失效
    • 比较有自信的项目
      • miui的资源替换

Javascript 定时器

  • node定时器
    • 同步任务
    • 异步任务
      • 浏览器
        • setTimeout
        • setInterval
      • node + 浏览器
        • setImmediate
        • process.nextTick()

同步任务 > 异步任务

  • 异步任务

    • 本轮循环
      • process.nextTick和promise的回调追加在本轮循环,同步任务之后
    • 次轮循环
      • setTimeout
      • setInterval
      • setImmediate
  • 在本轮循环中

    • 同步任务
    • process.nextTick
    • 微任务
      • promise的回调函数
  • 次轮循环

例如:

1
2
3
4
5
6
7
8
9
10
11
12
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
new Promise((resolve, reject) => {
console.log(3);
resolve();
})
.then(() => {
console.log(4);
});
console.log(5);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(1);
process.nextTick(() => {
console.log(2);
process.nextTick(() => {
console.log(3);
});
});
setTimeout(() => {
console.log(4);
}, 10);
setImmediate(() => {
console.log(5);
});
while (Math.random() < 0.99999);
console.log(6);

cankao
NodeJs 定时器详解

JavaScript WebWorker

  • web worker
    • 创建
      • new Worker(‘worker.js’,{name : ‘myw’})
      • new Worker(window.URL.createObjectURL(new Blob([‘console.info(“worker”)’])))
    • 信息传递
      • postMessage
      • onMessage
      • addEventListener(‘event-tag’, callback)
      • onError
        • addEventListener (‘error’ callback)
    • 关闭
      • worker.terminate
      • self.close
    • 以拷贝方式传递数据

参考
Javascript高级编程
Web Worker

  • nodejs 模块化规范
    • exports 和 module.exports
    • 引用顺序
    • 闭包解决
    • 面向对象开发
      • 闭包始终返回对象
    • YUI
    • commonJS
      • 原生module模块,每个文件都是一个module实例
      • 文件内部通过require对象引入指定模块
      • 所有文件加载同步完成
      • 通过module关键字暴露内容
      • 每个模块加载一次之后就会被缓存
      • 模块编译本质是沙箱编译
      • 使用了nodeapi ,只能在服务端运行
    • AMD 和 RequireJS
      • AMD Asynchronous Module Definition
        • 异步方式加载模块
          • 模块的加载不影响后续代码使用
          • 所有依赖该模块的语句定义在回调中,等依赖项加载完后回调函数运行
      • RequireJS 是AMD的实现
        • define(id?, dependencies?, factory)
        • 动态创建script标签
          • onload监听加载完毕
    • CMD 和 SeaJs
      • SeaJs是CMD的实现
    • ES6 中的模块化
      • 新增关键字
        • import
        • from
        • as
        • export
        • default
      • 输出值引用
      • 编译时输出接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// YUI - 编写模块
YUI.add('dom', function(Y) {
Y.DOM = { ... }
})

// YUI - 使用模块
YUI().use('dom', function(Y) {
Y.DOM.doSomeThing();
// use some methods DOM attach to Y
})

// hello.js
YUI.add('hello', function(Y){
Y.sayHello = function(msg){
Y.DOM.set(el, 'innerHTML', 'Hello!');
}
},'3.0.0',{
requires:['dom']
})

// main.js
YUI().use('hello', function(Y){
Y.sayHello("hey yui loader");
})
1
2
3
4
// 沙箱编译
(function (exports, require, module, __filename, __dirname) {
//原始文件内容
})();

参考
JavaScript模块化发展

  • ‘\\\‘.replace(new RegExp(‘\\\\‘, ‘gi’), ‘/‘)
    • 根据字符串构造正则的时候会发生转义,构造出来的字符串又会发生转义