Hysteria2Controller.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. namespace App\Http\Controllers\Api\WebApi;
  3. use App\Http\Controllers\Controller;
  4. use App\Models\Node;
  5. use App\Models\User;
  6. use Illuminate\Http\JsonResponse;
  7. use Illuminate\Http\Request;
  8. use Log;
  9. use Validator;
  10. /**
  11. * Hysteria2 API控制器
  12. * 提供Hysteria2 HTTP验证端点.
  13. */
  14. class Hysteria2Controller extends Controller
  15. {
  16. private function returnFormat(array $data = [], bool $ok = false): JsonResponse
  17. {
  18. return response()->json(['ok' => $ok, ...$data]);
  19. }
  20. /**
  21. * Hysteria2 HTTP验证端点
  22. * 用于验证用户身份,接收Hysteria2服务端的验证请求
  23. */
  24. public function authenticate(Node $node, Request $request): JsonResponse
  25. {
  26. // 使用Validator验证输入,更详细地验证各个字段格式
  27. $validator = Validator::make($request->all(), [
  28. 'auth' => ['required', 'string', 'regex:/^\d+:[^:]+$/'],
  29. 'addr' => ['nullable', 'string', 'regex:/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[[0-9a-fA-F:]+\]):\d+$/'],
  30. 'tx' => 'sometimes|numeric|min:0',
  31. ]);
  32. if ($validator->fails()) {
  33. return $this->returnFormat(['error' => 'Invalid request format']);
  34. }
  35. $credentials = $validator->validated();
  36. // 从auth字段提取端口和密码 (格式: port:passwd)
  37. $auth = $credentials['auth'];
  38. $authParts = explode(':', $auth, 2);
  39. if (count($authParts) < 2) {
  40. return $this->returnFormat(['error' => 'Invalid auth format']);
  41. }
  42. [$port, $password] = $authParts;
  43. // 根据端口查找用户
  44. $user = User::where('port', $port)->first();
  45. if (! $user) {
  46. return $this->returnFormat(['error' => 'User not found']);
  47. }
  48. // 验证用户状态
  49. if (! $user->enable) {
  50. return $this->returnFormat(['error' => 'User was disabled']);
  51. }
  52. // 验证密码
  53. if ($user->passwd !== $password) {
  54. return $this->returnFormat(['error' => 'Password incorrect']);
  55. }
  56. if (! $user->nodes()->where('node.id', $node->id)->exists()) {
  57. return $this->returnFormat(['error' => 'Does not have permission to access this node']);
  58. }
  59. // 从addr字段提取客户端IP和端口
  60. $addr = $credentials['addr'] ?? null;
  61. // 区分IPv4和IPv6格式来解析IP和端口
  62. if ($addr) {
  63. // parse_url 无法直接解析没有 scheme 的地址,所以拼上 //
  64. $parsed = parse_url('//'.$addr);
  65. $clientIp = str_replace(['[', ']'], '', $parsed['host'] ?? '');
  66. $port = $parsed['port'] ?? null;
  67. if (! filter_var($clientIp, FILTER_VALIDATE_IP)) {
  68. return $this->returnFormat(['error' => 'Invalid IP address']);
  69. }
  70. // 记录在线IP日志
  71. $node->onlineIps()->create([
  72. 'user_id' => $user->id,
  73. 'ip' => $clientIp,
  74. 'port' => $port,
  75. 'created_at' => time(),
  76. ]);
  77. } else {
  78. Log::error('Hysteria2: addr is null', $credentials);
  79. }
  80. return $this->returnFormat(['id' => (string) $user->id], true);
  81. }
  82. }