Laravel 5 - перенаправление на HTTPS
Работаю над своим первым проектом Laravel 5 и не уверен, где и как разместить логику для принудительного использования HTTPS в моем приложении. Решающим моментом здесь является то, что существует множество доменов, указывающих на приложение, и только два из трех используют SSL (третий - запасной домен, долгая история). Поэтому я хотел бы разобраться с этим в логике моего приложения, а не в .htaccess.
В Laravel 4.2 я выполнил перенаправление с помощью этого кода, расположенного в filters.php
:
App::before(function($request)
{
if( ! Request::secure())
{
return Redirect::secure(Request::path());
}
});
Я думаю, что промежуточное программное обеспечение - это то, где что-то вроде этого должно быть реализовано, но я не могу понять это с его помощью.
Спасибо!
ОБНОВЛЕНИЕ
Если вы используете Cloudflare, как я, это достигается путем добавления нового правила страницы в вашу панель управления.
16 answers
Вы можете заставить его работать с классом промежуточного программного обеспечения. Позвольте мне дать вам идею.
namespace MyApp\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\App;
class HttpsProtocol {
public function handle($request, Closure $next)
{
if (!$request->secure() && App::environment() === 'production') {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}
Затем примените это промежуточное программное обеспечение к каждому запросу, добавляя настройку правила в файл Kernel.php
, например:
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
// appending custom middleware
'MyApp\Http\Middleware\HttpsProtocol'
];
В приведенном выше примере промежуточное программное обеспечение перенаправит каждый запрос на https, если:
- Текущий запрос не содержит защищенного протокола (http)
- , если ваше окружение равно
production
. Итак, просто отрегулируйте настройки в соответствии с вашими предпочтениями.
Вспышка облака
Я использую этот код в производственной среде с подстановочным знаком SSL, и код работает правильно. Если я удалю && App::environment() === 'production'
и протестирую его в localhost, перенаправление также будет работать. Таким образом, наличие или отсутствие установленного SSL не является проблемой. Похоже, вам нужно очень внимательно следить за своим слоем Cloudflare, чтобы перенаправиться на протокол Https.
Изменить 23/03/2015
Благодаря предложению @Adam Link
: скорее всего, это вызвано заголовками эта вспышка облака проходит. CloudFlare, скорее всего, попадает на ваш сервер через HTTP и передает заголовок X-Forwarded-Proto, который объявляет, что он перенаправляет запрос HTTPS. Вам нужно добавить еще одну строку в ваше промежуточное программное обеспечение, в которой говорится...
$request->setTrustedProxies( [ $request->getClientIp() ] );
...чтобы доверять заголовкам, которые отправляет CloudFlare. Это остановит цикл перенаправления
Редактировать 27.09.2016 - Laravel v5.3
Просто нужно добавить класс промежуточного программного обеспечения в группу web
в kernel.php file
:
protected $middlewareGroups = [
'web' => [
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
// here
\MyApp\Http\Middleware\HttpsProtocol::class
],
];
Помните, что
web
группа - это применяется к каждому маршруту по умолчанию, поэтому вам не нужно явно задаватьweb
в маршрутах или контроллерах.
Редактировать 23.08.2018 - Laravel v5.7
- Для перенаправления запроса в зависимости от среды вы можете использовать
App::environment() === 'production'
. Для предыдущей версии былоenv('APP_ENV') === 'production'
. - Использование
\URL::forceScheme('https');
на самом деле не перенаправляет. Он просто создает ссылки сhttps://
после того, как веб-сайт будет отображен.
Другой вариант, который сработал для меня, в AppServiceProvider поместите этот код в метод загрузки:
\URL::forceScheme('https');
Функция, написанная до forceSchema('https'), была неправильной, ее forceScheme
В качестве альтернативы, если вы используете Apache, вы можете использовать файл .htaccess
для принудительного использования ваших URL-адресов для использования префикса https
. В Laravel 5.4 я добавил следующие строки в свой файл .htaccess
, и это сработало для меня.
RewriteEngine On
RewriteCond %{HTTPS} !on
RewriteRule ^.*$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Для laravel 5.4 используйте этот формат для перенаправления https вместо .htaccess
namespace App\Providers;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
URL::forceScheme('https');
}
}
Похоже на ответ маникса, но в одном месте. Промежуточное программное обеспечение для принудительного использования HTTPS
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ForceHttps
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (!app()->environment('local')) {
// for Proxies
Request::setTrustedProxies([$request->getClientIp()]);
if (!$request->isSecure()) {
return redirect()->secure($request->getRequestUri());
}
}
return $next($request);
}
}
Это для Larave 5.2.x и выше. Если вы хотите иметь возможность предоставлять какой-либо контент по протоколу HTTPS, а другой - по протоколу HTTP, вот решение, которое сработало для меня. Вы можете задаться вопросом, почему кто-то хочет предоставлять только некоторый контент по протоколу HTTPS? Почему бы не подавать все через HTTPS?
Хотя совершенно нормально обслуживать весь сайт по протоколу HTTPS, отключение всего по протоколу HTTPS влечет за собой дополнительные накладные расходы на вашем сервере. Помните, что шифрование обходится недешево. Небольшие накладные расходы также влияет на время отклика вашего приложения. Вы могли бы возразить, что товарное оборудование дешево, а влияние незначительно, но я отвлекся:) Мне не нравится идея предоставления больших страниц маркетингового контента с изображениями и т. Д. По протоколу https. Итак, вот оно. Это похоже на то, что другие предлагали выше, используя промежуточное программное обеспечение, но это полноценное решение, которое позволяет переключаться между HTTP/HTTPS.
Сначала создайте промежуточное программное обеспечение.
php artisan make:middleware ForceSSL
Вот как должно выглядеть ваше промежуточное программное обеспечение нравиться.
<?php
namespace App\Http\Middleware;
use Closure;
class ForceSSL
{
public function handle($request, Closure $next)
{
if (!$request->secure()) {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}
Обратите внимание, что я не фильтрую на основе среды, потому что у меня есть настройка HTTPS как для локальной разработки, так и для производства, поэтому в этом нет необходимости.
Добавьте следующее в свое программное обеспечение routeMiddleware\App\Http\Kernel.php чтобы вы могли выбрать, какая группа маршрутов должна использовать SSL.
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'can' => \Illuminate\Foundation\Http\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'forceSSL' => \App\Http\Middleware\ForceSSL::class,
];
Далее, я хотел бы защитить две основные группы входа/регистрации и т. Д. И все остальное за промежуточным программным обеспечением аутентификации.
Route::group(array('middleware' => 'forceSSL'), function() {
/*user auth*/
Route::get('login', 'AuthController@showLogin');
Route::post('login', 'AuthController@doLogin');
// Password reset routes...
Route::get('password/reset/{token}', 'Auth\PasswordController@getReset');
Route::post('password/reset', 'Auth\PasswordController@postReset');
//other routes like signup etc
});
Route::group(['middleware' => ['auth','forceSSL']], function()
{
Route::get('dashboard', function(){
return view('app.dashboard');
});
Route::get('logout', 'AuthController@doLogout');
//other routes for your application
});
Подтвердите, что ваши промежуточные программы применяются к вашим маршрутам правильно с консоли.
php artisan route:list
Теперь, когда вы защитили все формы или конфиденциальные области вашего приложения, теперь главное - использовать шаблон представления для определения ваших безопасных и общедоступных (не https) ссылок.
Основываясь на приведенном выше примере, вы бы отображали свои защищенные ссылки следующим образом -
<a href="{{secure_url('/login')}}">Login</a>
<a href="{{secure_url('/signup')}}">SignUp</a>
Небезопасные ссылки могут быть представлены в виде
<a href="{{url('/aboutus',[],false)}}">About US</a></li>
<a href="{{url('/promotion',[],false)}}">Get the deal now!</a></li>
Что это делает, так это отображает полный URL-адрес, такой как https://yourhost/login и http://yourhost/aboutus
Если вы не отображали полный URL-адрес с помощью http и использовали относительный URL-адрес ссылки ("/aboutus"), то https сохранится после посещения пользователем защищенного сайта.
Надеюсь, это поможет!
В IndexController.php положить
public function getIndex(Request $request)
{
if ($request->server('HTTP_X_FORWARDED_PROTO') == 'http') {
return redirect('/');
}
return view('index');
}
В AppServiceProvider.php положить
public function boot()
{
\URL::forceSchema('https');
}
В AppServiceProvider.php каждый редирект будет переходить на url https, и для http-запроса нам нужно один раз перенаправить , поэтому в IndexController.php Просто нам нужно сделать один раз перенаправление
Как насчет простого использования файла .htaccess для перенаправления https? Это должно быть помещено в корневой каталог проекта (не в общую папку). Ваш сервер должен быть настроен так, чтобы указывать на корневой каталог проекта.
<IfModule mod_rewrite.c>
RewriteEngine On
# Force SSL
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Remove public folder form URL
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>
Я использую это для laravel 5.4 (последняя версия на момент написания этого ответа), но он должен продолжать работать для версий функций, даже если laravel изменит или удалит некоторые функции.
Приведенные выше ответы не сработали для меня, но, похоже, Дениз Туран переписал .htaccess таким образом, чтобы он работал с балансировщиком нагрузки Heroku здесь: https://www.jcore.com/2017/01/29/force-https-on-heroku-using-htaccess/
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Вот как это сделать на Heroku
Чтобы принудительно использовать SSL на вашем компьютере, но не локально, добавьте в конец вашего.htaccess общедоступный/:
# Force https on heroku...
# Important fact: X-forwarded-Proto will exist at your heroku dyno but wont locally.
# Hence we want: "if x-forwarded exists && if its not https, then rewrite it":
RewriteCond %{HTTP:X-Forwarded-Proto} .
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Вы можете проверить это на своем локальном компьютере с помощью:
curl -H"X-Forwarded-Proto: http" http://your-local-sitename-here
, который устанавливает заголовок X-перенаправленный в форму, которую он примет на heroku.
Т.е. он имитирует, как дино героку увидит запрос.
Вы получите этот ответ на своем локальном компьютере:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://tm3.localhost:8080/">here</a>.</p>
</body></html>
Это перенаправление. Это то, что собирается сделать хероку верните клиенту, если вы установили.htaccess, как указано выше. Но это не происходит на вашем локальном компьютере, потому что X-перенаправление не будет установлено (мы подделали его с помощью curl выше, чтобы посмотреть, что происходит).
Для Laravel 5.6 мне пришлось немного изменить условие, чтобы оно заработало.
От:
if (!$request->secure() && env('APP_ENV') === 'prod') {
return redirect()->secure($request->getRequestUri());
}
Кому:
if (empty($_SERVER['HTTPS']) && env('APP_ENV') === 'prod') {
return redirect()->secure($request->getRequestUri());
}
Это сработало для меня. Я сделал пользовательский php-код, чтобы принудительно перенаправить его на https. Просто включите этот код в header.php
<?php
if (isset($_SERVER['HTTPS']) &&
($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$protocol = 'https://';
}
else {
$protocol = 'http://';
}
$notssl = 'http://';
if($protocol==$notssl){
$url = "https://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";?>
<script>
window.location.href ='<?php echo $url?>';
</script>
<?php } ?>
Если вы используете CloudFlare, вы можете просто создать правило страницы, чтобы всегда использовать HTTPS: Это перенаправит каждый http://запрос на https://
В дополнение к этому вам также придется добавить что-то подобное в свой \app\Providers\AppServiceProvider.php функция загрузки():
if (env('APP_ENV') === 'production' || env('APP_ENV') === 'dev') {
\URL::forceScheme('https');
}
Это гарантирует, что каждая ссылка/путь в вашем приложении использует https:// вместо http://.
Я использую в Laravel 5.6.28 следующее промежуточное программное обеспечение:
namespace App\Http\Middleware;
use App\Models\Unit;
use Closure;
use Illuminate\Http\Request;
class HttpsProtocol
{
public function handle($request, Closure $next)
{
$request->setTrustedProxies([$request->getClientIp()], Request::HEADER_X_FORWARDED_ALL);
if (!$request->secure() && env('APP_ENV') === 'prod') {
return redirect()->secure($request->getRequestUri());
}
return $next($request);
}
}
Я добавляю эту альтернативу, так как я много страдал из-за этой проблемы. Я пробовал все разные способы, но ничего не получалось. Итак, я придумал обходной путь для этого. Возможно, это не лучшее решение, но оно действительно работает -
К вашему сведению, я использую Laravel 5.6
if (App::environment('production')) {
URL::forceScheme('https');
}
Производство