# Derby 安全

> 原文： [http://zetcode.com/db/apachederbytutorial/sec/](http://zetcode.com/db/apachederbytutorial/sec/)

在下一章中，我们将提到 Derby 的安全性选项。

我们将在本章中简要提及两个基本安全概念。 用户认证和用户授权。 用户认证正在验证用户凭据，然后再授予对 Derby 系统的访问权限。 用户授权是授予读取和/或写入 Derby 数据库权限的手段。

此外，Derby 允许对磁盘上存储的数据库文件进行加密。 Derby 网络流量可以使用 SSL/TLS 加密协议进行加密。

## Derby 默认

默认情况下，Derby 不需要用户认证。 用户名成为程序中的默认架构，并且用户密码将被忽略。 要启用认证，我们必须修改 Derby 属性。 用户授权已关闭。 另外，Derby 没有数据库超级用户。

## 数据库所有者

数据库所有者是创建数据库的用户。 如果创建数据库时没有提供用户，则数据库所有者将设置为默认授权标识符 APP。 当我们启用 SQL 授权时，控制数据库所有者很重要。

## 数据库加密

Derby 为我们提供了一种加密磁盘上数据的方法。 引导数据库的用户必须提供启动密码。 创建数据库时可以对其进行加密。 也可以加密现有的未加密数据库。 在加密数据库时，还必须指定启动密码，该密码是用于生成加密键的字母数字字符串。

```java
ij> CONNECT 'jdbc:derby:testdb;create=true;dataEncryption=true;
bootPassword=3344kkllqq**';

```

创建数据库时，我们可以对其进行加密。 我们将`dataEncryption`属性设置为`true`，并提供启动密码。 现在，每次启动数据库时，我们都必须提供启动密码。

```java
ij> CONNECT 'jdbc:derby:testdb';
ERROR XJ040: Failed to start database 'testdb' with class loader 
sun.misc.Launcher$AppClassLoader@360be0, see the next exception for details.
ERROR XBM06: Startup failed. An encrypted database cannot be accessed without 
the correct boot password.

```

在嵌入式模式下，当我们连接到数据库时，我们还将引导它。 当我们尝试在没有启动密码的情况下连接到加密数据库时，Derby 将显示以上错误消息。

```java
ij> CONNECT 'jdbc:derby:testdb;bootPassword=3344kkllqq**';
ij> SHOW CONNECTIONS;
CONNECTION0* -  jdbc:derby:testdb
* = current connection

```

使用正确的启动密码，我们已成功连接到`testdb`数据库。

## 认证方式

认证限制了对正确用户的访问。 默认情况下，Derby 中的认证处于关闭状态。

Derby 通过三种方式提供认证。

*   LDAP 外部认证
*   自定义 Java 类
*   内置系统

Derby 官方文档警告说 Derby 的内置认证机制仅适用于开发和测试目的。 强烈建议生产系统依赖 LDAP 或用户定义的类进行认证。

### 嵌入

认证可以设置为两个级别。 在系统级别或数据库级别。

```java
ij> CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.user.user12', '34klq*');
0 rows inserted/updated/deleted
ij> CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.connection.requireAuthentication', 
'true');
0 rows inserted/updated/deleted

```

以上两个语句可在数据库级别为当前连接的数据库启用用户认证。 我们使用密码创建了一个用户，并启用了`derby.connection.requireAuthentication`属性。

```java
ij> CONNECT 'jdbc:derby:testdb';
ERROR 08004: Connection authentication failure occurred.  Reason: Invalid authentication..
ij> CONNECT 'jdbc:derby:testdb;user=user12;password=34klq*';
ij> SHOW CONNECTIONS;
CONNECTION0* -  jdbc:derby:testdb
* = current connection

```

启用用户认证后，当我们要连接到`testdb`数据库时，我们必须提供用户凭据。

### 客户端服务器

在下一个示例中，我们将在客户端/服务器模式下使用 Derby。 我们有一个加密的`testdb`数据库。

```java
$ startNetworkServer &

```

Derby 服务器已启动。

```java
ij> CONNECT 'jdbc:derby://localhost:1527/dbs/testdb;bootPassword=3344kkllqq**';

```

首次连接`testdb`数据库时，必须提供启动密码。 这是因为以前我们已经加密了`testdb`数据库。

```java
ij> CONNECT 'jdbc:derby://localhost:1527/dbs/testdb';
ij> SHOW CONNECTIONS;
CONNECTION0* -  jdbc:derby://localhost:1527/dbs/testdb
* = current connection

```

一旦数据库已经启动，我们就不需要以客户端/服务器模式启动数据库。 与嵌入式模式不同，在嵌入式模式下，我们每次都连接到数据库时，也会对其进行引导。

在下一步中，我们将在客户端/服务器模式下启用用户认证。 为此，我们需要编辑`derby.properties`文件。

```java
$ stopNetworkServer

```

首先，如果 Derby 服务器正在运行，我们将其停止。 请注意，启用用户认证后，我们需要提供用户凭据才能停止服务器。 `stopNetworkServer`脚本带有`-user`和`-password`选项。

```java
$ cat dbs/derby.properties 
derby.connection.requireAuthentication=true
derby.user.user12=34klq*
derby.authentication.provider=BUILTIN

```

在 Derby 系统目录中，我们修改`derby.properties`文件。 如果文件不存在，我们将创建它。 在属性文件中，我们启用身份​​验证并使用密码创建用户。 我们还将认证供应器设置为 Derby `BUILTIN`。

```java
$ startNetworkServer &

```

我们启动 Derby 服务器。

```java
$ java  -Dderby.system.home=/home/janbodnar/programming/derby/dbs \
-Dij.protocol=jdbc:derby: -jar $DERBY_HOME/lib/derbyrun.jar ij
ij version 10.8
ij> 

```

我们启动`ij`工具。

```java
ij> CONNECT 'jdbc:derby:testdb;bootPassword=3344kkllqq**';
ERROR 08004: Connection authentication failure occurred.  Reason: Invalid authentication..

```

我们尝试连接到`testdb`数据库。 由于 Derby 服务器已重新启动，因此我们提供了启动密码。 但是，我们看到一条错误消息。 这是因为我们启用了用户认证。 我们还必须提供用户凭据。

```java
ij> CONNECT 'jdbc:derby:testdb;user=user12;password=34klq*;
bootPassword=3344kkllqq**';

```

使用此连接字符串，我们已成功连接到`testdb`数据库。

## 用户授权

通过 Derby 中的用户授权，可以授予和撤消访问系统，数据库，对象或 SQL 操作的权限。 我们可以在 Derby 中将用户授权属性设置为系统级属性或数据库级属性。

Derby 具有一些影响用户授权的属性。 `derby.database.defaultConnectionMode`属性控制默认访问模式。 如果未设置该属性，则该属性默认为`fullAccess`，即具有读写访问权限。 其他两个选项是`noAccess`和`readOnlyAccess`。 通过`derby.database.fullAccessUsers`和`derby.database.readOnlyAccessUsers`，我们可以控制哪些用户可以读写，哪些用户可以对数据库进行只读访问。 `derby.database.sqlAuthorization`属性启用 SQL 标准授权。 当`derby.database.sqlAuthorization`属性设置为`true`时，对象所有者可以使用`GRANT`和`REVOKE SQL`语句来设置特定数据库对象或特定 SQL 操作的用户权限。

我们可以授予或撤销的特权是：`DELETE`，`EXECUTE`，`INSERT`，`SELECT`，`REFERENCES`，`TRIGGER`和`UPDATE`。

为`derby.database.defaultConnectionMode`属性指定的访问模式将覆盖数据库对象所有者授予的权限。

```java
$ cat dbs/derby.properties 
derby.connection.requireAuthentication=true
derby.user.user12=34klq*
derby.user.user13=33kl33
derby.user.user14=14kl14
derby.user.user15=35rr++
derby.authentication.provider=BUILTIN
derby.database.defaultConnectionMode=readOnlyAccess
derby.database.fullAccessUsers=user12

```

我们修改`derby.properties`文件。 我们添加了三个用户。 一个用户`user12`拥有对数据库的完全访问权限。 其他三个具有默认的只读访问权限。

```java
export DERBY_OPTS=-Dderby.system.home=/home/janbodnar/programming/derby/dbs

```

请注意，为了使网络服务器知道带有`derby.property`的系统目录在哪里，我们将`DERBY_OPTS`变量设置为包含 derby 系统目录。

```java
$ stopNetworkServer
$ startNetworkServer &
$ java  -Dderby.system.home=/home/janbodnar/programming/derby/dbs \
-Dij.protocol=jdbc:derby: -jar $DERBY_HOME/lib/derbyrun.jar ij

```

我们重新启动网络服务器并启动`ij`工具。

```java
ij> CONNECT 'jdbc:derby://localhost/testdb;user=user13;
password=33kl33;bootPassword=3344kkllqq**';

```

我们使用`user13`用户连接到`testdb`数据库。 由于我们是第一次连接数据库，因此我们也将其引导。 因此，我们需要启动密码，因为该数据库先前已加密。

```java
ij> SELECT * FROM USER12.CARS;
ID         |NAME                          |PRICE      
------------------------------------------------------
1          |Audi                          |52642      
2          |Mercedes                      |57127      
3          |Skoda                         |9000       
4          |Volvo                         |29000      
5          |Bentley                       |350000     
6          |Citroen                       |21000      
7          |Hummer                        |41400      
8          |Volkswagen                    |21600      

8 rows selected

```

`user13`有权查看位于`USER12`模式中的`CARS`表中的数据。

```java
ij> INSERT INTO USER12.CARS VALUES(9, 'Toyota', 27000);
ERROR 25502: An SQL data change is not permitted for a read-only connection, 
user or database.

```

但是，尝试修改`CARS`表中的数据会导致错误。 未授予执行更改的权限。

```java
ij> DISCONNECT;
ij> CONNECT 'jdbc:derby://localhost/testdb;user=user12;
password=34klq*';

```

我们关闭连接，并连接为`user12`。 该用户在属性文件中具有完全访问权限。 即使`user12`是数据库的所有者和`CARS`表的所有者，他也不能修改表，除非使用 Derby 属性具有完全访问权限。

```java
ij> INSERT INTO CARS VALUES(9, 'Toyota', 27000);
1 row inserted/updated/deleted
ij> SELECT * FROM CARS WHERE ID = 9;
ID         |NAME                          |PRICE      
------------------------------------------------------
9          |Toyota                        |27000      

1 row selected

```

我们已经成功地在`CARS`表中添加了新行。

### SQL 授权

数据库或表之类的对象的所有者可以进一步限制使用数据库对象的权限。 我们可以使用`GRANT`和`REVOKE`语句来授予或撤消权限。 数据库和表的所有者是创建它们的当前用户。 请注意，`derby.database.defaultConnectionMode`会覆盖`GRANT`语句赋予的权限。 因此，如果用户具有通过默认连接方式指定的`readOnlyAccess`，则即使`GRANT`语句授予了该权限，该用户也无法修改数据库对象。

当`derby.database.sqlAuthorization`属性设置为`true`时，对象所有者可以使用`GRANT`和`REVOKE SQL`语句来设置特定数据库对象或特定 SQL 操作的用户权限。 请注意，在`derby.properties`文件中设置系统范围的属性仅对新数据库有效。 对于现有数据库，我们只能设置数据库范围的`derby.database.sqlAuthorization`属性。 在将`derby.database.sqlAuthorization`属性设置为`true`之后，我们无法将该属性设置回`false`。

```java
ij> CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.database.sqlAuthorization', 
'true');

```

`derby.database.sqlAuthorization`属性已设置为 true。 该属性是静态的。 我们必须重新启动`testdb`数据库以使该属性正常工作。

```java
ij> CONNECT 'jdbc:derby://localhost/testdb;shutdown=true;
user=user12;password=34klq*';

ij> CONNECT 'jdbc:derby://localhost/testdb;user=user12;
password=34klq*;bootPassword=3344kkllqq**';

```

我们关闭了`testdb`数据库，然后重新启动它。

```java
ij(CONNECTION1)> GRANT SELECT ON CARS TO user15;
0 rows inserted/updated/deleted

```

我们向表`CARS`的`user15`提供`SELECT`特权。

```java
ij(CONNECTION1)> UPDATE CARS SET PRICE=27001 WHERE ID=9;
1 row inserted/updated/deleted

ij(CONNECTION1)> SELECT * FROM CARS;
ID         |NAME                          |PRICE      
------------------------------------------------------
1          |Audi                          |52642      
2          |Mercedes                      |57127      
3          |Skoda                         |9000       
4          |Volvo                         |29000      
5          |Bentley                       |350000     
6          |Citroen                       |21000      
7          |Hummer                        |41400      
8          |Volkswagen                    |21600      
9          |Toyota                        |27001 

```

作为表所有者的`user12`具有完全特权。 上面的命令确认他在`CARS`表上具有`UPDATE`和`SELECT`特权。

```java
ij(CONNECTION1)> DISCONNECT;
ij> CONNECT 'jdbc:derby://localhost/testdb;user=user14;
password=14kl14';
ij(CONNECTION1)> SELECT * FROM USER12.CARS;
ERROR 42502: User 'USER14' does not have SELECT permission 
on column 'ID' of table 'USER12'.'CARS'.

```

我们从数据库断开连接，并以`user14`身份连接。 尝试执行`SELECT`语句会导致错误。 `user14`不具有对`CARS`表中`SELECT`数据的特权。

```java
ij(CONNECTION1)> DISCONNECT;
ij> CONNECT 'jdbc:derby://localhost/testdb;user=user15;
password=35rr++';

ij> SELECT * FROM USER12.CARS;
ID         |NAME                          |PRICE      
------------------------------------------------------
1          |Audi                          |52642      
2          |Mercedes                      |57127      
3          |Skoda                         |9000       
4          |Volvo                         |29000      
5          |Bentley                       |350000     
6          |Citroen                       |21000      
7          |Hummer                        |41400      
8          |Volkswagen                    |21600   
9          |Toyota                        |27000      

8 rows selected

```

接下来，我们以`user15`连接。 用户可以从`CARS`表中选择数据。

```java
ij(CONNECTION1)> SELECT * FROM USER12.AUTHORS;
ERROR 42502: User 'USER15' does not have SELECT 
permission on column 'ID' of table 'USER12'.'AUTHORS'.

```

但是他不能从`AUTHORS`表中选择数据。 表所有者`user12`没有授予从该表中选择数据的权限。

```java
ij(CONNECTION1)> UPDATE USER12.CARS SET PRICE=27000 WHERE ID=9;
ERROR 25502: An SQL data change is not permitted for a read-only 
connection, user or database.

```

`user15`在`CARS`表上也没有`UPDATE`特权。

在本章中，我们讨论了 Derby 中的安全性选项。