蒙奇在路上

OAuth2简述

什么是OAuth2

OAuth2是一个服务开放(授权)标准,它表示允许用户授权第三方应用访问该用户在另外一个服务器存储的用户信息,而不用将存储在该服务器的用户名以及密码或所有用户信息告知给第三方应用。

OAuth2的应用场景

当你想使用QQ登录的第三方应用时,此时第三方应用需要获取你的QQ信息(昵称、头像、openid等),但是又不能直接用户名和密码等信息。那么此时就需要你登录你的QQ服务器,然后授权给该第三方应用一部分信息。而OAuth2就是为了实现上述目标而制定的一种规范。

Oauth2的授权模式

  • 授权码(authorization code)
  • 隐藏式(implicit)
  • 密码式(resource owner password credentials)
  • 客户端凭证(client credentials)

授权码

授权码模式,指的是第三方应用先申请一个授权码,然后再拿着该授权码获取令牌。

img

  1. 用户访问第三方应用客户端,随后该客户端跳转到认证服务器,跳转的过程中附带上重定向URI。
  2. 用户在认证服务器给予授权之后,跳转到事先定义好的redirect_uri,并返回上一个授权码,该授权码是与当前客户端绑定的,无法被其他客户端使用。
  3. 客户端收到该授权码,携带上步骤1中的redirect_uri,向认证服务器申请令牌。请求成功后认证服务器会返回一个令牌,该令牌存在一个有效时间。
  4. 客户端拿到该令牌之后,将该令牌携带在请求中请求资源服务器,资源服务器判断该令牌的有效性,最终判断是否返回有效的资源信息。

隐藏式

当有些网站时纯前端架构的时候,就无法通过后端来使用上述的授权模式了,令牌必须存在前端。该模式下不通过第三方应用的服务器,直接在浏览器里想认证服务器申请令牌,跳过了授权码的步骤。所有步骤都是在浏览器内完成,令牌对访问者是可见的,并且客户端不需要认证。

img

  1. 客户端在页面内放置一个链接,该链接指向认证服务器
  2. 用户点击授权,给该客户端授权
  3. 认证服务器在授权之后将跳转到客户端指定的redirect_uri,并将令牌包含在URI中,其中令牌并不是以查询参数的形式存储在URI中,而是以锚点的形式存储。
  4. 浏览器想服务器发起请求,资源服务器返回一个网页,网页内包含一个可以获取上述令牌的脚本
  5. 浏览器执行脚本,提取令牌,发送给客户端
  6. 客户端拿到令牌向资源服务器请求,资源服务器判断该令牌的有效性,最终判断是否返回有效的资源信息。

密码式

听名字我们就可以知道,该授权模式需要密码来进行。其实质是,如果你高度信任某个应用,那么可以直接告诉这个应用你的用户名和密码,然后该应用拿到这些信息去申请令牌。

img

  1. 用户向第三方应用提供用户名和密码
  2. 该应用将用户名和密码发送给认证服务器,然后请求令牌
  3. 认证服务器确认无误后,想客户端提供令牌
  4. 客户端拿到令牌向资源服务器请求,资源服务器判断该令牌的有效性,最终判断是否返回有效的资源信息。

客户端凭证

客户端凭证模式下,即是通过客户端的名义,而不是用户的名义去获取令牌。该模式下可以在纯后端进行,常见的参数有client_id、client_secret。

img

  1. 客户端向认证服务器发送一个携带client_id和client_secret的请求
  2. 认证服务器接收到客户端请求,并验证client_id和client_secret的有效性,如果有效这向客户端提供令牌。

What is JWT?

What is JWT?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA. Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

Application scenarios

Single Sign On

This is the most common scenario to use JWT. In a multi-system service architecture, after a user logged in to one system at first time, there is no need to log in repeatedly when using other systems.

Authorization

After a user logged in, every subsequent request contains JWT. When the JWT is verified, the user is allowed to access the resource he should access.

Components of JWT

JWT consists of three parts: Header, Payload, Signature, and they are connected with a dot.

  • The Header consists of two parts: the type of token and the signing algorithm being used, e.g. :
{
    'alg': "HS256",
    'typ': "JWT"
}
  • The Payload is actually a place to store valid information which has three types of claims.

Registered claims: Predefined claims which are not mandatory but recommended. Such as iss(issuer) and exp(expiration time)

Public claims: They can be defined at will.

Private claims: These are the custom claims created to share information between parties that agree on using them and are neither registered or public claims.

  • The Signature is a digital signature to prevent data from being tampered.

How is JWT generated

JWT is divided into three parts. How do these three parts come together to form a completed JWT?

  • The Header is encoded by base64 to get the first part.
  • The Payload is encoded by base64 to get the second part.
  • Through the algorithm in Header with a secret sign that encoded Header and encoded Payload and Connected with dot.

Tips: Jwt is not encrypted, so it's better not to store some sensitive information directly in JWT.

Workflow of JWT

After successful login for the first time, the user will get a JWT. This JWT is the certificate for the user to interact with the server, that is, the server uses this token to determine which user the requester is. When the user wants to access the resources on the server, the token was sent, which is usually placed in authorization, using bearer schema. For example:

Authorization: Bearer token

What are the advantages over traditional session authentication?

Session authentication

Because HTTP is a stateless protocol, we provide our user name and password for authentication when we first access it. Then, in the second visit, we still need to carry out a user authentication. Because according to the HTTP protocol, we don't know who the originator of the request is, in order to identify which user initiated the request, we can only save a copy of the user's information on the server, and then pass the same information to the user, so that the user can save it in cookies. In the next request, you only need to carry the information in the cookies. According to the submitted cookies, the server can determine which user it is. This is the traditional authentication based on session.

Problems in session based authentication

Session: after each user has been authenticated by our application, our application needs to make a record on the server to facilitate the authentication of the next request. Generally speaking, the session is saved in memory. With the increase of authenticated users, the cost of the server will increase significantly.

Scalability: after user authentication, the server makes authentication records. If the authentication records are saved in memory, it means that the next request of the user must be on this server, so that the authorized resources can be obtained. This also means that the expansion ability of the application is limited.

CSRF: because user identification is based on cookie, if cookie is intercepted, users will be vulnerable to cross site request forgery attack.

What are the advantages of JWT based authentication

  • Tokens are stored in the client, completely stateless and scalable.
  • Security: token is different from a cookie. Each request will send a token. Since no cookie is sent, it can also prevent CSRF attacks.

reference from https://jwt.io/introduction

MySQL创建用户和配置远程连接

创建远程连接用户

在某些情况下,可能别人需要连接你的数据库进行操作,出于安全考虑,我们需要新建一个用户,让该用户只具有一部分的操作权限操作数据库。

当然如果只有你一个人使用这个数据库的话,也可以跳过这一步,直接选择root用户来操作。

创建用户

使用help命令查看如何创建用户

help create user
Name: 'CREATE USER'
Description:
Syntax:
CREATE USER user_specification
    [, user_specification] ...

user_specification:
    user
    [
        IDENTIFIED BY [PASSWORD] 'password'
      | IDENTIFIED WITH auth_plugin [AS 'auth_string']
    ]

根据帮助说明,接下来我们来创建用户

create user 'zjc'@'localhost' # 创建一个用户zjc,不需要密码即可登录,但只可以在本机登录
create user 'zjc'@'%'	# 创建一个用户zjc,不需要密码即可登录,可以在任意主机登录
create user 'zjc'@'%' identified by '123456'	# 创建一个用户zjc,登录时需要密码123456才可以登录,会自动将密码加密
create user 'zjc'@'%' identified by password '123456' # 创建一个用户zjc,登录时需要密码123456才可以登录,会将密码明文存储

修改密码

命令:

set password for 'username'@'host' = PASSWORD('newpassword');

查看帮助命令查看详情

> help set password
Name: 'SET PASSWORD'
Description:
Syntax:
SET PASSWORD [FOR user] =
    {
        PASSWORD('cleartext password')
      | OLD_PASSWORD('cleartext password')
      | 'encrypted password'
    }


修改密码:

set password for 'zjc'@'%' = PASSWORD("654321")

删除用户

命令:

drop user username

用户授权

查看用户目前的权限

> show grants for zjc;
+---------------------------------+
| Grants for zjc@%                |
+---------------------------------+
| GRANT USAGE ON *.* TO `zjc`@`%` |
+---------------------------------+

USAGE相当于一个占位符,表示目前zjc用户什么权限都没有,需要我们给他授予权限

授权该用户权限

命令:

grant privileges ON databasename.tablename to 'username'@'host'

同样使用help命令详情

> help grant
Name: 'GRANT'
Description:
Syntax:
GRANT
    priv_type [(column_list)]
      [, priv_type [(column_list)]] ...
    ON [object_type] priv_level
    TO user_specification [, user_specification] ...
    [REQUIRE {NONE | ssl_option [[AND] ssl_option] ...}]
    [WITH with_option ...]

GRANT PROXY ON user_specification
    TO user_specification [, user_specification] ...
    [WITH GRANT OPTION]

object_type:
    TABLE
  | FUNCTION
  | PROCEDURE

priv_level:
    *
  | *.*
  | db_name.*
  | db_name.tbl_name
  | tbl_name
  | db_name.routine_name

user_specification:
    user
    [
        IDENTIFIED BY [PASSWORD] 'password'
      | IDENTIFIED WITH auth_plugin [AS 'auth_string']
    ]

ssl_option:
    SSL
  | X509
  | CIPHER 'cipher'
  | ISSUER 'issuer'
  | SUBJECT 'subject'

with_option:
    GRANT OPTION
  | MAX_QUERIES_PER_HOUR count
  | MAX_UPDATES_PER_HOUR count
  | MAX_CONNECTIONS_PER_HOUR count
  | MAX_USER_CONNECTIONS count

  • privileges:用户的操作权限,如SELECT,INSERT,UPDATE等,如果要授予所的权限则使用ALL
  • databasename:数据库名
  • tablename:表名,如果要授予该用户对所有数据库和表的相应操作权限则可用表示,如.*

给用户zjc授予权限

grant all on *.* to 'zjc'@'%';	

刷新权限

flush privileges;

取消该用户的权限

命令:

revoke privilege ON databasename.tablename FROM 'username'@'host';

help查看详情

> help revoke
Name: 'REVOKE'
Description:
Syntax:
REVOKE
    priv_type [(column_list)]
      [, priv_type [(column_list)]] ...
    ON [object_type] priv_level
    FROM user [, user] ...

REVOKE ALL PRIVILEGES, GRANT OPTION
    FROM user [, user] ...

REVOKE PROXY ON user
    FROM user [, user] ...

说明:同上面的授权部分

配置远程链接

找到配置文件中的[mysqld]一项

# this is only for the mysqld standalone daemon
[mysqld]

#
# * Basic Settings
#
user                    = mysql
pid-file                = /run/mysqld/mysqld.pid
socket                  = /run/mysqld/mysqld.sock
#port                   = 3306
basedir                 = /usr
datadir                 = /var/lib/mysql
tmpdir                  = /tmp
lc-messages-dir         = /usr/share/mysql
#skip-external-locking
skip-name-resolve

# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
# bind-address            = 127.0.0.1

bind-address 注释掉,,然后重启数据库即可

systemctl restart mysql

问题记录

我在尝试用 navicat连接的时候,出现(2013,XXXXXXXX)的错误,这样是由于mysql在接收到客户端链接的时候需要对dns进行反向解析,由于我实在局域网内,所有反向解析是不可能成功的。

解决办法有两种:

  1. 把client的ip写在mysql服务器的/etc/hosts文件里,随便给个名字就可以了。
  2. 在 my.cnf 中加入 skip-name-resolve

于第一种方法比较笨,也不实用,那么 skip-name-resolve 选项可以禁用dns解析,但是,这样不能在mysql的授权表中使用主机名了,只能使用IP。

什么是mysql的dns反解析

mysql接收到连接请求后,获得的是客户端的ip,为了更好的匹配mysql.user里的权限记录(某些是用hostname定义的)。

如果mysql服务器设置了dns服务器,并且客户端ip在dns上并没有相应的hostname,那么这个过程很慢,导致连接等待。

官方解释

How MySQL uses DNS When a new thread connects to mysqld, mysqld will spawn a new thread to handle the request. This thread will first check if the hostname is in the hostname cache. If not the thread will call gethostbyaddr_r() and gethostbyname_r() to resolve the hostname. If the operating system doesn’t support the above thread-safe calls, the thread will lock a mutex and call gethostbyaddr() and gethostbyname() instead. Note that in this case no other thread can resolve other hostnames that is not in the hostname cache until the first thread is ready. You can disable DNS host lookup by starting mysqld with –skip-name-resolve. In this case you can however only use IP names in the MySQL privilege tables. If you have a very slow DNS and many hosts, you can get more performance by either disabling DNS lookop with –skip-name-resolve or by increasing the HOST_CACHE_SIZE define (default: 128) and recompile mysqld. You can disable the hostname cache with –skip-host-cache. You can clear the hostname cache with FLUSH HOSTS or mysqladmin flush-hosts. If you don’t want to allow connections over TCP/IP, you can do this by starting mysqld with –skip-networking.

根据文档说明,如果你的mysql主机查询DNS很慢或是有很多客户端主机时会导致连接很慢,由于我们的开发机器是不能够连接外网的,所以DNS解析是不可能完成的,从而也就明白了为什么连接那么慢了。同时,请注意在增加该配置参数后,mysql的授权表中的host字段就不能够使用域名而只能够使用 ip地址了,因为这是禁止了域名解析的结果。

Linux中的软链接和硬链接

Linux文件系统

在 UNIX 系统中,操作系统为磁盘上的文本与图像、鼠标与键盘等输入设备及网络交互等 I/O 操作设计了一组通用 API,使他们被处理时均可统一使用字节流方式。

换言之,UNIX 系统中除进程之外的一切皆是文件,而 Linux 保持了这一特性。为了便于文件的管理,Linux 还引入了目录(有时亦被称为文件夹)这一概念。目录使文件可被分类管理,且目录的引入使 Linux 的文件系统形成一个层级结构的目录树。

Linux 系统的顶层目录结构

/              根目录
├── bin     存放用户二进制文件
├── boot    存放内核引导配置文件
├── dev     存放设备文件
├── etc     存放系统配置文件
├── home    用户主目录
├── lib     动态共享库
├── lost+found  文件系统恢复时的恢复文件
├── media   可卸载存储介质挂载点
├── mnt     文件系统临时挂载点
├── opt     附加的应用程序包
├── proc    系统内存的映射目录,提供内核与进程信息
├── root    root 用户主目录
├── sbin    存放系统二进制文件
├── srv     存放服务相关数据
├── sys     sys 虚拟文件系统挂载点
├── tmp     存放临时文件
├── usr     存放用户应用程序
└── var     存放邮件、系统日志等变化文件

Linux 与其他类 UNIX 系统一样并不区分文件与目录:目录是记录了其他文件名的文件。使用命令 mkdir 创建目录时,若期望创建的目录的名称与现有的文件名(或目录名)重复,则会创建失败。

硬链接与软链接的联系与区别

我们知道文件都有文件名与数据,这在 Linux 上被分成两个部分:用户数据 (user data) 与元数据 (metadata)。用户数据,即文件数据块 (data block),数据块是记录文件真实内容的地方;而元数据则是文件的附加属性,如文件大小、创建时间、所有者等信息。在 Linux 中,元数据中的 inode 号(inode 是文件元数据的一部分但其并不包含文件名,inode 号即索引节点号)才是文件的唯一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序通过 inode 号寻找正确的文件数据块。

在 Linux 系统中查看 inode 号可使用命令 stat 或 ls -i(若是 AIX 系统,则使用命令 istat)。

 # stat /home/harris/source/glibc-2.16.0.tar.xz
 File: `/home/harris/source/glibc-2.16.0.tar.xz'
 Size: 9990512      Blocks: 19520      IO Block: 4096   regular file
Device: 807h/2055d      Inode: 2485677     Links: 1
Access: (0600/-rw-------)  Uid: ( 1000/  harris)   Gid: ( 1000/  harris)
...
...
# ls -i -F /home/harris/Desktop/glibc.tar.xz
2485677 /home/harris/Desktop/glibc.tar.xz

为解决文件的共享使用,Linux 系统引入了两种链接:硬链接 (hard link) 与软链接(又称符号链接,即 soft link 或 symbolic link)。链接为 Linux 系统解决了文件的共享使用,还带来了隐藏文件路径、增加权限安全及节省存储等好处。若一个 inode 号对应多个文件名,则称这些文件为硬链接。换言之,硬链接就是同一个文件使用了多个别名(见 图 2.hard link 就是 file 的一个别名,他们有共同的 inode)。硬链接可由命令 link 或 ln 创建。

创建硬链接

link oldfile newfile
ln oldfile newfile

查找有相同 inode 号的文件

df -i --print-type

由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性

  • 文件有相同的 inode 及 data block;
  • 只能对已存在的文件进行创建;
  • 不能交叉文件系统进行硬链接的创建;
  • 不能对目录进行创建,只可对文件创建;
  • 删除一个硬链接文件并不影响其他有相同 inode 号的文件。

硬链接不能对目录创建是受限于文件系统的设计。现 Linux 文件系统中的目录均隐藏了两个个特殊的目录:当前目录(.)与父目录(..)。查看这两个特殊目录的 inode 号可知其实这两目录就是两个硬链接。若系统允许对目录创建硬链接,则会产生目录环。

软链接与硬链接不同,若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接。软链接就是一个普通文件,只是数据块内容有点特殊。软链接有着自己的 inode 号以及用户数据块。因此软链接的创建与使用没有类似硬链接的诸多限制:

  • 软链接有自己的文件属性及权限等;
  • 可对不存在的文件或目录创建软链接;
  • 软链接可交叉文件系统;
  • 软链接可对文件或目录创建;
  • 创建软链接时,链接计数 i_nlink 不会增加;
  • 删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。

创建软连接

ln -s 源文件路径 目标路径

我们尝试对不存在的文件创建软连接

# ls -li
 total 0

 // 可对不存在的文件创建软链接
 # ln -s old.file soft.link
 # ls -liF
 total 0
 789467 lrwxrwxrwx 1 root root 8 Sep  1 18:00 soft.link -> old.file 

 // 由于被指向的文件不存在,此时的软链接 soft.link 就是死链接
 # cat soft.link 
 cat: soft.link: No such file or directory 

 // 创建被指向的文件 old.file,soft.link 恢复成正常的软链接
 # echo "This is an original file_A" >> old.file
 # cat soft.link
 This is an original file_A

 // 对不存在的目录创建软链接
 # ln -s old.dir soft.link.dir
 # mkdir -p old.dir/test
 # tree . -F --inodes
 .
├── [ 789497]  old.dir/
│   └── [ 789498]  test/
├── [ 789495]  old.file
├── [ 789495]  soft.link -> old.file
└── [ 789497]  soft.link.dir -> old.dir/

当然软链接的用户数据也可以是另一个软链接的路径,其解析过程是递归的。但需注意:软链接创建时原文件的路径指向使用绝对路径较好。使用相对路径创建的软链接被移动后该软链接文件将成为一个死链接,因为链接数据块中记录的亦是相对路径指向。