• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

miaoxing / plugin / 3766633246

pending completion
3766633246

push

github

twinh
feat(plugin): `BasePage` 增加 `pageInit` 事件

0 of 1 new or added line in 1 file covered. (0.0%)

84 existing lines in 24 files now uncovered.

902 of 2275 relevant lines covered (39.65%)

19.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.86
/src/Service/Jwt.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Miaoxing\Plugin\Service;
6

7
use DateTimeImmutable;
8
use Lcobucci\JWT\Builder;
9
use Lcobucci\JWT\Parser;
10
use Lcobucci\JWT\Signer;
11
use Lcobucci\JWT\Signer\Key;
12
use Lcobucci\JWT\Signer\Rsa\Sha256;
13
use Lcobucci\JWT\Token;
14
use Lcobucci\JWT\ValidationData;
15
use Miaoxing\Plugin\BaseService;
16

17
/**
18
 * @mixin \ReqMixin
19
 */
20
class Jwt extends BaseService
21
{
22
    public const CODE_EXPIRED = 200007;
23

24
    /**
25
     * @var string
26
     */
27
    protected $signerClass = Sha256::class;
28

29
    /**
30
     * @var string
31
     */
32
    protected $privateKey = 'file://storage/keys/private.key';
33

34
    /**
35
     * @var string
36
     */
37
    protected $publicKey = 'file://storage/keys/public.key';
38

39
    /**
40
     * @return Signer
41
     */
42
    public function getSigner(): Signer
43
    {
44
        return new $this->signerClass();
18✔
45
    }
46

47
    /**
48
     * @return string
49
     * @svc
50
     */
51
    protected function getPrivateKey()
52
    {
53
        return $this->privateKey;
18✔
54
    }
55

56
    /**
57
     * @param string $privateKey
58
     * @return $this
59
     * @svc
60
     */
61
    protected function setPrivateKey(string $privateKey)
62
    {
63
        $this->privateKey = $privateKey;
×
64
        return $this;
×
65
    }
66

67
    /**
68
     * @return string
69
     * @svc
70
     */
71
    protected function getPublicKey()
72
    {
73
        return $this->publicKey ?: $this->privateKey;
9✔
74
    }
75

76
    /**
77
     * @param string $publicKey
78
     * @return $this
79
     * @svc
80
     */
81
    protected function setPublicKey(string $publicKey)
82
    {
83
        $this->publicKey = $publicKey;
×
84
        return $this;
×
85
    }
86

87
    /**
88
     * @param array $claims
89
     * @param int $expire
90
     * @return Token
91
     * @throws \Exception
92
     * @svc
93
     */
94
    protected function generate(array $claims, int $expire = 86400 * 30): Token
95
    {
96
        $jti = base64_encode(random_bytes(8));
18✔
97
        $builder = (new Builder())->issuedBy($this->req->getSchemeAndHost())
18✔
98
            ->identifiedBy($jti)
18✔
99
            ->withHeader('jti', $jti)
18✔
100
            ->issuedAt(new DateTimeImmutable())
18✔
101
            ->expiresAt(new DateTimeImmutable('@' . (time() + $expire)));
18✔
102
        foreach ($claims as $name => $value) {
18✔
103
            $builder->withClaim($name, $value);
18✔
104
        }
105
        return $builder->getToken($this->getSigner(), new Key($this->getPrivateKey()));
18✔
106
    }
107

108
    /**
109
     * @param string $token
110
     * @return Ret
111
     * @svc
112
     */
113
    protected function verify(string $token): Ret
114
    {
115
        if (!$token) {
18✔
116
            return err('Token 不能为空');
3✔
117
        }
118

119
        if (false !== strpos($token, ' ')) {
15✔
120
            $token = explode(' ', $token)[1];
×
121
        }
122

123
        try {
124
            $parsedToken = (new Parser())->parse($token);
15✔
125
        } catch (\Throwable $e) {
3✔
126
            return err([
3✔
127
                'message' => '解析 Token 失败',
3✔
128
                'detail' => $e->getMessage(),
3✔
129
            ]);
2✔
130
        }
131

132
        if ($parsedToken->isExpired(new DateTimeImmutable())) {
12✔
133
            return err('Token 已过期', static::CODE_EXPIRED);
3✔
134
        }
135

136
        if (!$parsedToken->verify($this->getSigner(), new Key($this->getPublicKey()))) {
9✔
137
            return err('Token 签名错误');
3✔
138
        }
139

140
        $data = new ValidationData();
6✔
141
        $data->setIssuer($this->req->getSchemeAndHost());
6✔
142
        if (!$parsedToken->validate($data)) {
6✔
143
            return err('Token 内容错误');
3✔
144
        }
145

146
        return suc(['token' => $parsedToken]);
3✔
147
    }
148

149
    /**
150
     * 生成默认配置所需的密钥
151
     *
152
     * @svc
153
     * @experimental
154
     */
155
    protected function generateDefaultKeys(): Ret
156
    {
157
        $res = openssl_pkey_new([
×
UNCOV
158
            'private_key_bits' => 2048,
×
159
            'private_key_type' => \OPENSSL_KEYTYPE_RSA,
160
        ]);
161

162
        openssl_pkey_export($res, $privateKey);
×
163

164
        $details = openssl_pkey_get_details($res);
×
165
        $publicKey = $details['key'];
×
166

167
        openssl_free_key($res);
×
168

169
        $ret = $this->write($this->publicKey, $publicKey);
×
170
        if ($ret->isErr()) {
×
171
            return $ret;
×
172
        }
173
        return $this->write($this->privateKey, $privateKey);
×
174
    }
175

176
    protected function write($path, $content): Ret
177
    {
178
        if ('file://' === substr($path, 0, 7)) {
×
179
            $path = substr($path, 7);
×
180
        }
181

182
        $dir = dirname($path);
×
183
        //return !(!\is_dir($directory) && !@\mkdir($directory, 0777, \true) && !\is_dir($directory));
184
        if (!is_dir($dir) && !@mkdir($dir, 0777, true) && !is_dir($dir)) {
×
185
            return err(['创建目录 %s 失败,请检查是否有权限操作', $dir]);
×
186
        }
187

188
        file_put_contents($path, $content);
×
189
        return suc();
×
190
    }
191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc