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

ger*_*rsh 98 random postgresql

我想制作一个随机字符串,用于使用PostgreSQL进行会话验证.我知道我可以得到一个随机数SELECT random(),所以我试过SELECT md5(random()),但这不起作用.我怎样才能做到这一点?

Pet*_*aut 222

您可以像这样修复您的初始尝试:

SELECT md5(random()::text);
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,这仅返回"十六进制数字字母"{0..9,a..f}上的字符串.可能还不够 - 取决于你想用它们做什么. (16认同)
  • 以十六进制表示时,MD5字符串的长度始终为32个字符.如果你想要一个长度为64的字符串,你可以连接2个MD5字符串:`SELECT concat(md5(random():: text),md5(random():: text));`如果你想在中间的某个地方(例如50个字符),你可以得到一个子串:`SELECT substr(concat(md5(random():: text),md5(random():: text)),0,50); (8认同)
  • 对于会话ID来说不是一个很好的解决方案,没有太多的随机性.答案也是6岁.[使用`gen_random_uuid()`](http://stackoverflow.com/a/41608000/124486)查看完全不同的方法:更快,更随机,更有效地存储在数据库中. (2认同)

Szy*_*ski 72

我建议这个简单的解决方案:

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

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;
Run Code Online (Sandbox Code Playgroud)

用法:

select random_string(15);
Run Code Online (Sandbox Code Playgroud)

示例输出:

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)
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案使用chars数组两端的值 - 0和z - 与其余数据一样频繁.为了更均匀的字符分布,我将`chars [1 + random()*(array_length(chars,1)-1)]`替换为`chars [ceil(61*random())]` (6认同)
  • 很好的答案,除了它不使用安全的随机数生成器,因此对会话ID不太好.请参阅:http://stackoverflow.com/questions/9816114/quality-of-postgresqls-random-function (2认同)

gro*_*urk 29

在Marcin的解决方案的基础上,您可以使用任意字母表(在这种情况下,所有62个ASCII字母数字字符):

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


Eva*_*oll 19

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

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();

           gen_random_uuid            
--------------------------------------
 202ed325-b8b1-477f-8494-02475973a28f
Run Code Online (Sandbox Code Playgroud)

值得一读UUID上的文档

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

与UUID的碰撞有多罕见,或者是可猜测的?假设它们是随机的,

需要生成大约100万亿版本的4个UUID才能获得单个重复的十亿分之一("冲突").只有在生成261个UUID(2.3 x 1018或2.3 quintillion)之后,一次碰撞的几率才会上升到50%.将这些数字与数据库相关联,并考虑到版本4 UUID冲突的概率是否可忽略的问题,请考虑包含2.3个quintillion版本4 UUID的文件,其中50%的可能性包含一个UUID冲突.它假设没有其他数据或开销,大小为36艾字节,是目前存在的最大数据库的数千倍,大约为千兆字节.以每秒生成10亿UUID的速率,生成文件的UUID需要73年.假设没有备份或冗余,它还需要大约360万个10TB硬盘或磁带盒来存储它.以典型的"磁盘到缓冲"传输速率(每秒1千兆位)读取文件,单个处理器需要3000多年.由于驱动器的不可恢复读取错误率是每1018位读取1位,充其量,当文件包含大约1020位时,仅从端到端读取文件一次将导致至少大约100倍的错误读取UUID而不是重复.毫无疑问,存储,网络,电源和其他硬件和软件错误比UUID重复问题的频率高出数千倍.- 来源维基百科

综上所述,

  • UUID是标准化的.
  • gen_random_uuid()是128位随机存储在128位(2**128组合)中.0垃圾.
  • random() 在PostgreSQL中只生成52位随机数(2**52种组合).
  • md5()存储为UUID的是128位,但它只能与其输入一样随机(如果使用则为52位random())
  • md5()作为文本存储的是288位,但它只能与其输入一样随机(如果使用则为52位random()) - 超过UUID大小的两倍和随机性的一小部分)
  • md5() 作为哈希,可以如此优化,以至于它不能有效地做很多事情.
  • UUID对存储非常有效:PostgreSQL提供的类型恰好是128位.不像textvarchar等其存储为varlena具有用于字符串的长度的开销.
  • PostgreSQL漂亮的UUID附带了一些默认的运算符,元素和功能.

  • 部分不正确:由于版本使用4位,变量使用2位,因此正确生成的随机UUID只有122个随机位:https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29 (3认同)
  • 如果源代码不执行此处写的内容,则它不是UUID,因此PostgreSQL不应该这样调用。 (2认同)

Mar*_*ski 14

我最近玩的是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
Run Code Online (Sandbox Code Playgroud)

方法的第二个参数generate_series指示字符串的长度.

  • 我喜欢这个,但是当我使用UPDATE语句时,发现所有行都设置为相同的随机密码而不是唯一密码.我通过将主键ID添加到公式中来解决这个问题.我将它添加到随机值并再次减去它.随机性没有改变,但PostgreSQL被欺骗重新计算每一行的值.这是一个例子,使用主键名"my_id":`array_to_string(ARRAY(SELECT chr((65 + round((random()+ my_id-my)*25)):: integer)FROM generate_series(1,8 )),'')` (8认同)
  • 不.您正在回答'如何生成随机**会话ID**'而不是'如何生成随机**字符串**'.你已经根据描述中的两个词改变了问题(和标题)的含义.你在回答不同的问题.并继续滥用你的节制力来改变问题意味着什么. (4认同)

Jef*_*ave 11

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

CREATE EXTENSION IF NOT EXISTS pgcrypto;
Run Code Online (Sandbox Code Playgroud)

然后你的语句变成对gen_salt()的简单调用,它生成一个随机字符串:

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

 gen_salt
-----------
$1$M.QRlF4U
$1$cv7bNJDM
$1$av34779p
$1$ZQkrCXHD
Run Code Online (Sandbox Code Playgroud)

前导号码是散列标识符.有几种算法可用,每种都有自己的标识符:

  • 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


And*_*lfe 11

请使用string_agg!

SELECT string_agg (substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ceil (random() * 62)::integer, 1), '')
FROM   generate_series(1, 45);
Run Code Online (Sandbox Code Playgroud)

我正在使用MD5来生成UUID.我只想要一个比一个random ()整数更多位的随机值.


Jar*_*eck 9

@Kavius 推荐使用pgcrypto,而不是gen_salt,那么gen_random_bytes呢?而sha512不是md5呢?

create extension if not exists pgcrypto;
select digest(gen_random_bytes(1024), 'sha512');
Run Code Online (Sandbox Code Playgroud)

文档:

F.25.5。随机数据函数

gen_random_bytes(count integer) 返回字节

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


Ben*_*bre 7

create extension if not exists pgcrypto;
Run Code Online (Sandbox Code Playgroud)

然后

SELECT encode(gen_random_bytes(20),'base64')
Run Code Online (Sandbox Code Playgroud)

甚至

SELECT encode(gen_random_bytes(20),'hex')
Run Code Online (Sandbox Code Playgroud)

这适用于 20 字节 = 160 位随机性(例如 sha1)。


Lar*_*dua 6

INTEGER 参数定义字符串的长度。保证以相等的概率覆盖所有 62 个字母数字字符(与 Internet 上漂浮的其他一些解决方案不同)。

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;
Run Code Online (Sandbox Code Playgroud)

  • @EvanCarroll:平心而论,据我所知,`gen_random_uuid()` 出现在 9.4 版中,该版本于 2014 年 12 月 18 日发布,比您投反对票的答案晚了一年多。额外的挑剔:答案只有 3 1/2 岁 :-) 但是你说得对,现在我们有了 `gen_random_uuid()`,这就是应该使用的。因此,我会赞成你的回答。 (3认同)

Pat*_*ick 6

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

  1. 使用序列.适合在单个数据库上使用.
  2. 使用UUID.普遍独特,在分布式环境中也很好.

UUID 凭借其生成算法保证是唯一的; 有效是非常不可能的,你会不会产生任何机器上有两个相同的数字,在任何时候,曾(注意,这是不是随机字符串,它比的UUID小得多的周期性更强).

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

  • @ jmar777 UUID的全部目的是难以猜测和高度随机.除了v1版本,它们具有非常高的周期性; v4完全是128位随机.它们用于您执行的每个网上银行交易.如果它们足够好,那么它们就足够了. (5认同)

小智 5

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