Microsoft サポートオンライン 「Office 文書内のハイパーリンクを開くと Cookie が紛失する」
http://support.microsoft.com/kb/811929/ja
http://support.microsoft.com/kb/899927/ja
という問題であるが、説明がMSらしく意味不明な上、MSは問題だと認識しているくせに修正しようと思っていないらしい。
仕方ないので、Webアプリケーション側で何とかしろと言われるわけで、まったく迷惑な話である。
ログイン認証のあるWebアプリケーションで、かつ、ログイン画面ではなく、アプリケーションの特定のURLに直接飛べるURLを用意するような場合、例えば、メールに記載されているURLをクリックすると、ログイン画面でIDとパスワードを入力したら、そのURLで示されている個人ページへ自動でジャンプさせるような場合であるが、一旦ログイン画面を経由させなければならないので、もともとアクセスしてきたURLをCookieへ入れておいて、ログイン画面で認証後、CookieからそのURLを取り出して、ジャンプするという仕組みになっていることが多い。Cookieじゃなくてセッションに入れている場合もあるが、そのセッションを示すIDがCookieに入っているから同じ事だ。
ジャンプと書いたが、正式には、HTTP のリダイレクションという仕組みを使うのだが、これは相手が最初からブラウザであれば何の問題もなく上手くいく仕組みであるが、問題は、Hyperlink 。どうも Office のハイパーリングは、リダイレクションの最終着陸地がHTMLではなくファイルだった場合にブラウザではなく、そのファイルに最適?なアプリへ渡したいと思っているらしく、リダイレクションをOfficeアプリ内ですべて処理してしまう仕組みになっているようだ。そうして、最終着陸地がHTMLだった場合には、その最後のリダイレクション先のURLをブラウザに投げてよこす事になる。
そう、この最終着陸地は、ログイン画面のURLである。その結果、Webアプリケーション側から見ると、Officeさんからなんだかごちゃごちゃ言われた後に、そっちはぷっつり連絡が来なくなって、代りに全く別な人から突然ログイン画面にアクセスされたという状態になり、結果ログインに成功しても、デフォルトのメイン画面を表示するしか無くなってしまうのだ。 さっきCookieに入れたのがあるじゃないかと思う人は素人さんなわけで、Cookieというのは、クライアント側(Officeとかブラウザ)に保存されて、サーバーにアクセスする時に毎回HTTPのリクエストヘッダーに入れて、サーバーからはレスポンスヘッダーに入れて返すというキャッチボールな仕組みだから、サーバーには全く保存されていないし、クライアントがOfficeからブラウザに変わった時点で消えてしまう事になる。OfficeからブラウザへCookieを渡す方法なんて無いし、セキュリティ上そんな方法は有ってはならないもの。
私がこれをOffice製品のバグだと言いたい点は、リダイレクションを自前で処理してしまう点ではなく、最終的にブラウザに投げると決めた時に、「なんで最初のHTMLを投げずに、リダイレクションの最後のURLを投げるんだ、このやろう!」な点。
最初のURLを投げれば、ブラウザはリダイレクションをやり直す事になるかもしれないが、しかしサーバーとは最初の手順から話ができるので、通常のアクセスと何ら変わらずに処理できるのに… まったく余計なお世話。
この問題、原因がわかってしまえば、納得はいくのだが、問題は対処が非常にやっかい。ユーザ全員が、MSが書いているようなレジストリ設定を変更する対応をしてくれるなんてことは、まったくあり得ないので、Webアプリ側で対処しなければならないのだが、上述のように、Officeにごにょごにょ言われた後に突然ブラウザからログイン画面へのアクセスが来るので、この別々のアクセスを関連付けるのが難しい。Cookieを使おうにも、そのCookie自体が役立たずだし、単純にgetパラメータ付きのURLを返しても、リダイレクションであるうちは、ブラウザへURLを投げてくれない。
対処するには、何としても、Officeではなく、ブラウザに最初にOfficeがアクセスしてきたURLを渡してやる必要があるのだ。その為には、Officeにはリダイレクションではなく、HTMLを返すURLを投げて、そのURLをブラウザへ渡してもらわなければならない。
うまく説明できないが、要するにどうどうめぐりになってしまう。
ずいぶんと悩んだ挙句、ようやく見つけたこの堂々巡りを断ち切る方法は、クライアントサイドリダイレクション。 と言うと面倒臭そうだが、meta タグの refresh の事。
要するに、get パラメータで渡されるURLを、meta の rehresh の URL に仕込んだHTMLを非認証(public)で生成してそのHTMLをOfficeに投げ返してやると、そのgetパラメータ付きのURLを、Officeがブラウザへ投げて、今度はブラウザが同じHTMLを開くことになる。その後は、refresh により、ブラウザがそのURLへアクセスすることになり、目出度くハイパーリンクの設定をした最初のURLに、「ブラウザが」アクセスする形になり、あとは通常どおりだ。
もちろんOfficeのハイパーリング以外ではこんなしちめんどくさい事をしたくないし、うまくやらないとrefreshが無限ループになるので、ハイパーリングになる可能性のあるURLだけちょっと特殊なものになるように加工しておいて、そのURLの時だけクライアントサイドリダイレクトを使うようにした方が良い。私の場合は、Officeに渡す(ハイパーリンクになる)URLには、本来のURLの末尾に “/x” を付ける事にして、この時だけ refresh を使う事にして、refresh のURLには当然末尾の /x を取ったものを設定した。
目出度くハイパーリンクで目的の画面へ飛ばせる様にはなったが、MSの尻拭いは虚しい。