source: branches/version-2_13-dev/data/class/sessionfactory/SC_SessionFactory_UseRequest.php @ 23256

Revision 23256, 15.7 KB checked in by Seasoft, 8 years ago (diff)

#2427 (SC_SessionFactory_UseRequest#initSession URLパスでリダイレクトしている)
#2445 (TOPページのURLに揺らぎがある)
#2446 (HTTP へ復帰すべき画面遷移でも HTTPS が維持される)

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2/*
3 * This file is part of EC-CUBE
4 *
5 * Copyright(c) 2000-2013 LOCKON CO.,LTD. All Rights Reserved.
6 *
7 * http://www.lockon.co.jp/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 */
23
24/**
25 * Cookieを使用せず、リクエストパラメーターによりセッションを継続する設定を行うクラス.
26 *
27 * このクラスを直接インスタンス化しないこと.
28 * 必ず SC_SessionFactory クラスを経由してインスタンス化する.
29 * また, SC_SessionFactory クラスの関数を必ずオーバーライドしている必要がある.
30 *
31 * @package SC_SessionFactory
32 * @author LOCKON CO.,LTD.
33 * @version $Id$
34 */
35class SC_SessionFactory_UseRequest extends SC_SessionFactory_Ex
36{
37    public $state = null;
38
39    /**
40     * PC/モバイルのセッション管理オブジェクトを切り替える
41     *
42     * @param string $state
43     */
44    public function setState($state = 'pc')
45    {
46        switch ($state) {
47            case 'mobile':
48                $this->state = new LC_UseRequest_State_Mobile;
49                break;
50
51            case 'pc':
52            default:
53                $this->state = new LC_UseRequest_State_PC;
54                break;
55        }
56    }
57
58    /**
59     * Cookieを使用するかどうか
60     *
61     * @return boolean 常にfalseを返す
62     */
63    public function useCookie()
64    {
65        return false;
66    }
67
68    /**
69     * dtb_mobile_ext_session_id テーブルを検索してセッションIDを取得する。
70     * PCサイトでもモバイルサイトでもこのテーブルを利用する.
71     *
72     * @return string|null 取得したセッションIDを返す。
73     *                     取得できなかった場合は null を返す。
74     */
75    public function getExtSessionId()
76    {
77        if (!preg_match('|^' . ROOT_URLPATH . '(.*)$|', $_SERVER['SCRIPT_NAME'], $matches)) {
78            return null;
79        }
80
81        $url = $matches[1];
82        $lifetime = $this->state->getLifeTime();
83        $time = date('Y-m-d H:i:s', time() - $lifetime);
84        $objQuery =& SC_Query_Ex::getSingletonInstance();
85
86        foreach ($_REQUEST as $key => $value) {
87            $session_id = $objQuery->get('session_id', 'dtb_mobile_ext_session_id',
88                                         'param_key = ? AND param_value = ? AND url = ? AND create_date >= ?',
89                                         array($key, $value, $url, $time));
90            if (isset($session_id)) {
91                return $session_id;
92            }
93        }
94
95        return null;
96    }
97
98    /**
99     * 外部サイト連携用にセッションIDとパラメーターの組み合わせを保存する。
100     *
101     * @param  string $param_key   パラメーター名
102     * @param  string $param_value パラメーター値
103     * @param  string $url         URL
104     * @return void
105     */
106    public function setExtSessionId($param_key, $param_value, $url)
107    {
108        $objQuery =& SC_Query_Ex::getSingletonInstance();
109
110        // GC
111        $lifetime = $this->state->getLifeTime();
112        $time = date('Y-m-d H:i:s', time() - $lifetime);
113        $objQuery->delete('dtb_mobile_ext_session_id', 'create_date < ?', array($time));
114
115        $arrValues = array(
116            'session_id'  => session_id(),
117            'param_key'   => $param_key,
118            'param_value' => $param_value,
119            'url'         => $url,
120        );
121
122        $objQuery->insert('dtb_mobile_ext_session_id', $arrValues);
123    }
124
125    /**
126     * セッションデータが有効かどうかをチェックする。
127     *
128     * @return boolean セッションデータが有効な場合は true、無効な場合は false を返す。
129     */
130    public function validateSession()
131    {
132        /**
133         * PCサイトでは
134         *  ・セッションデータが適切に設定されているか
135         *  ・UserAgent
136         *  ・IPアドレス
137         *  ・有効期限
138         * モバイルサイトでは
139         *  ・セッションデータが適切に設定されているか
140         *  ・機種名
141         *  ・IPアドレス
142         *  ・有効期限
143         *  ・phone_id
144         * がチェックされる
145         */
146
147        return $this->state->validateSessionData();
148    }
149
150    /**
151     * パラメーターから有効なセッションIDを取得する。
152     *
153     * @return string|false 取得した有効なセッションIDを返す。
154     *                      取得できなかった場合は false を返す。
155     */
156    public function getSessionId()
157    {
158        // パラメーターからセッションIDを取得する。
159        $sessionId = @$_POST[session_name()];
160        if (!isset($sessionId)) {
161            $sessionId = @$_GET[session_name()];
162            // AU動画音声ファイルダウンロード対策
163            // キャリアがAUで、動画、音声ファイルをダウンロードする際に
164            // SESSIONIDの後に余計なパラメータが付与され、セッションが無効になるケースがある
165            if (SC_MobileUserAgent::getCarrier() == 'ezweb') {
166                $idArray = split("\?", $sessionId);
167                $sessionId = $idArray[0];
168            }
169        }
170        if (!isset($sessionId)) {
171            $sessionId = $this->getExtSessionId();
172        }
173        if (!isset($sessionId)) {
174            return false;
175        }
176
177        // セッションIDの存在をチェックする。
178        $objSession = new SC_Helper_Session_Ex();
179        if ($objSession->sfSessRead($sessionId) === null) {
180            GC_Utils_Ex::gfPrintLog("Non-existent session id : sid=$sessionId");
181
182            return false;
183        }
184
185        return session_id($sessionId);
186    }
187
188    /**
189     * セッション初期処理を行う。
190     *
191     * @return void
192     */
193    public function initSession()
194    {
195        // セッションIDの受け渡しにクッキーを使用しない。
196        ini_set('session.use_cookies', '0');
197        ini_set('session.use_trans_sid', '1');
198        ini_set('session.use_only_cookies', '0');
199
200        // パラメーターから有効なセッションIDを取得する。
201        $sessionId = $this->getSessionId();
202
203        if (!$sessionId) {
204            session_start();
205        }
206
207        /*
208         * PHP4 では session.use_trans_sid が PHP_INI_PREDIR なので
209         * ini_set() で設定できない
210         */
211        if (!ini_get('session.use_trans_sid')) {
212            output_add_rewrite_var(session_name(), session_id());
213        }
214
215        // セッションIDまたはセッションデータが無効な場合は、セッションIDを再生成
216        // し、セッションデータを初期化する。
217        if ($sessionId === false || !$this->validateSession()) {
218            session_regenerate_id(true);
219            // セッションデータの初期化
220            $this->state->inisializeSessionData();
221
222            // 新しいセッションIDを付加してリダイレクトする。
223            if ($_SERVER['REQUEST_METHOD'] == 'GET') {
224                // GET の場合は同じページにリダイレクトする。
225                $objMobile = new SC_Helper_Mobile_Ex;
226                header('Location: ' . $objMobile->gfAddSessionId());
227            } else {
228                // GET 以外の場合はトップページへリダイレクトする。
229                header('Location: ' . TOP_URL . '?' . SID);
230            }
231            exit;
232        }
233
234        // 有効期限を更新する.
235        $this->state->updateExpire();
236    }
237}
238/**
239 * セッションデータ管理クラスの基底クラス
240 *
241 */
242class LC_UseRequest_State
243{
244    /** 名前空間(pc/mobile) */
245    public $namespace = '';
246    /** 有効期間 */
247    public $lifetime  = 0;
248    /** エラーチェック関数名の配列 */
249    public $validate  = array();
250
251    /**
252     * 名前空間を取得する
253     *
254     * @return string
255     */
256    public function getNameSpace()
257    { return $this->namespace; }
258
259    /**
260     * 有効期間を取得する
261     *
262     * @return integer
263     */
264    public function getLifeTime()
265    { return $this->lifetime; }
266
267    /**
268     * セッションデータが設定されているかを判定する.
269     * $_SESSION[$namespace]の値が配列の場合に
270     * trueを返す.
271     *
272     * @return boolean
273     */
274    public function validateNameSpace()
275    {
276        $namespace = $this->getNameSpace();
277        if (isset($_SESSION[$namespace]) && is_array($_SESSION[$namespace])) {
278            return true;
279        }
280        GC_Utils_Ex::gfPrintLog("NameSpace $namespace not found in session data : sid=" . session_id());
281
282        return false;
283    }
284
285    /**
286     * セッションのデータを取得する
287     * 取得するデータは$_SESSION[$namespace][$key]となる.
288     *
289     * @param  string     $key
290     * @return mixed|null
291     */
292    public function getValue($key)
293    {
294        $namespace = $this->getNameSpace();
295
296        return isset($_SESSION[$namespace][$key])
297            ? $_SESSION[$namespace][$key]
298            : null;
299    }
300
301    /**
302     * セッションにデータを登録する.
303     * $_SESSION[$namespace][$key] = $valueの形で登録される.
304     *
305     * @param string $key
306     * @param mixed  $value
307     */
308    public function setValue($key, $value)
309    {
310        $namespace = $this->getNameSpace();
311        $_SESSION[$namespace][$key] = $value;
312    }
313
314    /**
315     * 有効期限を取得する.
316     *
317     * @return integer
318     */
319    public function getExpire()
320    {
321        return $this->getValue('expires');
322    }
323
324    /**
325     * 有効期限を設定する.
326     *
327     */
328    public function updateExpire()
329    {
330        $lifetime = $this->getLifeTime();
331        $this->setValue('expires', time() + $lifetime);
332    }
333
334    /**
335     * 有効期限内かどうかを判定する.
336     *
337     * @return boolean
338     */
339    public function validateExpire()
340    {
341        $expire = $this->getExpire();
342        if (intval($expire) > time()) {
343            return true;
344        }
345        $date = date('Y/m/d H:i:s', $expire);
346        GC_Utils_Ex::gfPrintLog("Session expired at $date : sid=" . session_id());
347
348        return false;
349    }
350
351    /**
352     * IPアドレスを取得する.
353     *
354     * @return string
355     */
356    public function getIp()
357    {
358        return $this->getValue('ip');
359    }
360
361    /**
362     * IPアドレスを設定する.
363     *
364     */
365    public function updateIp()
366    {
367        $this->setValue('ip', $_SERVER['REMOTE_ADDR']);
368    }
369
370    /**
371     * REMOTE_ADDRとセッション中のIPが同じかどうかを判定する.
372     * 同じ場合にtrueが返る
373     *
374     * @return boolean
375     */
376    public function validateIp()
377    {
378        $ip = $this->getIp();
379        if (!empty($_SERVER['REMOTE_ADDR']) && $ip === $_SERVER['REMOTE_ADDR']) {
380            return true;
381        }
382
383        $msg = sprintf('Ip Addr mismatch : %s != %s(expected) : sid=%s', $_SERVER['REMOTE_ADDR'], $ip, session_id());
384        GC_Utils_Ex::gfPrintLog($msg);
385
386        return false;
387    }
388
389    /**
390     * UserAgentもしくは携帯の機種名を取得する.
391     *
392     * @return string
393     */
394    public function getModel()
395    {
396        return $this->getValue('model');
397    }
398
399    /**
400     * セッション中のデータ検証する
401     *
402     * @return boolean
403     */
404    public function validateSessionData()
405    {
406        foreach ($this->validate as $method) {
407            $method = 'validate' . $method;
408            if (!$this->$method()) {
409                return false;
410            }
411        }
412
413        return true;
414    }
415
416    /**
417     * セッションデータを初期化する.
418     *
419     */
420    public function inisializeSessionData()
421    {
422    }
423}
424
425/**
426 * PCサイト用のセッションデータ管理クラス
427 *
428 */
429class LC_UseRequest_State_PC extends LC_UseRequest_State
430{
431    /**
432     * コンストラクタ
433     * セッションのデータ構造は下のようになる.
434     * $_SESSION['pc']=> array(
435     *     ['model']   => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)'
436     *     ['ip']      => '127.0.0.1'
437     *     ['expires'] => 1204699031
438     * )
439     *
440     * @return LC_UseRequest_State_PC
441     */
442    public function LC_UseRequest_State_PC()
443    {
444        $this->namespace = 'pc';
445        $this->lifetime  = SESSION_LIFETIME;
446        $this->validate  = array('NameSpace', 'Model', 'Ip', 'Expire');
447    }
448
449    /**
450     * セッションにUserAgentを設定する.
451     *
452     */
453    public function updateModel()
454    {
455        $this->setValue('model', $_SERVER['HTTP_USER_AGENT']);
456    }
457
458    /**
459     * UserAgentを検証する.
460     *
461     * @return boolean
462     */
463    public function validateModel()
464    {
465        $ua = $this->getModel();
466        if (!empty($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === $ua) {
467            return true;
468        }
469        $msg = sprintf('User agent model mismatch : %s != %s(expected), sid=%s',
470                       $_SERVER['HTTP_USER_AGENT'], $ua, session_id());
471        GC_Utils_Ex::gfPrintLog($msg);
472
473        return false;
474    }
475
476    /**
477     * セッションデータを初期化する.
478     *
479     */
480    public function inisializeSessionData()
481    {
482        $_SESSION = array();
483        $this->updateModel();
484        $this->updateIp();
485        $this->updateExpire();
486    }
487}
488
489/**
490 * モバイルサイト用のセッションデータ管理クラス
491 *
492 */
493class LC_UseRequest_State_Mobile extends LC_UseRequest_State
494{
495    /**
496     * コンストラクタ
497     * セッションのデータ構造は下のようになる.
498     * $_SESSION['mobile']=> array(
499     *     ['model']   => 901sh
500     *     ['ip']      => 127.0.0.1
501     *     ['expires'] => 1204699031
502     *     ['phone_id']=> ****
503     * )
504     *
505     * @return LC_UseRequest_State_Mobile
506     */
507    public function LC_UseRequest_State_Mobile()
508    {
509        $this->namespace = 'mobile';
510        $this->lifetime  = MOBILE_SESSION_LIFETIME;
511        $this->validate  = array('NameSpace', 'Model', 'Expire');
512    }
513
514    /**
515     * 携帯の機種名を設定する
516     *
517     */
518    public function updateModel()
519    {
520        $this->setValue('model', SC_MobileUserAgent_Ex::getModel());
521    }
522
523    /**
524     * セッション中の携帯機種名と、アクセスしてきたブラウザの機種名が同じかどうかを判定する
525     *
526     * @return boolean
527     */
528    public function validateModel()
529    {
530        $modelInSession = $this->getModel();
531        $model = SC_MobileUserAgent_Ex::getModel();
532        if (!empty($model) && $model === $modelInSession) {
533            return true;
534        }
535
536        return false;
537    }
538
539    /**
540     * 携帯のIDを取得する
541     *
542     * @return string
543     */
544    public function getPhoneId()
545    {
546        return $this->getValue('phone_id');
547    }
548
549    /**
550     * 携帯のIDを登録する.
551     *
552     */
553    public function updatePhoneId()
554    {
555        $this->setValue('phone_id', SC_MobileUserAgent_Ex::getId());
556    }
557
558    /**
559     * セッションデータを初期化する.
560     *
561     */
562    public function inisializeSessionData()
563    {
564        $_SESSION = array();
565        $this->updateModel();
566        $this->updateIp();
567        $this->updateExpire();
568        $this->updatePhoneId();
569    }
570}
571/*
572 * Local variables:
573 * coding: utf-8
574 * End:
575 */
Note: See TracBrowser for help on using the repository browser.