20190503のPHPに関する記事は9件です。

PHPアカウント登録機能メモ

PHPを学び始めたので、分かったことをメモしていく。

strlen()を知った時に
"〇文字以上〇文字以下でアカウント名(パスワード)を登録する"
という機能を作れるのではないかと思いコードを書いてみた。

$name = 123456789;   //使用可能です。

if (strlen($name)<=12 && strlen($name)>=4)
{
  echo "使用可能です";
}
elseif (strlen($name)<4) {
  echo "4文字以上で名前を設定してください。";
}
else
{
  echo "12文字以下で名前を設定してください。";
} 

Qiita初投稿なので、これからもっとアウトプットしていきたい。
綺麗にコードをかけるようになりたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

metaタグを用いたリダイレクト機能で躓いた件

#概要
HTMLにおける<meta>タグを使用した
リダイレクト機能をテストした際に
指定URLに飛ばずに四苦八苦し記録です。

背景

基礎からのMySQLを進める途中、
まさにその基礎勉強に飽き飽きし、
PHPの学習に飛んだ時の話です。

phpの話に入ったばかりの課題が
表題の件でした。
(最初にやることじゃなくね…?)

開発環境

MacOS Mojave
PHP 7.1.23
Apache 2.4.39
使用ブラウザ Safari

内容

初期に書いたコードは以下の通りでした。
文法は「基礎からのMySQL」に基づいていますが、
ちょっとだけアレンジしています。

test.php
<?php
print "<head>";
print " <meta http-equiv='refresh' content=5; URL='http://www.softbank.jp/'>";
print "</head>";
print "<body>";
print "5秒後にソフトバンクのページへ移動します";
print "</body>";
?>

何とも言えないクソコードですね。
出力結果を見てみましょう。

5秒後にソフトバンクのページへ移動します

↓5秒後…

5秒後にソフトバンクのページへ移動します

ページは変わらず、永遠と5秒ずつ同じページを
更新し続けています。
書籍の説明では、5秒後に
指定したURL先にジャンプするはずなのですが…

対処

ググりにググり、リダイレクト機能について調査
しましたが、ほとんどのサイトが上記の記述で
説明されていました。

ところが、ある一つのサイトだけ
違った記述方法でした。

その記述方法を用いて訂正したコードを
下記に示します。

test.php
<?php
print "<head>";
//シングルクオートの位置が違う
print " <meta http-equiv='refresh' content='5; URL=http://www.softbank.jp/'>";
print "</head>";
print "<body>";
print "5秒後にソフトバンクのページへ移動します";
print "</body>";
?>

訂正前はURLのみをシングルクオートで囲っていましたが

訂正前
<meta http-equiv='refresh' content=5; URL='http://www.softbank.jp/'>

訂正後はcontentで指定する秒数の前からシングルクオートで
囲っています。

訂正後
<meta http-equiv='refresh' content='5; URL=http://www.softbank.jp/'>

結果、出力は以下の通りになりました。

5秒後にソフトバンクのページへ移動します

↓5秒後…

(ソフトバンクホームページ)

成功しましたね。

結局何が原因なの?

現時点で分かっていません。
教えてエロい人。

参考書籍

基礎からのMySQL
参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPのincludeを簡単に非同期化にするLazy Includeコンセプト

PHPで別ファイルのHTML部品を読み込むincludeを、できるだけ簡単にJavaScriptによる非同期読み込みに変えちゃおうという試みです。名付けてLazy Include

php-lazy-include
https://github.com/miyanaga/php-lazy-include

画像の遅延読み込みLazy LoadもちょっとしたHTMLとJavaScriptのトリックですが、それをHTMLの断片にまで拡張したものです。

※ 上記のソースコードは実用的なライブラリではなく、コンセプトの実装例です。ぜひ改良してみてください!

コンセプト

ソースコードはよく見るPHPのincludeです。

index.php
<div>Main File</div>
<?php include('includee.php') ?>

includeされる側のファイルも通常通りHTML断片を出力するPHPですが、先頭と末尾に1行ずつ決まったおまじないが記述されています。

includee.php
<?php if(include('lazyInclude.php')) return ?>
<div>Sub File</div>
<?php endLazyInclude() ?>

結果

上記のソースが実行されると、次のようなHTMLとJavaScriptに展開されます。

includeを行った場所にひとまず目印となるdiv要素を置いておき、DOMContentLoadedイベントでそこに元々includeされるはずだった内容を展開するという流れです。

index.php
<div>Main File</div>
<!-- 非同期にincludee.phpの内容を展開する目印 #the-lazy-include -->
<div class="lazy-include" id="the-lazy-include"></div>
<script>
/* <script src="/includee.php?id=the-lazy-include"> をbody末尾に追加 */
window.addEventListener('DOMContentLoaded', function once() {
  window.removeEventListener('DOMContentLoaded', once);
  var script = document.createElement('script');
  script.setAttribute('src', "\/includee.php?id=the-lazy-include");
  document.body.appendChild(script);
});
</script>

bodyに追加されたscript要素は次のJavaScriptを読み込んで実行します。

includee.php
/* 目印 #the-lazy-include にincludee.phpの本来の内容を展開 */
document.getElementById("abc").innerHTML = "<div>Sub File<\/div>\n";

2行のおまじないを追加すると、JavaScriptによる非同期includeになり、そのおまじないを除去するとPHPによる普通の同期includeに戻るというものです。

何が嬉しいの?

ページの読み込み直後のレンダリングから、Lazy IncludeしたDOMを除外することで表示の高速化を図っています。

PageSpeed Insightsの点数はどのように計算されているか。100点をとるための条件」という記事で、PageSpeed Insightsの点数においてはファーストビューの描画時間が重要であると書きました。

このHTMLデータ自体の軽量化もさることながら、初期のDOMのオブジェクト数を減らすことはPageSpeed Insightsの点数アップにもよい影響が期待できます。

そのような非同期処理が、JavaScriptの記述不要で簡単に実現できます。

ユースケース - ボリューム感のあるフッター

フッターにサイトマップをまるごと掲載するサイトが増えています。サイトによってはクローラー向けの内部リンク目的だったり、デザイン上どっしりボリューム感を出す目的かもしれませんが、ユーザーにとっては無駄の多いコンテンツです。

  • たまに役立つかもしれないがそこまで滅多にスクロールしない。
  • 大規模サイトだとけっこうなHTMLの量とDOMオブジェクトの数がある。
  • ほとんど変更されないのに毎回毎回、HTMLに含まれて送信される。

もしSEO上の意図がないのであれば、Lazy Includeによって少し後でレンダリングすることで次の効果が期待できます。

  • 読み込みとファーストビューの描画が速くなる。
  • JavaScriptのキャッシュでブラウザにキャッシュを持たせることもできる(上記コードではExpiresヘッダは未実装)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Angular から PHP に JSON データを Post する

Angular から PHP に JSON データを Post する時に、PHP 側で $_POST で受け取る処理を検索したら自分の記事がヒットしたけど、お前 POST 処理しているけど何も値送信してねーじゃねーか、となったのでメモ。単純に $_POST では受け取れないのでちょっと一手間必要。

ポイント

  • HttpHeaders の Content-Type を application/x-www-form-urlencoded
  • 送信データをまるっと1つの変数に入れる
  • PHP で受け取った値を json_decode する

ソース

GlobalService(global.service.ts) 作成

Service として作成しなくても良いけど使い勝手良いので作成。ポイントは HttpHeaders 値。

import { Injectable }                   from '@angular/core';
import { HttpClient, HttpHeaders }      from '@angular/common/http';
import { timeout, catchError}           from 'rxjs/operators';
import { Observable, throwError}        from 'rxjs';

const HTTP_OPTIONS = {
  headers: new HttpHeaders({
    'Content-Type':  'application/x-www-form-urlencoded; charset=UTF-8'
  })
};

@Injectable({
  providedIn: 'root'
})


export class GlobalService {

  /**
   * コンストラクタ
   * @param {Router} _router
   */
  constructor(
    private _http: HttpClient
  ) { }

  /**
   * httpポスト処理
   * @param {string} _postUrl - 送信URL
   * @param {any} _trans_data - 送信データ(デフォルト空)
   * @return {Observable<any>} http.post処理のレスポンス
   */
  public http(_postUrl: string, _trans_data: any = ''): Observable<any> {
    let ret: Observable<any>;
    let postUrl: string;
    postUrl = _postUrl;
    ret = this._http.post(postUrl, _trans_data, HTTP_OPTIONS)
    .pipe(
      timeout(5000),
      catchError(this.handleError())
    );
    return ret;
  }

  /**
   * Observable のエラーを返却します
   * @param 無し
   * @return {Observable<any>}
   */
  private handleError(): any {
    return (error: any): Observable<any> => {
      const ret = {
        'status': error.status
        , 'data': error.statusText + '/' + error.url
      };
      return throwError(ret);
    };
  }

}

TopComponent(top.component.ts) 作成

POST 処理を行うページ。ポスト処理は画面ボタンのクリックにて行う想定。ポイントは送信 JSON 値を1つの変数に入れて送るところ。

import { Component, OnInit }      from '@angular/core';
import { GlobalService }          from '../../services/global.service';

@Component({
  selector: 'app-top',
  templateUrl: './top.component.html',
  styleUrls: ['./top.component.scss']
})

export class TopComponent implements OnInit {

  /**
   * コンストラクタ
   * @param {GlobalService} globalService
   */
  constructor(
    public gs: GlobalService
  ) {}

  ngOnInit() {}


  // クリック処理で POST 処理実施
  public clickTest() {
    let temp;
    temp = {
      'test': 'テストデータ'
    };

    const body = 'data=' + JSON.stringify(temp);  // <- 送信 JSON 値を data 変数に入れて送る

    this.gs.http('/test.php', body).subscribe(
      res => {
        console.log('success: ' + JSON.stringify(res));
      },
      error => {
        console.log('error: ' + JSON.stringify(error));
      }
    );
  }

}

PHP 処理

受け取る PHP は以下の感じ。json_decode で連想配列として取り扱える。

<?
$post = json_decode($_POST['data'], true);

$retArray = array(
  'rType'=> 'success'
  , 'message'=> $post['test'] // <- 送信されてきた「テストデータ」という文字列がこれで取得出来る
);

header('Content-type: application/json');
echo json_encode($retArray, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

以上で作業一式。

参考

Post JSON from angular 2 to php

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP初心者向け】継承について解説!

はじめに

  • 継承について本やネットの情報から調べて理解したことをまとめました。
  • もし、書いていることに何か間違いがある場合はご指摘いただけると嬉しいです。

継承とは

  • すでに定義されているクラスを元に、新しく拡張したクラスを作る仕組みのこと。
  • 継承元のクラスを「親クラス(スーパークラス)」。
  • 継承してできる新しいクラスを「子クラス(サブクラス)」と呼びます。

継承で出来ること

  • 親クラス(継承元)で定義したプロパティやメソッドを子クラス(継承してできる新しいクラス)で定義しなくても使うことができます。(親クラスから子クラスに引き継ぐイメージ)

継承で出来ないこと

  • 子クラスで定義したメソッドやプロパティは親クラスから呼び出せない。

継承の書き方

親クラス(スーパークラス)
<?php
class 親クラス名
{

}
?>
子クラス(サブクラス)
<?php
class 子クラス名 extends 親クラス名
{

}
?>

コード例

  • 継承の具体例を簡単な内容を元に紹介します。

親クラス

  • 親クラスを定義します
product.php
<?php

class Product
{
    private $name;

    public function getName(){
        return $this->name; 
    }

    public function setName($name){
        $this->name = $name;
    }
}

?>

子クラス

  • 子クラスを定義します
kitchenProduct.php
<?php

require_once("product.php");

class KitchenProduct extends Product{
    private $price;

    public function getPrice(){
        return $this->price; 
    }

    public function setPrice($price){
        $this->price = $price;
    }
}


$kitchen = new KitchenProduct();

$kitchen->setName("テーブル");

$kitchen->setPrice(3000);

echo $kitchen->getName() . "の金額は" . $kitchen->getPrice() . "円です";

?>
kitchenProduct.php(実行結果)
テーブルの金額は3000円です

解説

  • kitchenProduct.phpには定義されていないgetName()メソッドsetName()メソッドを使うことができました。
  • このように親クラスで定義したプロパティやメソッドを子クラスで定義しなくても使うことができます。

【補足】require_onceとは

  • require_onceを使うと、別のphpファイルを読み込むことができます。
  • 読み込んだファイルで定義されている内容を、記述したファイル内で使うことができます。

require_onceの書き方

require("ファイルパス");
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPの学習方法【GW連投企画】

XAMPPをフル活用しよう。

環境構築は重要です。でもそれ以上にコード自体を書く事の方が重要です。
LinuxにLAMP環境を作り本格的な環境を作るのは必須なのですが、最初は
挫折率を下げるために避けましょう。

フレームワークについて

フレームワークは複合技術なので、いきなり学習すると挫折する可能性が飛躍的に上がってしまいます。
PHPだけでログイン機能付きの掲示板アプリ程度のものを作れるようになってから挑戦しましょう

パーフェクトPHPについて

有名な書籍ですが、入門時には敷居が高いので最初に手をつけるのは避けましょう。
独自でフレームワークを作るところがとても勉強になるのである程度PHPでウェブアプリを
作れるようになってから挑戦しましょう

事前準備について

DBと連動しないJava ScriptでDomを操作するチュートリアルと、
DB中心のチュートリアルを学ぶと挫折率はグッと下がります。

進め方

事前準備

はじめてのJavaScript
詳解JavaScript DOM編
MySQL入門

本番

PHPの絵本 第2版 Webアプリ作りが楽しくなる新しい9つの扉
プログラミング自体が入門の方向け。変数の概念からイラストでわかりやすく説明してくれます。

いきなりはじめるPHP~ワクワク・ドキドキの入門教室~
ちょうど良いボリュームなので繰り返し学習するのに最適です。

独習PHP 第3版
上記本をある程度理解してから本格的な参考書に挑む方が挫折率を抑えれると思います。

PHPフレームワーク Laravel入門
フレームワークは最近採用が増えてきたLaravelが無難でしょう。

PHP逆引きレシピ 第2版
逆引き辞書はある程度わかってから全部通して読んでみると欠けている知識を確認できたりします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

selenium + php-webdriver で フルスクリーンキャプチャをとる

(課題) seleniumでフルスクリーンキャプチャしたい

selenium いろいろ自動化できて素敵なのですが、フルスクリーンで画面キャプチャがなかなかできませんでした。

結論から言うとヘッドレスモードで動かしたらあっさり撮れたんですが、なかなか結論に行きつけずハマったので挙げておきます。 :grinning:

(前提/参考) selenium + php-webdriver を動かすところまで

seleniumを使ってPHPでChromeの自動操作をする - Qiita
https://qiita.com/Rasukarusan/items/0ca204d5b0f0fb876252

PHPUnit + php-webdriver でWebUIのテストを書く - Qiita
https://qiita.com/zaburo/items/f11357170953a3c34b8f

Selenium × PHP でテスト自動化!【環境構築編】 | DACエンジニアブログ:アドテクゑびす界 http://yebisupress.dac.co.jp/2018/07/06/test_automation_with_selenium-x-php/

(先に結論)ヘッドレスモードで動かしたら撮れた

<?php
// PHP 7.1.23
// selenium-server-standalone-3.4.0.jar

require_once './vendor/autoload.php';

use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\WebDriverDimension;

//ヘッドレスモードで起動
$options = new ChromeOptions();
$options->addArguments(['--headless']);
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);
$driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities);

//繰り返しの処理を入れたければここから
$url = "https://www.yahoo.co.jp/";
$imageName = "example";
$driver->get($url);

// 一旦 ウィンドウサイズを任意に指定
// 縦幅は調整してくれるが、
// 横幅はここの値が引き継がれてるみたい?
$dimension = new WebDriverDimension(1920, 1080); // width, height
$driver->manage()->window()->setSize($dimension);

// 実際のコンテンツサイズを取得して調整
$contentWidth = $driver->executeScript("return Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);");
$contentHeight = $driver->executeScript("return Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);");
$dimension_content = new WebDriverDimension($contentWidth, $contentHeight);
$driver->manage()->window()->setSize($dimension_content);

$file = __DIR__ . $imageName . ".png";
$driver->takeScreenshot($file);

$driver->close();

(諦めた方法) スクロールして画像を切り貼りする

先達が紹介してくれている方法に、「(実際の画面サイズ以上のキャプチャが撮れないので)画面を一枚ずつとってスクロールして切り貼りする」というのがあるんですが、複雑なのでなんとかならんのかと思ったところ、

php-webdriverを使ってフルスクリーンのキャプチャを撮る | Shimabox Blog https://blog.shimabox.net/2017/07/31/take_full_screen_capture_with_php-webdriver/

Chromeでフルサイズのスクリーンショットを撮るためのパッチ - Qiita https://qiita.com/myhr/items/dff7cee30182ab56f737

Selenium でページ全体のスクリーンショットを撮る (Python) | Aqua Ware つぶやきブログ https://aquasoftware.net/blog/?p=1090

ヘッドレスモードだったら撮れるらしい?

こちらはpythonだけど「ヘッドレスモード」だったら撮れるとのこと

Python: Selenium + Headless Chrome で Web ページ全体のスクリーンショットを撮る - CUBE SUGAR CONTAINER https://blog.amedama.jp/entry/2018/07/28/003342

ヘッドレスブラウザとは

ヘッドレスブラウザは、GUI を持つ必要のない自動テスト環境やサーバー環境にとてもよいツールです。例としては、実際のウェブページに対してなにかテストを実行する、そのページの PDF を生成する、またはただ、そのページがどう表示されるかを検証するなどが挙げられるでしょうか。 https://developers.google.com/web/updates/2017/04/headless-chrome?hl=ja

php-webdriverでのヘッドレスモードの記載はこちらを参考に

ヘッドレスChromeとヘッドレスFirefoxをphp-webdriverで試す | Shimabox Blog https://blog.shimabox.net/2017/11/15/try_headless_chrome_and_firefox_with_php-webdriver/


……でヘッドレスモード動かしたらあっさり行けました(バージョン的な問題だったのかはわかっていません :bow: )。

いまのところの注意点としては、繰り返しキャプチャを行う際に一旦画面の縦幅をリセットしないと異様に縦長の画像が撮れてしまったりします。

とりあえずこちらからは以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MAMP上でsimpleSAMLphpのSPとIdPを構築する

はじめに

MAMPとは何か、等については次節のリンクで説明しているので割愛します。simpleSAMLphpは簡単に言うとシングルサインオンを実装するためのオープンソースソフトウェアです。ネットに転がっているサイトではだいたいVMを使って実装されておりMAMPを使った実装は見かけたことがなかったこと、およびSPとIdP両方についてひとつのサイトで説明しているものが少ないことから記事にしてみました。

わざわざ当サイトを見に来るような人はおそらくsimpleSAMLphpやSP・IdPといった知識は十二分に持っていらっしゃると思ったので説明ははしょっています。

環境
OS:macOS Mojave(バージョン10.14.1)
MAMP:Version 5.3(367)
PHP:Version 7.3.1
SimpleSAMLphp:Version 1.17.2

※MAMPのApacheのバージョンはどこから確認できるかわかりませんでした…。わかりましたら追記いたします。

MAMPのWebサーバー構築

下記記事参照
https://qiita.com/come340/items/6cb175918075f398a41a

simpleSAMLphpのインストール

https://simplesamlphp.org/download
から最新のものをダウンロード
VMならwgetコマンドを使うといいけれど今回はMAMP(すなわちMacOS上)でやるので普通にダウンロードします。

ダウンロードしたtarファイル(simplesamlphp-X.X.X.tar.gz)は/Applications/MAMP/htdocsに置いて展開します。

筆者は複数のSPおよびIdPを構築したかったのでとりあえず展開したファイルは置いておいてSP・IdPごとにコピーしました。

simpleSAMLphpの設定

ここはSP・IdPともに同様の設定を行うので、とりあえずSPについて説明します。
先に展開したsimplesSAMLphpのファイルをコピーしてsaml_sp1と名前をつけました。(これは何でもいいです。)

複数のローカルホストをたてられるように設定変更

/Applications/MAMP/apache/httpd.confを編集

httpd.conf
# Virtual hosts
#Include /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf

からコメントアウトを削除。

httpd.conf
# Virtual hosts
Include /Applications/MAMP/conf/apache/extra/httpd-vhosts.conf

ローカルホストの設定

/Applications/MAMP/conf/apache/extra/httpd-vhost.confを編集
最終行の後に追加して以下を記述

httpd-vhost.conf
# SimpleSAMLphp SP1
Listen 8001
<VirtualHost *:8001>
    SetEnv SIMPLESAMLPHP_CONFIG_DIR /Applications/MAMP/htdocs/saml_sp1/config

    DocumentRoot "/Applications/MAMP/htdocs/saml_sp1/www"
    ServerName saml_sp1.local:8001
    Alias /simplesaml "/Applications/MAMP/htdocs/saml_sp1/www"

    <Directory "/Applications/MAMP/htdocs/saml_sp1/www">
            <IfModule mod_authz_core.c>
            # For Apache 2.4:
            Require all granted
            AllowOverride All
            </IfModule>
    </Directory>
</VirtualHost>

8001はポート番号、設定したいローカルホストごとに異なる番号を振り分けるとよいです。
simpleSAMLphpの設定について調べているといろんなサイトで似たような設定の仕方が記述されていますが、コピペしないでちゃんと自分のホストとの整合性を確認したほうがいいと思います。チェックすべきポイントは

  • SetEnv
  • DocumentRoot
  • Alias
  • <Directory "">

あたりだと思います。Aliasは詳しくは説明しませんが/simplesamlの所だけは変えてしまうとうまくいかないのでそのままにしましょう。

ここまで設定できたらMAMPのStop ServersおよびStart Serversを押してサーバーを再起動のち、ブラウザに"localhost:8001"を入力してみたら以下のようなページが表示されるはずです。

スクリーンショット 2019-05-02 15.34.28.png

adminユーザパスワード設定

これは下記記事を参考に
https://qiita.com/haya43/items/c74d2710cd9b57d2cbb4

/Applications/MAMP/htdocs/saml_sp1/config/config.phpを編集

config.php
'auth.adminpassword' => 'test',

シークレットソルトを設定。下記コマンドでランダム文字列を生成。

tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo

VMで動かしたときはこれでよかったのですが、Macのターミナルではtr: Illegal byte sequenceというエラーを吐いてうまくいかなかったので調べてみたところ、以下コマンドでうまくいきました。

LC_CTYPE=C tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo

生成した文字列をコピーしてconfig.phpをさらに編集

config.php
'secretsalt' => '(生成した文字列)',

再びlocalhost:8001にブラウザからアクセスして管理者でログインできるか確認しましょう。

IdPの設定

筆者はIdPのポート番号は8011にしています。上記の設定で利用していたファイル名saml_sp1に当たるものはsaml_idp1としています。ブラウザから見るときはlocalhost:8011でアクセスします。

saml20-idpを有効化する

config.php
'enable.saml20-idp' => true,

SimpleSAMLphp設定ページの設定タブでSAML 2.0 IdPに緑のチェックマークがついているか確認しましょう。

自己証明書を作る

cdコマンドで/Applications/MAMP/htdocs/saml_idp1/cert配下に行きます。
以下コマンドで証明書を作成。

openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes -out server.crt -keyout server.pem

いろいろ聞かれますが、以下のように答えます。割と適当なので問題あればご指摘ください。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:(空白)
Organization Name (eg, company) [Internet Widgits Pty Ltd]:(空白)
Organizational Unit Name (eg, section) []:(空白)
Common Name (e.g. server FQDN or YOUR name) []:saml_idp1.local
Email Address []:(空白)

これで/cert配下にserver.crtとserver.pemが作成されます。一応lsコマンドで確認するといいです。デフォルトの設定でserver.crtとserver.pemというものが書かれているはずなのでその他のファイルは編集せずこのまま使えます。
もし異なる名称で作成した場合、metadata/saml20-idp-hosted.phpを編集します。

saml20-idp-hosted.php
    'privatekey' => '(設定した名称).pem',
    'certificate' => '(設定した名称).crt',

SPの設定

SPでもサーバー証明書を作成

/Applications/MAMP/htdocs/saml_sp1/certに移動して以下コマンド実行。あんまり意味があるかわかりませんが出力ファイルの名称をsaml_sp1.local.crt、saml_sp1.local.pemのように変えています。

openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes -out saml_sp1.local.crt -keyout saml_sp1.local.pem

IdPの設定時と同様に質問に答えていきます。Common Nameだけはsaml_sp1.localに変えておきます。

/Applications/MAMP/htdocs/saml_sp1/config/authsources.phpを編集

authsources.php
 'default-sp' => [
        'saml:SP',

        'privatekey' => 'saml_sp1.local.pem', # 追記
        'certificate' => 'saml_sp1.local.crt', # 追記

        // The entity ID of this SP.
        // Can be NULL/unset, in which case an entity ID is generated based on the metadata URL.
        'entityID' => 'http://localhost:8001', # 変更

        // The entity ID of the IdP this SP should contact.
        // Can be NULL/unset, in which case the user will be shown a list of available IdPs.
        'idp' => 'http://localhost:8011/simplesaml/saml2/idp/metadata.php', # 変更

        // The URL to the discovery service.
        // Can be NULL/unset, in which case a builtin discovery service will be used.

(以下省略)

entityIDは設定しているSPへのアクセスに利用しているURLに当たるもの(?)を、idpはIdPのエンティティIDを入力します。エンティティIDはIdP側のSimpleSAMLの設定ページの連携タブにある、SAML2.0 IdPメタデータの欄にあるEntity IDを入力しています。privatekeyとcertificateの欄はデフォルトでは書いていないので自分で追記します。

IdP・SPのメタデータ登録

IdPのメタデータをSPに登録

IdPの設定ページで'連携'タブを開き、SAML 2.0 IdPメタデータの欄から'メタデータを表示'を押してもらうと以下のような画面が表示されます。
スクリーンショット 2019-05-03 13.02.20.png
このメタデータXMLフォーマットをコピーして、今度はSPの設定ページに行きます。(IdPのページでもできる気がしますがなんとなくその方が良さそうなのでSP側でやります。)
'連携'タブを開いてツールの'XMLをSimpleSAMLphpメタデータに変換'をクリック。メタデータパーザの入力枠内に先程コピーしたXMLデータを入力し'パース'ボタンを押します。するとXMLフォーマットをSimpleSAMLphpのメタデータフォーマットに変換してくれるので再びコピー。

次に、/saml_sp1/metadata/saml20-idp-remote.phpを編集。先程コピーしたものをそのままペーストすれば良いです。

saml20-idp-remote.php
<?php
/**
 * SAML 2.0 remote IdP metadata for SimpleSAMLphp.
 *
 * Remember to remove the IdPs you don't use from this file.
 *
 * See: https://simplesamlphp.org/docs/stable/simplesamlphp-reference-idp-remote
 */

$metadata['http://localhost:8011/simplesaml/saml2/idp/metadata.php'] = array (
  'entityid' => 'http://localhost:8011/simplesaml/saml2/idp/metadata.php',
  'contacts' =>
  array (
  ),
  'metadata-set' => 'saml20-idp-remote',
  'SingleSignOnService' =>
  array (
    0 =>
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'http://localhost:8011/simplesaml/saml2/idp/SSOService.php',
    ),
  ),
(以下省略)

'<?php'のPHPタグをつけるのを忘れずに。(筆者は忘れていてエラーを吐かれました)

SPのメタデータをIdPに設定

SPへの設定と同様に、SPのXMLメタデータを変換しコピーします。こちらでは変換されたメタデータが'shib13-sp-remote'と'saml20-sp-remote'の二種類出てくるのでここでは後者を使用します。
/saml_idp1/metadata/saml20-sp-remote.phpを編集します。こちらも先程と同様にコピーしたものをそのままペーストします。PHPタグも忘れずに。

saml20-sp-remote.php
<?php
$metadata['http://localhost:8001'] = array (
  'entityid' => 'http://localhost:8001',
  'contacts' =>
  array (
  ),
  'metadata-set' => 'saml20-sp-remote',
  'AssertionConsumerService' =>
  array (
    0 =>
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'http://localhost:8001/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
      'index' => 0,
    ),
    1 =>
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post',
      'Location' => 'http://localhost:8001/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp',
      'index' => 1,
    ),
(以下省略)

IdPの設定ではさらに、認証モジュールの設定をします。詳しくは公式文書の3番を読んでください。
https://simplesamlphp.org/docs/stable/simplesamlphp-idp
今回はこの例にならってexampleauthモジュールを使用します。

・モジュールの有効化

cd /Applications/MAMP/htdocs/saml_idp1/
touch modules/exampleauth/enable

・/saml_idp1/config/authsources.phpを編集
以下のユニット前後のコメントアウト(/*, */)を削除

authsources.php
'example-userpass' => [
        'exampleauth:UserPass',

        // Give the user an option to save their username for future login attempts
        // And when enabled, what should the default be, to save the username or not
        //'remember.username.enabled' => false,
        //'remember.username.checked' => false,

        'student:studentpass' => [
            'uid' => ['test'],
            'eduPersonAffiliation' => ['member', 'student'],
        ],
        'employee:employeepass' => [
            'uid' => ['employee'],
            'eduPersonAffiliation' => ['member', 'employee'],
        ],
    ],

これで、IdPにユーザが登録されます。studentユーザはユーザID'test'、パスワード'studentpass'で、employeeユーザはユーザID'employee'、パスワード'employeepass'でログインできるようになります。

接続テスト

SP側のテスト

SP側の設定ページで'認証'タブを開いて'設定されている認証元をテスト'をクリック。default-spを選択してユーザ名、パスワードを入力。うまくいけば以下のような画面が表示されます。
スクリーンショット 2019-05-03 13.31.43.png

IdP側のテスト

SP同様に'設定されている認証元をテスト'をクリック。default-spまたはexample-userpassを選択するとSP側同様の画面が表示されます。

終わりに

書きたいことを全部まとめて書いてしまったため長くなってしまいましたが、とりあえずここまででなんとなくSPとIdPを動かせるようになると思います。筆者自身MAMPもシングルサインオンもド初心者ですのでなにか問題がありましたらご指摘いただけると幸いです。実際の運用についても今後追記できたらいいなと思っております。

本記事の投稿に当たって以下のサイトを参考にさせていただきました。
https://suer.hatenablog.com/entry/2015/11/20/141941

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

var-dumperのスタイルシートを上書きする関数を作ってみた

Laravelのコードリーディングをしている時に、オブジェクト($app)などの中身を調べるのにvar-dumperdump()を使っていました。
かなり便利な関数なのですが、CSSが見づらいです。
以下のコードでdump()を使っていると・・・

resources/views/test.blade.php
$route_collection = (array)(((array)$app)["\0*\0instances"]["routes"]);
dump($route_collection);

このような出力になります。

dumper.JPG

#000000の背景に#ffffffの文字や、<pre>の外の<body>の背景色(#ffffff)で目が痛くなってきます。
なので目にやさしい<style>を作ることにしました。

app/helpers.php
<?php
if (! function_exists('dump_css')) {
    /**
     * style sheet for var-dumper
     *
     * @return void
     */
    function dump_css()
    {
        $css = "<style>
            pre.sf-dump, pre.sf-dump .sf-dump-default { 
            font: 20px Arial, Helvetica, sans-serif;
            background-color: #dddddd;
            color: #4c4c4c;
        }
        pre.sf-dump .sf-dump-note {
            color: #012c42;
            font-weight: bold;
        }
        pre.sf-dump .sf-dump-key {
            color: #2d771d;
            font-size: 22px;
        }
        pre.sf-dump .sf-dump-public {
            color: #777777;
            font-size: 22px;
        }
        pre.sf-dump .sf-dump-protected {
            color: #3a3a3a;
        }
        pre.sf-dump .sf-dump-str {
            color: #2d6021;
        }
        pre.sf-dump .sf-dump-ellipsis {
            color: #ad5b03;
        }
        pre.sf-dump .sf-dump-meta {
            color: #736377;
        }
        </style>";

        echo $css;
    }
}

するとこうなります。

dump-after.JPG

かなり見やすくなりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む