创建密码生成器
发布于 作者: 史蒂夫·麦克杜格尔
密码生成是我们每个人在某个时候都会想到的事情,但我们如何才能做到这一点,并使这些密码易于记忆且安全呢?
在本教程中,我将带您了解我最近如何为乐趣构建密码生成器以及我的想法。我创建它不仅仅是为了 Laravel,而是作为一种与框架无关的方法,我可以轻松地在 Laravel 中使用它。
在使用创建密码工具时,您通常会得到一些用连字符连接在一起的单词。我想制作一些我可以使用并相对容易记住的东西,但可以选择通过在某些地方将字母替换为数字来使密码更“安全”。
让我们开始写代码吧!
我们首先要定义一个标准接口,我们的生成器将要实现它——这将允许我们在 Laravel 中、Laravel 之外以及任何我们想要使用它的地方使用它。
interface GeneratorContract{ /** * @return string */ public function generate(): string; /** * @return string */ public function generateSecure(): string;}
我们希望我们的实现有两种可能的方法,一种是使用令人难忘的单词生成随机密码,另一种是生成看起来更安全的相同密码。一个完美的例子是
flying-fish-swimming-lizard
- 令人难忘的 fly1ng-f1sh-sw1mm1ng-l1z4rd
- “安全”
为了实现这一点,我们需要创建一种生成单词的方法。我们需要名词
和形容词
,这意味着我们需要创建另一个要遵循的契约。这个契约将是一种从注册列表中获取随机单词的方法,无论是标准方法还是我们所需的安全
方法。
interface ListContract{ public function random(): string; public function secure(): string;}
这个契约上的两种方法都将实现大致相同的事情,唯一的区别是其中一种方法在返回之前我们会进行一些额外的处理。让我们深入代码,看看这是如何工作的。
我们知道我们需要一个名词列表和一个形容词列表,它们在行为方面都是一样的。将来,我们可能会稍微改变一下,这样我们也可以包含动词,但现在,我们将专注于名词和形容词。为了实现这一点,我将使用 PHP 特性将共享行为添加到每个实现中。
trait HasWords{ /** * @param array $words */ public function __construct( private readonly array $words, ) { } public function random(): string { return $this->words[array_rand($this->words)]; }}
首先,我们创建特性,我们知道我们的实现将使用单词数组创建。然后,我们添加从这个数组中选择随机项目的方法。这将允许我们通过多次调用此方法并将结果连接起来来构建我们标准的令人难忘的密码。让我们将它添加到我们需要创建的两个实现中。
final class AdjectiveList implements ListContract{ use HasWords;} final class NounList implements ListContract{ use HasWords;}
我们的两个实现将允许我们生成我们需要的一半东西。接下来,我们必须考虑如何创建我们的“安全”密码。让我们重新审视我们创建的特性。
trait HasWords{ /** * @param array $words */ public function __construct( private readonly array $words, ) { } public function random(): string { return $this->words[array_rand($this->words)]; } public function secure(): string { $word = $this->random(); $asArray = str_split($word); $secureArray = array_map( callback: fn (string $item): string => $this->convertToNumerical($item), array: $asArray, ); return implode('', $secureArray); } public function convertToNumerical(string $item): string { return match ($item) { 'a' => '4', 'e' => '3', 'i' => '1', 'o' => '0', default => $item, }; }}
那么如何做到这一点呢?作为“安全”密码,我们仍然希望从我们的列表中获取一个随机单词,但我们需要进行一些额外的处理。我们将单词拆分为其核心字母,以获得一个数组。然后,我们可以遍历每个字母,并调用我们的 convert 方法来更新输入。我们的 convert 方法是一个简单的匹配语句,我们可以用它来将输入映射到输出,因此我们正在控制这些内容的写入方式。我在这里将它们映射到您可能发现的典型方法,其中数字表示看起来像字母表示——让我们仍然可以拥有令人难忘的密码。
我们的列表控制现在起作用了,我们可以创建一个名词和形容词列表——它们被注入构造函数中。我们可以直接获取单词,或者将其传递给转换器以使其更“安全”。我们的下一步是看看我们生成器的实现。到目前为止,我们只有列表本身的实现。让我们看更多代码。
final class PasswordGenerator implements GeneratorContract{ public function __construct( private readonly ListContract $nouns, private readonly ListContract $adjectives, ) { } public function generate(): string { return $this->build( $this->nouns->random(), $this->adjectives->random(), $this->nouns->random(), $this->adjectives->random(), ); } public function generateSecure(): string { return $this->build( $this->nouns->secure(), $this->adjectives->secure(), $this->nouns->secure(), $this->adjectives->secure(), ); } private function build(string ...$parts): string { return implode('-', $parts); }}
我们的密码生成器将两个列表实现带入其构造函数中,以便我们可以访问它们。然后,我们实现生成密码的两种方法:令人难忘的和安全的。
对于每种方法,我们都会在内部调用一个方法,该方法将允许我们通过使用连接字符串将数组连接起来来构建密码。如果我们想将其扩展到更多单词,我们必须传入更多随机单词。
我们的契约不需要知道build
方法,因为这是一个实现细节。我们可以扩展此方法来自定义连接字符,但我将留给您自己实现,如果您需要的话。
作为额外的附加内容,我们现在将创建一个服务提供者和外观,以便您可以快速调用它或将其注入到您的应用程序代码中。不过,这部分只适用于 Laravel。到目前为止,我们一直专注于与框架无关的代码。
final class PackageServiceProvider extends ServiceProvider{ public function register(): void { $this->app->singleton( abstract: GeneratorContract::class, concrete: fn (): GeneratorContract => new PasswordGenerator( nouns: new NounList( words: (array) config('password-generator.nouns'), ), adjectives: new AdjectiveList( words: (array) config('password-generator.adjectives'), ), ), ); } public function boot(): void { if ($this->app->runningInConsole()) { $this->publishes( paths: [ __DIR__ . '/../../config/password-generator.php', config_path('password-generator.php'), ], groups: 'password-generator-config', ); } }}
在这里,我们正在加载我们的配置,它将容纳我们希望在密码生成器中使用的名词和形容词。我们还向 Laravel 提供了一组关于如何从容器中加载我们的实现的说明。
现在,外观将以相同的方式工作,但会从容器中解析实现,允许您静态调用您希望调用的方法。
/** * @method static string generate() * @method static string generateSecure() * * @see GeneratorContract */final class Generator extends Facade{ protected static function getFacadeAccessor(): string { return GeneratorContract::class; }}
我们的外观返回我们用来绑定实现的契约,并且具有说明可用的方法及其预期返回类型的文档块。
现在我们只需要调用密码生成器,我们就可以为我们的用户提供随机生成的密码。
Generator::generate(); // flying-fish-swimming-lizardGenerator::generateSecure(); // fly1ng-f1sh-sw1mm1ng-l1z4rd
我创建了一个包来提供上面的代码,它还有更多代码,如果您希望更深入地了解示例,可以参考。您可以在此处找到此代码库:https://github.com/JustSteveKing/password-generator
免责声明。这并不适合在生产环境中使用来创建您的密码。我使用这个的用例实际上是生成一次性使用代码,例如一次性密码。这不是最安全的,因为单词列表非常小,会让您容易受到潜在的字典攻击。