如何在 PostgreSQL 中创建适合会话 ID 的随机字符串?

我想创建一个随机字符串,用于使用 PostgreSQL 进行会话验证。我知道我可以得到一个随机数与 SELECT random(),所以我尝试 SELECT md5(random()),但这不工作。我怎么能这么做?

128129 次浏览

我建议这个简单的解决办法:

这是一个非常简单的函数,它返回一个给定长度的随机字符串:

Create or replace function random_string(length integer) returns text as
$$
declare
chars text[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
result text := '';
i integer := 0;
begin
if length < 0 then
raise exception 'Given length cannot be less than 0';
end if;
for i in 1..length loop
result := result || chars[1+random()*(array_length(chars, 1)-1)];
end loop;
return result;
end;
$$ language plpgsql;

用法:

select random_string(15);

输出示例:

select random_string(15) from generate_series(1,15);


random_string
-----------------
5emZKMYUB9C2vT6
3i4JfnKraWduR0J
R5xEfIZEllNynJR
tMAxfql0iMWMIxM
aPSYd7pDLcyibl2
3fPDd54P5llb84Z
VeywDb53oQfn9GZ
BJGaXtfaIkN4NV8
w1mvxzX33NTiBby
knI1Opt4QDonHCJ
P9KC5IBcLE0owBQ
vvEEwc4qfV4VJLg
ckpwwuG8YbMYQJi
rFf6TchXTO3XsLs
axdQvaLBitm6SDP
(15 rows)

我最近在使用 PostgreSQL,我想我找到了一个更好的解决方案,只使用内置的 PostgreSQL 方法——不使用 pl/pgsql。唯一的限制是它目前只生成 UPCASE 字符串、数字或小写字符串。

template1=> SELECT array_to_string(ARRAY(SELECT chr((65 + round(random() * 25)) :: integer) FROM generate_series(1,12)), '');
array_to_string
-----------------
TFBEGODDVTDM


template1=> SELECT array_to_string(ARRAY(SELECT chr((48 + round(random() * 9)) :: integer) FROM generate_series(1,12)), '');
array_to_string
-----------------
868778103681

generate_series方法的第二个参数指定字符串的长度。

你可以像这样修复你最初的尝试:

SELECT md5(random()::text);

比其他一些建议要简单得多。 : -)

在 Marcin 的解决方案的基础上,您可以使用任意的字母表(在本例中,是所有62个 ASCII 字母数字字符) :

SELECT array_to_string(array
(
select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', trunc(random() * 62)::integer + 1, 1)
FROM   generate_series(1, 12)), '');

select * from md5(to_char(random(), '0.9999999999999999'));

INTEGER 参数定义字符串的长度。保证涵盖所有62个字母字符的概率相同(不像其他解决方案漂浮在互联网上)。

CREATE OR REPLACE FUNCTION random_string(INTEGER)
RETURNS TEXT AS
$BODY$
SELECT array_to_string(
ARRAY (
SELECT substring(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
FROM (ceil(random()*62))::int FOR 1
)
FROM generate_series(1, $1)
),
''
)
$BODY$
LANGUAGE sql VOLATILE;

虽然默认情况下不是活动的,但是您可以激活一个核心扩展:

CREATE EXTENSION IF NOT EXISTS pgcrypto;

然后,您的语句变成对 gen _ salt ()的简单调用,它会生成一个随机字符串:

select gen_salt('md5') from generate_series(1,4);


gen_salt
-----------
$1$M.QRlF4U
$1$cv7bNJDM
$1$av34779p
$1$ZQkrCXHD

前导数是一个散列标识符。有几种算法可用,每种算法都有自己的标识符:

  • Md5:1元
  • Bf: $2a $06 $
  • Des: 无标识符
  • Xdes: _ J9. .

更多关于分机的资料:


剪辑

正如 Evan Carrol 所指出的,从 v9.4开始,您可以使用 gen_random_uuid()

Http://www.postgresql.org/docs/9.4/static/pgcrypto.html

我不认为你是在寻找一个随机的字符串本身。会话验证需要的是一个保证唯一的字符串。是否存储用于审核的会话验证信息?在这种情况下,您需要字符串在会话之间是唯一的。我知道两种相当简单的方法:

  1. 使用序列。适合在单个数据库上使用。
  2. 使用 UUID。通用唯一,所以在分布式环境中也很好。

UUIDs are 保证 to be unique by virtue of their algorithm for generation; effectively it is 非常非常 unlikely that you will generate two identical numbers on any machine, at any time, ever (note that this is much stronger than on random strings, which have a far smaller periodicity than UUIDs).

需要加载 uuid-ossp 扩展才能使用 UUID。安装完成后,在 SELECT、 INSERT 或 UPDATE 调用中调用任何可用的 uuid _ gener_ vXXX ()函数。Uuid 类型是一个16字节的数字,但它也有一个字符串表示形式。

@ Kavius 推荐使用 pgcrypto,但是用 gen_salt代替 gen_random_bytes怎么样? 用 sha512代替 md5怎么样?

create extension if not exists pgcrypto;
select digest(gen_random_bytes(1024), 'sha512');

文件:

F. 25.5. 随机数据函数

Gen _ Random _ bytea 返回值

返回加密强随机字节数。最多1024 字节可以一次提取。这是为了避免 随机性生成器池随机性生成器池。

select encode(decode(md5(random()::text), 'hex')||decode(md5(random()::text), 'hex'), 'base64')

请使用 string_agg

SELECT string_agg (substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ceil (random() * 62)::integer, 1), '')
FROM   generate_series(1, 45);

我正在将它与 MD5一起使用,以生成一个 UUID。我只需要一个比 random ()整数更多位的随机值。

您可以从 UUID 获得128位随机数。这是在现代 PostgreSQL 中完成工作的方法。

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();


gen_random_uuid
--------------------------------------
202ed325-b8b1-477f-8494-02475973a28f

可能是 UUID 上的文档也值得一读

数据类型 UUID 存储由 RFC 4122,ISO/IEC 9834-8:2005和相关标准定义的通用唯一标识符(UUID)。(有些系统将这种数据类型称为全局唯一标识符,或 GUID。)这个标识符是一个 128位数量,它是由一个算法生成的,这个算法选择使得同一个标识符不太可能由已知宇宙中的任何其他人使用相同的算法生成。因此,对于分布式系统,这些标识符比序列生成器提供了更好的唯一性保证,序列生成器仅在单个数据库中是唯一的。

How rare is a collision with UUID, or guessable? Assuming they're random,

大约需要生成100万亿个版本4的 UUID,才有十亿分之一的几率出现单个重复(“冲突”)。只有在产生了261个 UUID (2.3 x 10 ^ 18或2.3乘以18)之后,一次碰撞的几率才上升到50% 。将这些数字与数据库联系起来,并考虑版本4 UUID 冲突的概率是否可以忽略的问题,考虑一个包含2.3乘以4的版本4 UUID 的文件,其中50% 的几率包含一个 UUID 冲突。如果没有其他数据或开销的话,它的大小将是36EB,比目前存在的最大数据库大几千倍,这些数据库的大小大约是 PB。按照每秒生成10亿个 UUID 的速度,生成文件的 UUID 将需要73年的时间。如果没有备份或冗余,它还需要大约360万10TB 的硬盘或磁带来存储。对于单个处理器来说,以每秒1G 的“磁盘到缓冲区”传输速率读取文件需要3000多年的时间。由于驱动器的不可恢复读取错误率是每1018位读取1位,最好的情况下,而文件将包含约1020位,仅仅从头到尾读取文件一次,就会导致误读 UUID 比重复读取多约100倍。存储、网络、电源和其他硬件和软件错误无疑要比 UUID 复制问题频繁数千倍。

source: 维基百科

总而言之,

  • UUID 是标准化的。
  • gen_random_uuid()是128位随机存储在128位(2 * * 128组合)。0-浪费。
  • 在 PostgreSQL 中,random()只生成52位随机数(2 * * 52个组合)。
  • 存储为 UUID 的 md5()为128位,但它只能与其输入(如果使用 random(),则为52位)一样随机
  • md5() stored as text is 288 bits, but it only can only be as random as its input (如果使用 random(),则为52位) - over twice the size of a UUID and a fraction of the randomness)
  • md5()作为一个散列,可以如此优化,它并没有有效地做很多。
  • UUID 对于存储来说是高效的: PostgreSQL 提供的类型正好是128位。不像 textvarchar等存储为 varlena,它有字符串长度的开销。
  • PostgreSQL nifty UUID comes with some default operators, castings, and features.
create extension if not exists pgcrypto;

那么

SELECT encode(gen_random_bytes(20),'base64')

甚至

SELECT encode(gen_random_bytes(20),'hex')

这是为了20字节 = 160位的随机性(例如,只要 sha1)。