Nginx获取用户真实IP

首先强调的是,这里需要两层nginx,用户访问nginx1,转发到nginx2(192.168.1.111),nginx2到真实后端。

nginx1 配置

1
2
3
4
5
6
7
8
9
10
server{
...
listen 8888;
location /test {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $remote_addr;
proxy_pass http://nginx2:8888/test2;
}
}

nginx2 配置

1
2
3
4
5
6
7
8
9
server{
...
listen 8888;
location /test2 {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-real-ip $remote_addr;
default_type text/html;
return 200 'This is text!';
}

测试

用户访问

1
curl -i -H "X-Forwarded-For: 110.110.110.110" -H "X-real-ip: 110.110.110.110" -s nginx1:8888/test -v

nginx1 日志:

1
192.168.1.110 0.000 - [12/Sep/2018:11:01:51 +0800] "GET /test HTTP/1.1" 200 13 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 110.110.110.110 110.110.110.110

nginx2 日志:

1
192.168.1.111 0.000 - [12/Sep/2018:11:01:51 +0800] "GET /test2 HTTP/1.1" 200 13 - "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 192.168.1.110 110.110.110.110

其中192.168.1.111为nginx1的ip

可以看到,在nginx2中可以拿X-real-ip获取用户的真实ip,在后端中可以拿这个头信息。

注意!必须要规定好nginx是在架构的哪一层级,根据所处的层级配置,否则该方法无效。

Docker on SailfishOS

How to install Docker on SailfishOS/如何将Docker安装到SailfishOS

This post will show you how to install Docker on SailfishOS, and some hacks need to do.

这篇文章将介绍如何将Docker安装到SailfishOS上,和需要做的一些hack。

Prerequisites/先决条件

https://docs.docker.com/install/linux/docker-ce/binaries/#install-daemon-and-client-binaries-on-linux

  • A 64-bit installation
  • Version 3.10 or higher of the Linux kernel. The latest version of the kernel available for you platform is recommended.
  • iptables version 1.4 or higher
  • git version 1.7 or higher
  • A ps executable, usually provided by procps or a similar package.
  • XZ Utils 4.9 or higher
  • A properly mounted cgroupfs hierarchy; a single, all-encompassing cgroup mount point is not sufficient. See Github issues #2683, #3485, #4568).

  • 64位系统

  • 3.10内核或更高
  • iptable版本至少是1.4
  • git版本至少1.7
  • 可以执行ps
  • xz工具版本至少4.9
  • 正确安装的cgroupfs层次结构; 一个单一的,无所不包的cgroup挂载点是不够的。

Check Kernel support/检查内核支持

Use this script check-config.sh
使用这个脚本 check-config.sh

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
[[email protected] ~]$ ./check-config.sh 
info: reading kernel config from /proc/config.gz ...

Generally Necessary:
- cgroup hierarchy: properly mounted [/sys/fs/cgroup]
- CONFIG_NAMESPACES: enabled
- CONFIG_NET_NS: enabled
- CONFIG_PID_NS: enabled
- CONFIG_IPC_NS: enabled
- CONFIG_UTS_NS: enabled
- CONFIG_CGROUPS: enabled
- CONFIG_CGROUP_CPUACCT: enabled
- CONFIG_CGROUP_DEVICE: enabled
- CONFIG_CGROUP_FREEZER: enabled
- CONFIG_CGROUP_SCHED: enabled
- CONFIG_CPUSETS: enabled
- CONFIG_MEMCG: enabled
- CONFIG_KEYS: enabled
- CONFIG_VETH: enabled
- CONFIG_BRIDGE: enabled
- CONFIG_BRIDGE_NETFILTER: enabled (as module)
- CONFIG_NF_NAT_IPV4: enabled
- CONFIG_IP_NF_FILTER: enabled
- CONFIG_IP_NF_TARGET_MASQUERADE: enabled
- CONFIG_NETFILTER_XT_MATCH_ADDRTYPE: enabled
- CONFIG_NETFILTER_XT_MATCH_CONNTRACK: enabled
- CONFIG_NETFILTER_XT_MATCH_IPVS: enabled
- CONFIG_IP_NF_NAT: enabled
- CONFIG_NF_NAT: enabled
- CONFIG_NF_NAT_NEEDED: enabled
- CONFIG_POSIX_MQUEUE: enabled
- CONFIG_DEVPTS_MULTIPLE_INSTANCES: enabled

Optional Features:
- CONFIG_USER_NS: enabled
- CONFIG_SECCOMP: enabled
- CONFIG_CGROUP_PIDS: missing
- CONFIG_MEMCG_SWAP: enabled
- CONFIG_MEMCG_SWAP_ENABLED: enabled
(cgroup swap accounting is currently enabled)
- CONFIG_MEMCG_KMEM: enabled
- CONFIG_RESOURCE_COUNTERS: enabled
- CONFIG_BLK_CGROUP: enabled
- CONFIG_BLK_DEV_THROTTLING: missing
- CONFIG_IOSCHED_CFQ: enabled
- CONFIG_CFQ_GROUP_IOSCHED: missing
- CONFIG_CGROUP_PERF: enabled
- CONFIG_CGROUP_HUGETLB: missing
- CONFIG_NET_CLS_CGROUP: enabled
- CONFIG_CGROUP_NET_PRIO: enabled
- CONFIG_CFS_BANDWIDTH: missing
- CONFIG_FAIR_GROUP_SCHED: enabled
- CONFIG_RT_GROUP_SCHED: enabled
- CONFIG_IP_VS: enabled
- CONFIG_IP_VS_NFCT: enabled
- CONFIG_IP_VS_RR: enabled
- CONFIG_EXT3_FS: enabled
- CONFIG_EXT3_FS_XATTR: enabled
- CONFIG_EXT3_FS_POSIX_ACL: enabled
- CONFIG_EXT3_FS_SECURITY: enabled
- CONFIG_EXT4_FS: enabled
- CONFIG_EXT4_FS_POSIX_ACL: missing
- CONFIG_EXT4_FS_SECURITY: enabled
enable these ext4 configs if you are using ext4 as backing filesystem
- Network Drivers:
- "overlay":
- CONFIG_VXLAN: enabled
Optional (for encrypted networks):
- CONFIG_CRYPTO: enabled
- CONFIG_CRYPTO_AEAD: enabled
- CONFIG_CRYPTO_GCM: enabled
- CONFIG_CRYPTO_SEQIV: enabled
- CONFIG_CRYPTO_GHASH: enabled
- CONFIG_XFRM: enabled
- CONFIG_XFRM_USER: enabled
- CONFIG_XFRM_ALGO: enabled
- CONFIG_INET_ESP: enabled
- CONFIG_INET_XFRM_MODE_TRANSPORT: enabled
- "ipvlan":
- CONFIG_IPVLAN: missing
- "macvlan":
- CONFIG_MACVLAN: enabled
- CONFIG_DUMMY: missing
- "ftp,tftp client in container":
- CONFIG_NF_NAT_FTP: enabled
- CONFIG_NF_CONNTRACK_FTP: enabled
- CONFIG_NF_NAT_TFTP: enabled
- CONFIG_NF_CONNTRACK_TFTP: enabled
- Storage Drivers:
- "aufs":
- CONFIG_AUFS_FS: missing
- "btrfs":
- CONFIG_BTRFS_FS: enabled
- CONFIG_BTRFS_FS_POSIX_ACL: enabled
- "devicemapper":
- CONFIG_BLK_DEV_DM: enabled
- CONFIG_DM_THIN_PROVISIONING: missing
- "overlay":
- CONFIG_OVERLAY_FS: enabled
- "zfs":
- /dev/zfs: missing
- zfs command: missing
- zpool command: missing

Limits:
- /proc/sys/kernel/keys/root_maxkeys: 1000000

[[email protected] ~]$

Generally Necessary must be all enabled, if not enabled, you must enable it in your kernel defconfig, and rebuild kernel.
Generally Necessary 部分必须全部是enabled, 如果没有启用,必须启用然后重启编译内核。

Download the static binary archive/下载静态二进制文件

https://download.docker.com/linux/static/stable/aarch64/

Extract the archive and put them to /usr/bin/, 18.06 is a working version.

Add nemo to docker group/将nemo用户添加到docker组

1
2
groupadd docker
usermod -a -G docker nemo

Run Docker/启动Docker

Start docker daemon/ 启动docker守护进程
devel-su /usr/bin/dockerd

Or use systemd/ 或者使用systemd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStartSec=0
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s

[Install]
WantedBy=multi-user.target

Check version/检查版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[[email protected] nemo]# docker version

Client:
Version: 18.06.1-ce
API version: 1.38
Go version: go1.10.3
Git commit: e68fc7a
Built: Tue Aug 21 17:20:38 2018
OS/Arch: linux/arm64
Experimental: false

Server:
Engine:
Version: 18.06.1-ce
API version: 1.38 (minimum version 1.12)
Go version: go1.10.3
Git commit: e68fc7a
Built: Tue Aug 21 17:27:20 2018
OS/Arch: linux/arm64
Experimental: false

Test/测试
devel-su docker run hello-world

This command downloads a test image and runs it in a container. When the container runs, it prints an informational message and exits. / 这个命令会下载一个测试镜像,如果执行成功会打印如下信息

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
[[email protected] nemo]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
255483503861: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdc
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(arm64v8)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/

Test network mapping /测试网络映射

On one terminal/在一个终端中执行

1
2
3
[[email protected] nemo]# docker run -it --rm -p 6080:80 nginx:latest        
172.17.0.1 - - [05/Sep/2018:08:54:52 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.58.0-DEV" "-"
172.17.0.1 - - [05/Sep/2018:08:55:51 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.58.0-DEV" "-"

Vist on another terminal/在另一个终端中访问

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
[[email protected] ~]$ curl -s 127.0.0.1:6080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
[[email protected] ~]$

TODO

Wayland forward /wayland转发

Reference/参考:

Have fun ;)

从iOS迁移到SailfishOS计划

本文作为一个实验性计划,从iOS迁移到SailfishOS

首先我们来整理一下软件替代品

  • 即时通讯类
  • 拍照类
  • 金融类
  • 新闻类
  • 文本阅读类
  • 浏览器
  • 地图导航类
  • 智能穿戴设备手机端
  • SNS类
  • 一些工具类

下面展开以上几种类型有哪些及替代品

即时通讯类

  • QQ
  • 微信
  • Telegram
  • IRC

拍照类

  • 暂无需求

新闻类

  • IT之家等
  • 网易新闻等

文本阅读类

  • 暂无需求

浏览器

  • Chrome

地图导航类

  • 高德地图

智能穿戴设备手机端

  • 暂无

SNS类

  • 微博

一些工具类

  • 翻译/查询快递/等等

#### 未完待续…

本文准备弃坑

参考如下:

https://raimue.blog/2018/01/09/goodbye-sailfish-os-and-jolla/

清理kubernetes中未正常退出的pod

长时间运行的k8s节点可能会存在某些pod不自动退出,一直处于Terminating的状态
于是我们可以用这个脚本定时进行清理

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
#!/bin/bash
#############################
### clean terminated pods ###
### run at you own risk ! ###
#############################
export PATH=/usr/local/cfssl/bin:/usr/local/docker/:/usr/local/kubernetes/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
getns(){
namespaces=`kubectl get namespaces|grep -v "NAME"|awk '{print $1}'`
for n in ${namespaces};
do
pods_str=`kubectl get pods -n ${n}|grep "Terminating"`
IFS=$'\n' read -rd '' -a pods <<<"$pods_str"
if [ -n "$pods" ]; then
getpod ${n} $pods;
fi
done
}
getpod(){
ns=$1;
for podinfo in $2;
do
pod=`echo $podinfo|awk '{print $1}'`
delpod $pod $ns;
done
}
delpod(){
echo "kubectl delete pods $1 -n $2 --grace-period=0 --force"
kubectl delete pods $1 -n $2 --grace-period=0 --force
}
main(){
getns
}
main

No subject alternative DNS name matching xxx.com found

[TOC]

故事经过 TLDR;

故事太长,可以直接看这里 解决

昨天下午,突然有同事说OA打不开了,打开页面一看出现如下错误:

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
500 Servlet Exception
[show] java.security.cert.CertificateException: No subject alternative DNS name
matching xxx.com found.

java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
No subject alternative DNS name matching xxx.com found.
at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:328)
at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:291)
at org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:32)
at org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:187)
at org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:164)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
at com.xxx.plugin.AuthenticationFilter.doFilter(AuthenticationFilter.java:163)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
at org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:65)
at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:187)
at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:265)
at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:273)
at com.caucho.server.port.TcpConnection.run(TcpConnection.java:682)
at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:730)
at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:649)
at java.lang.Thread.run(Thread.java:662)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException:
No subject alternative DNS name matching xxx.com found.
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1699)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)

内心中第一反应是证书的问题,于是赶紧上cas服务器查看日志,一切正常😵
然后上OA的服务器,将证书导入,重启服务,该是什么错还是什么错。。。

上谷歌一查,应该是匹配不到证书里的DNS名,但是确实是有的呀。

由于还有其他java系统接入了CAS登录,都是正常的,于是开始怀疑是OA那台的有问题,开始查看是不是有人最近改动过什么

然鹅并没有。。。

然后查看OA的日志(用的Resin中间件。。。
发现如下错误:

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
[17:00:15.288] {http--8080-6$1533061820} java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching auth.corp.flamingo-inc.com found.
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:295)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:33)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:178)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.validation.AbstractTicketValidationFilter.doFilter(AbstractTicketValidationFilter.java:132)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
[17:00:15.288] {http--8080-6$1533061820} at com.xxx.plugin.AuthenticationFilter.doFilter(AuthenticationFilter.java:163)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:110)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.FilterFilterChain.doFilter(FilterFilterChain.java:87)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.webapp.WebAppFilterChain.doFilter(WebAppFilterChain.java:187)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.dispatch.ServletInvocation.service(ServletInvocation.java:265)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.http.HttpRequest.handleRequest(HttpRequest.java:273)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.server.port.TcpConnection.run(TcpConnection.java:682)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:730)
[17:00:15.288] {http--8080-6$1533061820} at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:649)
[17:00:15.288] {http--8080-6$1533061820} at java.lang.Thread.run(Thread.java:662)
[17:00:15.288] {http--8080-6$1533061820} Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching auth.corp.flamingo-inc.com found.
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1699)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
[17:00:15.288] {http--8080-6$1533061820} at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
[17:00:15.288] {http--8080-6$1533061820} at org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:281)
[17:00:15.288] {http--8080-6$1533061820} ... 15 more
[17:00:15.288] {http--8080-6$1533061820} Caused by: java.security.cert.CertificateException: No subject alternative DNS name matching auth.corp.flamingo-inc.com found.
[17:00:15.288] {http--8080-6$1533061820} at sun.security.util.HostnameChecker.matchDNS(HostnameChecker.java:193)
[17:00:15.288] {http--8080-6$1533061820} at sun.security.util.HostnameChecker.match(HostnameChecker.java:77)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:264)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:250)
[17:00:15.288] {http--8080-6$1533061820} at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)

开始怀疑是我写的AuthenticationFilter的锅,于是用了个新版本的cas-client-core,依然不行

开始升级jdk,由于是windows,直接安装的exe,然后改了启动脚本里面的JAVA_HOME,重启依然发现不行。。。 (((φ(◎ロ◎;)φ)))

于是暂时回退到了没有用CAS接入的版本,下班了。。。

今天回来不甘心啊,继续尝试解决

既然证书找不到xxx.com,那我换台nginx直接写host试试?

更新了一下测服nginx的证书,OA主机指定测服nginx ip

然后!!!! 可以了!!!! 😭

然后跟同事调试了一会,发现他昨天下午添加了一个域名abc.com,然后导致了default_server不是adc.com了。。。

解决方法

更新一下jdk就可以了(摊手),OA用的是jdk1.6,jdk1.6旧版本不支持SNI,至于什么是SNI等,看这里 https://github.com/ditunes/blog/issues/13

之前没成功是因为windows的服务里面写死了jdk的路径。。。

使用Openresty将pastebin内容格式化显示

前情提要

这里用的 https://github.com/solusipse/fiche 的pastebin服务端,功能不多但是贴一些代码或者日志足够了

痛点

fiche将贴的内容保存为文件,然后用nginx显示,如下:

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name mysite.com www.mysite.com;
charset utf-8;

location / {
root /home/www/code/;
index index.txt index.html;
}
}

这样显示出来的文本在行数多的时候就看瞎眼了 @[email protected]

解决痛点

我们可以用openresty将要显示的文本提前拼凑成html,html中用google的code prettify格式化处理。
对于用curl请求的我们不做处理,直接返回纯文本。

lua代码如下:

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
local headers = ngx.req.get_headers()
local reqUri = ngx.var.request_uri
local uri = ngx.var.uri
local userAgent = headers["user-agent"]
local codePath = "/data/fiche/code"

if uri == "/" then
ngx.say("Welcome to p.qiyuos.cn")
ngx.exit(200)
end

if uri == "/favicon.ico" then
ngx.exit(200)
end

local function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end

--读取文件到内存
local function readFile2Mem(file)
local fp = io.open(file,"r")
if fp then
return fp:read("*all")
end
end

local codeBeauty1 = [[
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script src="https://cdn.bootcss.com/prettify/r298/run_prettify.min.js?autoload=true&amp;lang=html"></script>
<style type="text/css">
body {
background: #fff;
}
pre.prettyprint {
background-color: #eee;
}
.prettyprint ol.linenums > li { list-style-type: decimal; }
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
</style>
</head>
<body>
<pre class="prettyprint linenums" id="quine">
]]
local codeBeauty2 = [[
</pre>
<script type="text/javascript">//<![CDATA[
(function () {
function htmlEscape(s) {
return s;
// .replace(/&/g, '&amp;')
// .replace(/</g, '&lt;')
// .replace(/>/g, '&gt;');
}
// this page's own source code
var quineHtml = htmlEscape(
document.getElementById("quine").innerHTML
);
// Highlight the operative parts:
quineHtml = quineHtml.replace(
/&lt;script src[\s\S]*?&gt;&lt;\/script&gt;|&lt;!--\?[\s\S]*?--&gt;|&lt;pre\b[\s\S]*?&lt;\/pre&gt;/g,
'<span class="operative">$&<\/span>');
// insert into PRE
document.getElementById("quine").innerHTML = quineHtml;
})();
//\]\]>
</script>
</body>
</html>
]]

if not file_exists(codePath .. uri .. "/index.txt") then
ngx.exit(404)
end

local paste = readFile2Mem(codePath .. uri .. "/index.txt")
if not userAgent or string.len(userAgent) < 20 or not string.match(userAgent, "Mozilla") then
ngx.say(paste)
ngx.exit(200)
else
ngx.say(codeBeauty1 .. paste .. codeBeauty2)
ngx.exit(200)
end

nginx 配置如下:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name mysite.com www.mysite.com;
charset utf-8;
location / {
default_type text/html;
content_by_lua_file "/usr/local/openresty/scripts/fiche.lua";
}
}

最后

代码还不完善,可能存在一些bug,欢迎提出

使用openresty搭一个类似ip.cn的接口

Openresty是什么

OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

官网 https://openresty.org/cn/

Openresty解决了nginx不能很好的添加一些逻辑判断的痛点,而且又不失性能。

接口代码

代码简单到不能再简单了,就三行

1
2
3
local ip = ngx.var.remote_addr
ngx.header["Content-Type"] = "text/plain charset=utf-8"
ngx.say(ip)
nginx.conf配置:
1
2
3
4
5
6
...
http
{
...
lua_package_path "/usr/local/openresty/script/waf/?.lua;/usr/local/openresty/lualib/?.lua";
...

vhost配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server
{
listen 80;
server_name ip.testing.cn;
index index.html index.htm index.php;
charset utf-8;
# 原生nginx的方式,貌似更简单...
# location / {
# default_type text/plain;
# return 200 $remote_addr;
# }
location /{
content_by_lua_block {
local ip = ngx.var.remote_addr
ngx.header["Content-Type"] = "text/plain charset=utf-8"
ngx.say(ip)
}
}

}

cas根据请求参数跳过验证

接上一篇 https://birdzhang.xyz/2018/05/15/Java%E6%94%B9%E5%86%99HttpServletRequest%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0/ ,这里说一下cas的坑(这里的cas是3.5版本)

虽然cas有CAS Authentication Filter,但是这个只是根据请求的uri过滤的,那么这里就出现了一个奇怪的bug

即使我在excludePaths里面添加了/test1/,但是当请求地址为/test1/123?ticket=123的时候,还是会去cas服务器验证!!!

我们来看代码:

CAS Authentication 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
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
package com.birdzhang.demo.check;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;

public class AuthenticationFilter extends AbstractCasFilter{


/**
* The URL to the CAS Server login.
*/
private String casServerLoginUrl;

/**
* Whether to send the renew request or not.
*/
private boolean renew = false;

/**
* Whether to send the gateway request or not.
*/
private boolean gateway = false;
/**
* 添加属性,这里用来存放不过滤地址正则表达式,可以根据自己需求定制---1
*/
private String excludePaths;

private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();

protected void initInternal(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
//log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
//log.trace("Loaded renew parameter: " + this.renew);
setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
//log.trace("Loaded gateway parameter: " + this.gateway);

final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);

if (gatewayStorageClass != null) {
try {
this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
} catch (final Exception e) {
//log.error(e,e);
throw new ServletException(e);
}
}
//自定义添加代码,用来读取web配置文件中excludes属性值 ---2
excludePaths = getPropertyFromInitParams(filterConfig, "excludePaths", null);//filterConfig.getInitParameter("excludePaths");
excludePaths = excludePaths.trim();
}
}

public void init() {
super.init();
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}
//url判断逻辑,这里大家可以根据自己需要来制订规则
private boolean isExclude(String uri){
boolean isInWhiteList = false;
if(excludePaths!=null&& uri!=null){
isInWhiteList = uri.matches(excludePaths);
}
return isInWhiteList;
}


public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession(false);
final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
// 该判断是自定义的对符合条件的url进行通过处理 ---3
if(isExclude(request.getRequestURI())){
filterChain.doFilter(request, response);
return;
}

if (assertion != null) {
filterChain.doFilter(request, response);
return;
}

final String serviceUrl = constructServiceUrl(request, response);
final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
filterChain.doFilter(request, response);
return;
}

final String modifiedServiceUrl;

//log.debug("no ticket and no assertion found");
if (this.gateway) {
//log.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}

/* if (log.isDebugEnabled()) {
log.debug("Constructed service url: " + modifiedServiceUrl);
}*/

final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

/* if (log.isDebugEnabled()) {
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}*/

response.sendRedirect(urlToRedirectTo);
}

public final void setRenew(final boolean renew) {
this.renew = renew;
}

public final void setGateway(final boolean gateway) {
this.gateway = gateway;
}

public final void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}

public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
this.gatewayStorage = gatewayStorage;
}
}

可以看到在uri匹配到excludepath之后会走filterChain.doFilter(request, response);,由于我们并没有对filterChain做什么修改,所以最终还是跑到cas那里去了

修改后的代码如下:

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
package com.birdzhang.plugin;

/**
* @author debo.zhang
*
*/
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
import org.jasig.cas.client.authentication.GatewayResolver;
import org.jasig.cas.client.util.AbstractCasFilter;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.validation.Assertion;

public class AuthenticationFilter extends AbstractCasFilter{


/**
* The URL to the CAS Server login.
*/
private String casServerLoginUrl;

/**
* Whether to send the renew request or not.
*/
private boolean renew = false;

/**
* Whether to send the gateway request or not.
*/
private boolean gateway = false;
/**
* 添加属性,这里用来存放不过滤地址正则表达式,可以根据自己需求定制---1
*/
private String excludePaths;

private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();

protected void initInternal(final FilterConfig filterConfig) throws ServletException {
if (!isIgnoreInitConfiguration()) {
super.initInternal(filterConfig);
setCasServerLoginUrl(getPropertyFromInitParams(filterConfig, "casServerLoginUrl", null));
//log.trace("Loaded CasServerLoginUrl parameter: " + this.casServerLoginUrl);
setRenew(parseBoolean(getPropertyFromInitParams(filterConfig, "renew", "false")));
//log.trace("Loaded renew parameter: " + this.renew);
setGateway(parseBoolean(getPropertyFromInitParams(filterConfig, "gateway", "false")));
//log.trace("Loaded gateway parameter: " + this.gateway);

final String gatewayStorageClass = getPropertyFromInitParams(filterConfig, "gatewayStorageClass", null);

if (gatewayStorageClass != null) {
try {
this.gatewayStorage = (GatewayResolver) Class.forName(gatewayStorageClass).newInstance();
} catch (final Exception e) {
//log.error(e,e);
throw new ServletException(e);
}
}
//自定义添加代码,用来读取web配置文件中excludes属性值 ---2
excludePaths = getPropertyFromInitParams(filterConfig, "excludePaths", null);//filterConfig.getInitParameter("excludePaths");
excludePaths = excludePaths.trim();
}
}

public void init() {
super.init();
CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
}

//url判断逻辑,这里大家可以根据自己需要来制订规则
private boolean isExclude(String uri){
boolean isInWhiteList = false;
if(excludePaths!=null&& uri!=null){
isInWhiteList = uri.matches(excludePaths);
}
return isInWhiteList;
}



public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpSession session = request.getSession(false);

final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;

//hack for 云桥
Map<String, String[]> paramsMap = request.getParameterMap();

if(null != paramsMap ) {
if(paramsMap.containsKey("ticket") && paramsMap.containsKey("operation")) {
/* String token = Arrays.toString(paramsMap.get("ticket"));
request.removeAttribute("ticket");
request.setAttribute("token", token.substring(1, token.length()-1));
filterChain.doFilter(request, response);
return;*/

/*StringBuffer paramsBuff = new StringBuffer();
for(String param: paramsMap.keySet()) {
String newString = Arrays.toString(paramsMap.get(param));
if(param.equals("ticket")) {
paramsBuff.append("&").append("eticket").append("=").append(newString.substring(1, newString.length()-1));
}else {
paramsBuff.append("&").append(param).append("=").append(newString.substring(1, newString.length()-1));
}
}
String port = request.getServerPort() == 80?"":":"+request.getServerPort();
String scheme = request.getScheme();
String host = request.getServerName();
String redirectUrl = String.format("%s://%s%s%s?%s",
scheme,
host,
port,
request.getRequestURI(),
paramsBuff.toString().substring(1)
);
System.out.println("request url:"+redirectUrl);
response.sendRedirect(redirectUrl);
return;*/

Map<String, String[]> extraParams = new HashMap<String, String[]>();
HttpServletRequest wrappedRequest = new PrettyFacesWrappedRequest(request, extraParams);
request.getRequestDispatcher(request.getRequestURI()).forward(wrappedRequest, response);
return;
}
}


// 该判断是自定义的对符合条件的url进行通过处理
if(isExclude(request.getRequestURI())){
filterChain.doFilter(request, response);
return;
}



if (assertion != null) {
filterChain.doFilter(request, response);
return;
}

final String serviceUrl = constructServiceUrl(request, response);
final String ticket = CommonUtils.safeGetParameter(request,getArtifactParameterName());
final boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);

if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
filterChain.doFilter(request, response);
return;
}

final String modifiedServiceUrl;

//log.debug("no ticket and no assertion found");
if (this.gateway) {
//log.debug("setting gateway attribute in session");
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
} else {
modifiedServiceUrl = serviceUrl;
}

/* if (log.isDebugEnabled()) {
log.debug("Constructed service url: " + modifiedServiceUrl);
}*/

final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);

/* if (log.isDebugEnabled()) {
log.debug("redirecting to \"" + urlToRedirectTo + "\"");
}*/

response.sendRedirect(urlToRedirectTo);
}

public final void setRenew(final boolean renew) {
this.renew = renew;
}

public final void setGateway(final boolean gateway) {
this.gateway = gateway;
}

public final void setCasServerLoginUrl(final String casServerLoginUrl) {
this.casServerLoginUrl = casServerLoginUrl;
}

public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
this.gatewayStorage = gatewayStorage;
}
}

Java改写HttpServletRequest请求参数

为什么会有这个需求呢? 因为被 casAuthenticationFilter坑了

下一篇再说一下AuthenticationFilter的坑,这里主要说修改请求的参数

代码是从网上找的,找不到具体来源了,直接贴代码吧

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
package com.birdzhang.plugin;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;


/**
* @author birdzhang
*
*/
public class PrettyFacesWrappedRequest extends HttpServletRequestWrapper
{
private final Map<String, String[]> modifiableParameters;
private Map<String, String[]> allParameters = null;

/**
* Create a new request wrapper that will merge additional parameters into
* the request object without prematurely reading parameters from the
* original request.
*
* @param request
* @param additionalParams
*/
public PrettyFacesWrappedRequest(final HttpServletRequest request,
final Map<String, String[]> additionalParams)
{
super(request);
modifiableParameters = new HashMap<String, String[]>();
modifiableParameters.putAll(additionalParams);
}

@Override
public String getParameter(final String name)
{
String[] strings = getParameterMap().get(name);
if (strings != null)
{
return strings[0];
}
return super.getParameter(name);
}

@Override
public Map<String, String[]> getParameterMap()
{
if (allParameters == null)
{
allParameters = new TreeMap<String, String[]>();
Map<String, String[]> superMap = super.getParameterMap();
// 在这里根据你的需要修改
for (String key : superMap.keySet()) {
if(key.equals("ticket")) {
allParameters.put("token", superMap.get(key));
}else {
allParameters.put(key, superMap.get(key));
}
}
allParameters.putAll(modifiableParameters);
}
//Return an unmodifiable collection because we need to uphold the interface contract.
return Collections.unmodifiableMap(allParameters);
}

@Override
public Enumeration<String> getParameterNames()
{
return Collections.enumeration(getParameterMap().keySet());
}

@Override
public String[] getParameterValues(final String name)
{
return getParameterMap().get(name);
}
}

使用:

1
2
3
4
Map<String, String[]> extraParams = new HashMap<String, String[]>();
HttpServletRequest wrappedRequest = new PrettyFacesWrappedRequest(request, extraParams);
request.getRequestDispatcher(request.getRequestURI()).forward(wrappedRequest, response);
return;

完美的将ticket参数替换为token参数

SailfishOS移植到Redmi 5 Plus的一些记录

移植旗鱼系统到红米5p的过程记录

!!!可以去ホロ 🐺的博客看看更详细的!!!

https://blog.yoitsu.moe/category/sailfish.html

准备工作

搭建环境

Android编译环境(HADK 第4章)

需要先配置一下hadk的环境变量,一共三个文件,可以参考这里: https://github.com/CancroSailors/sailfish-build-environment

然后source ~/.hadk.env一下

1. 安装git等

sudo apt-get install git

2. 同步CM的代码

配置git的用户名跟邮箱,填自己的就行,随便填也行
1
2
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
创建检出代码的目录,所在的盘空间要足够
1
2
3
4
sudo mkdir -p $ANDROID_ROOT
sudo chown -R $USER $ANDROID_ROOT
cd $ANDROID_ROOT
repo init -u git://github.com/mer-hybris/android.git -b hybris-14.1
配置你的设备的仓库,这里是vince
1
2
3
4
5
6
7
8
9
10
11
12
mkdir $ANDROID_ROOT/.repo/local_manifests
cat <<'EOF' >> $ANDROID_ROOT/.repo/local_manifests/vince.xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<project path="device/xiaomi/vince" name="Sailfish-On-Vince/device_xiaomi_vince" revision="cm-14.1" />
<project path="vendor/xiaomi/vince" name="Sailfish-On-Vince/vendor_xiaomi_vince" revision="cm-14.1" />
<project path="kernel/xiaomi/vince" name="Sailfish-On-Vince/kernel_xiaomi_msm8953" revision="cm-14.1" />
<project path="hybris/droid-hal-version-vince" name="Sailfish-On-Vince/droid-hal-version-vince" revision="master" />
<project path="hybris/droid-config-vince" name="Sailfish-On-Vince/droid-config-vince" revision="master" />
<project path="rpm" name="Sailfish-On-Vince/droid-hal-vince" revision="master" />
</manifest>
EOF
同步代码

repo sync --fetch-submodules

如果你要求快,可以用 repo sync --fetch-submodules -c --no-tags --no-clone-bundle,这样只会拉取当前要用的代码,但是不方便后面的修改提交

出错就多同步几次

Mer编译环境

参见官方教程: https://sailfishos.org/wiki/Platform_SDK_Installation

安装Platform SDK

1
2
3
4
5
6
7
8
9
export PLATFORM_SDK_ROOT=/srv/mer
curl -k -O http://releases.sailfishos.org/sdk/installers/latest/Jolla-latest-SailfishOS_Platform_SDK_Chroot-i486.tar.bz2 ;
sudo mkdir -p $PLATFORM_SDK_ROOT/sdks/sfossdk ;
sudo tar --numeric-owner -p -xjf Jolla-latest-SailfishOS_Platform_SDK_Chroot-i486.tar.bz2 -C $PLATFORM_SDK_ROOT/sdks/sfossdk ;
echo "export PLATFORM_SDK_ROOT=$PLATFORM_SDK_ROOT" >> ~/.bashrc
echo 'alias sfossdk=$PLATFORM_SDK_ROOT/sdks/sfossdk/mer-sdk-chroot' >> ~/.bashrc ; exec bash ;
echo 'PS1="PlatformSDK $PS1"' > ~/.mersdk.profile ;
echo '[ -d /etc/bash_completion.d ] && for i in /etc/bash_completion.d/*;do . $i;done' >> ~/.mersdk.profile ;
sfossdk

另开一个终端,输入sfossdk,进入mer下

安装targets,官方教程:https://sailfishos.org/wiki/Platform_SDK_Target_Installation

其实就是执行下面的命令,要下载这两个包,过程有些慢

(2019-06-13更新,修复链接地址为最新的)

1
2
sdk-assistant create xiaomi-vince-latest http://releases.sailfishos.org/sdk/targets/Sailfish_OS-latest-Sailfish_SDK_Tooling-i486.tar.7z
sdk-assistant create xiaomi-vince-armv7hl http://releases.sailfishos.org/sdk/targets/Sailfish_OS-latest-Sailfish_SDK_Target-armv7hl.tar.7z

更新到最新(Update to latest)

1
2
3
sudo ssu re x.y.z.ab
sudo zypper ref
sudo zypper dup

安装打包的工具

1
sudo zypper in android-tools createrepo_c

修改fixup-mountpoints

文件在hybris/hybris-boot/fixup-mountpoints,添加你的设备的,这里是vince。
adb到手机上,输入ls -l /dev/block/platform/*/by-name/, 获取分区信息,或者其他路径的,HADK里面有写

Camera支持

1
2
3
4
5
6
7
8
cd $ANDROID_ROOT/external/droidmedia
echo 'DROIDMEDIA_32 := true' >> env.mk
echo 'FORCE_HAL:=1' >> env.mk

# For video recorder
# https://github.com/sailfishos/droidmedia/pull/11
echo 'MINIMEDIA_AUDIOPOLICYSERVICE_ENABLE:=1' >> env.mk
echo 'AUDIOPOLICYSERVICE_ENABLE:=1' >> env.mk

编译hybris-hal

1
2
3
4
5
cd $ANDROID_ROOT
source build/envsetup.sh
export USE_CCACHE=1
breakfast $DEVICE
make -j8 hybris-hal

期间可能会报错,谷歌搜一下

验证Kernel

1
2
cd $ANDROID_ROOT
hybris/mer-kernel-check/mer_verify_kernel_config ./out/target/product/$DEVICE/obj/KERNEL_OBJ/.config

出现WARNING或者ERROR,将提示的加入到你的defconfig中,我的在kernel/xiaomi/vince/arch/arm64/configs/vince_defconfig
然后执行make hybris-root后重新验证。没有出现ERROR后可以执行make hybris-recovery

打包dhd(HADK 第7章)

再开一个终端(我们这里称终端2),输入sfossdk,进入mer打包环境下

1
2
cd $ANDROID_ROOT
rpm/dhd/helpers/build_packages.sh -d

打包droidmedia与audioflingerglue

> 如果你的机器是32位的话那么下面的命令去掉_32,下面的也一样

在终端1中

1
make -j$(nproc --all) $(external/droidmedia/detect_build_targets.sh $PORT_ARCH $(gettargetarch))

在终端2中

1
2
3
4
5
6
7
8
9
10
DROIDMEDIA_VERSION=$(git --git-dir external/droidmedia/.git describe --tags | sed \
-r "s/\-/\+/g")
rpm/dhd/helpers/pack_source_droidmedia-localbuild.sh $DROIDMEDIA_VERSION
mkdir -p hybris/mw/droidmedia-localbuild/rpm
cp rpm/dhd/helpers/droidmedia-localbuild.spec \
hybris/mw/droidmedia-localbuild/rpm/droidmedia.spec
sed -ie "s/0.0.0/$DROIDMEDIA_VERSION/" \
hybris/mw/droidmedia-localbuild/rpm/droidmedia.spec
mv hybris/mw/droidmedia-$DROIDMEDIA_VERSION.tgz hybris/mw/droidmedia-localbuild
rpm/dhd/helpers/build_packages.sh --build=hybris/mw/droidmedia-localbuild

在终端1中

1
make -j$(nproc --all) $(external/audioflingerglue/detect_build_targets.sh $PORT_ARCH $(gettargetarch))

在终端2中

1
2
3
4
5
6
7
8
9
10
AUDIOFLINGERGLUE_VERSION=$(git --git-dir external/audioflingerglue/.git describe --tags | sed \
-r "s/\-/\+/g")
rpm/dhd/helpers/pack_source_audioflingerglue-localbuild.sh $AUDIOFLINGERGLUE_VERSION
mkdir -p hybris/mw/audioflingerglue-localbuild/rpm
cp rpm/dhd/helpers/audioflingerglue-localbuild.spec \
hybris/mw/audioflingerglue-localbuild/rpm/audioflingerglue.spec
sed -ie "s/0.0.0/$AUDIOFLINGERGLUE_VERSION/" \
hybris/mw/audioflingerglue-localbuild/rpm/audioflingerglue.spec
mv hybris/mw/audioflingerglue-$AUDIOFLINGERGLUE_VERSION.tgz hybris/mw/audioflingerglue-localbuild
rpm/dhd/helpers/build_packages.sh --build=hybris/mw/audioflingerglue-localbuild

然后重新打包dhd

1
rpm/dhd/helpers/build_packages.sh --droid-hal

打包其他中间件包(mw)

注意到 https://wiki.merproject.org/wiki/Adaptations/faq-hadk 搜索当前版本需要对应的mw版本号,不一定用最新的

执行 rpm/dhd/helpers/build_packages.sh --mw ,然后选择 all 即可

【进阶】上传到obs打包

将droid-local-repo/vince下 droid-hal-vince/.rpm 跟audioflingerglue.rpm 、 droidmedia*.rpm 上传到obs的droid-hal-vince下

例如这些包:https://build.merproject.org/package/show/nemo:devel:hw:xiaomi:vince/droid-hal-vince

obs打包还需要dhc,dhv等等几个包,此处不详细说明了,可以到 https://github.com/mer-hybris 看其他机型的

打包dhv

也就是 droid-hal-$DEVICE-version

rpm/dhd/helpers/build_packages.sh -v

如果提示 droid-configs 之类的找不到,需要手动安装

1
sb2 -t $VENDOR-$DEVICE-$PORT_ARCH -R -msdk-install zypper -n install droid-config-$DEVICE

[email protected]@[email protected]@.ks

~obs打包完之后,将droid-config-vince-kickstart-configuration-0.2.4*.armv7hl.rpm 下载下来,解压获得[email protected]@[email protected]@.ks放到$ANDROID_ROOT下面~

1
2
3
4
5
6
7
8
cd $ANDROID_ROOT
HA_REPO="repo --name=[email protected]@"
HA_DEV="repo --name=[email protected]@"
KS="[email protected]@[email protected]@.ks"
sed \
"/$HA_REPO/i$HA_DEV --baseurl=file:\/\/$ANDROID_ROOT\/droid-local-repo\/$DEVICE" \
$ANDROID_ROOT/hybris/droid-configs/installroot/usr/share/kickstarts/$KS \
> $KS

镜像制作

1
2
3
4
5
6
7
8
9
10
cd $ANDROID_ROOT
RELEASE=2.1.4.14
EXTRA_NAME=-alpha1
hybris/droid-configs/droid-configs-device/helpers/process_patterns.sh
sudo mic create fs --arch=$PORT_ARCH \
--tokenmap=ARCH:$PORT_ARCH,RELEASE:$RELEASE,EXTRA_NAME:$EXTRA_NAME \
--record-pkgs=name,url \
--outdir=sfe-$DEVICE-$RELEASE$EXTRA_NAME \
--pack-to=sfe-$DEVICE-$RELEASE$EXTRA_NAME.tar.bz2 \
$ANDROID_ROOT/[email protected]@[email protected]@.ks

在当前目录下会有一个 sfe-$DEVICE-$RELEASE$EXTRA_NAME目录,下面的 sailfishos-$DEVICE-$RELEASE$EXTRA_NAME.zip就是要使用的刷机包

刷机

刷入 lineageos-14.1的包,刷入上面的sailfishos-$DEVICE-$RELEASE$EXTRA_NAME.zip的包,重启

可以参考 https://wiki.merproject.org/wiki/Adaptations/libhybris/Install_SailfishOS_for_Vince

调试

欢迎加入 IRC #jolla-cn #sailfishos-porters (只限英文交流) 或 https://t.me/joinchat/GTqoL1HLIYXWNf-JeijTAg 讨论

相关文档