{"id":3294,"date":"2025-03-24T08:46:30","date_gmt":"2025-03-23T23:46:30","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=3294"},"modified":"2025-03-24T08:47:00","modified_gmt":"2025-03-23T23:47:00","slug":"%e3%80%90%e4%bf%9d%e5%ad%98%e7%89%88%e3%80%91laravel%e8%aa%8d%e8%a8%bc%e5%ae%8c%e5%85%a8%e3%82%ac%e3%82%a4%e3%83%892024-%e5%9f%ba%e7%a4%8e%e3%81%8b%e3%82%89%e5%ae%9f%e8%b7%b5%e3%81%be%e3%81%a7","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=3294","title":{"rendered":"\u3010\u4fdd\u5b58\u7248\u3011Laravel\u8a8d\u8a3c\u5b8c\u5168\u30ac\u30a4\u30c92024 &#8211; \u57fa\u790e\u304b\u3089\u5b9f\u8df5\u307e\u3067\u4f7f\u3048\u308b15\u306e\u653b\u7565\u30c6\u30af\u30cb\u30c3\u30af"},"content":{"rendered":"\n<div class=\"toc\"><br \/>\n<b>Warning<\/b>:  Undefined array key \"is_admin\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>116<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_category_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>121<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>128<\/b><br \/>\n    <div id=\"toc_container\" class=\"sgb-toc--bullets js-smooth-scroll\" data-dialog-title=\"\u76ee\u6b21\">\n      <p class=\"toc_title\">\u76ee\u6b21 <\/p>\n      <ul class=\"toc_list\">  <li class=\"first\">    <a href=\"#i-0\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u6982\u8981<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-1\">Laravel\u8a8d\u8a3c\u6a5f\u80fd\u306e\u5168\u4f53\u50cf\u3068\u7279\u5fb4<\/a>      <\/li>      <li>        <a href=\"#i-2\">\u8a8d\u8a3c\u306b\u95a2\u9023\u3059\u308b\u91cd\u8981\u306a\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-3\">\u8a8d\u8a3c\u6a5f\u80fd\u3067\u5b9f\u73fe\u3067\u304d\u308b\u3053\u3068<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-4\">\u57fa\u672c\u7684\u306a\u8a8d\u8a3c\u6a5f\u80fd\u306e\u5b9f\u88c5\u624b\u9806<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-5\">\u8a8d\u8a3c\u30b9\u30ab\u30d5\u30a9\u30fc\u30eb\u30c9\u306e\u751f\u6210\u3068\u521d\u671f\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-6\">\u30e6\u30fc\u30b6\u30fc\u30e2\u30c7\u30eb\u3068\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-7\">\u30ed\u30b0\u30a4\u30f3\u30fb\u30ed\u30b0\u30a2\u30a6\u30c8\u6a5f\u80fd\u306e\u5b9f\u88c5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-8\">\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-9\">\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u306e\u4f5c\u6210\u3068\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-10\">\u72ec\u81ea\u306e\u8a8d\u8a3c\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u5b9f\u88c5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-11\">\u8a8d\u8a3c\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-12\">\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-13\">\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u306e\u4f5c\u6210\u3068\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-14\">\u72ec\u81ea\u306e\u8a8d\u8a3c\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u5b9f\u88c5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-15\">\u8a8d\u8a3c\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-16\">\u591a\u8981\u7d20\u8a8d\u8a3c\u306e\u5b9f\u88c5\u30ac\u30a4\u30c9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-17\">\u4e8c\u6bb5\u968e\u8a8d\u8a3c\u306e\u57fa\u672c\u8a2d\u5b9a\u3068\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-18\">\u30e1\u30fc\u30eb\u8a8d\u8a3c\u306e\u8ffd\u52a0\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-19\">SMS\u306b\u3088\u308b\u8a8d\u8a3c\u30b3\u30fc\u30c9\u306e\u5b9f\u88c5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-20\">\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u306e\u5c0e\u5165\u65b9\u6cd5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-21\">Laravel Socialite\u306e\u8a2d\u5b9a\u3068\u57fa\u672c\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-22\">\u4e3b\u8981SNS\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3068\u306e\u9023\u643a\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-23\">\u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u306e\u53d6\u5f97\u3068\u4fdd\u5b58\u306e\u5b9f\u88c5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-24\">\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5f37\u5316<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-25\">\u30d1\u30b9\u30ef\u30fc\u30c9\u30dd\u30ea\u30b7\u30fc\u306e\u8a2d\u5b9a\u3068\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-26\">\u4e0d\u6b63\u30a2\u30af\u30bb\u30b9\u5bfe\u7b56\u306e\u5c0e\u5165<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-27\">\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u3068XSS\u5bfe\u7b56<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-28\">\u6a5f\u80fd\u306e\u30c6\u30b9\u30c8\u3068\u8a8d\u8a3c\u904b\u7528<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-29\">\u8a8d\u8a3c\u6a5f\u80fd\u306e\u5358\u4f53\u30c6\u30b9\u30c8\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-30\">\u7d71\u5408\u30c6\u30b9\u30c8\u306b\u3088\u308b\u8a8d\u8a3c\u30d5\u30ed\u30fc\u306e\u691c\u8a3c<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-31\">\u672c\u756a\u74b0\u5883\u3067\u306e\u76e3\u8996\u3068\u30ed\u30b0\u7ba1\u7406<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-32\">\u8a8d\u8a3c\u306b\u95a2\u3059\u308b\u958b\u767a\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-33\">\u30bb\u30ad\u30e5\u30a2\u306a\u30d1\u30b9\u30ef\u30fc\u30c9\u30cf\u30c3\u30b7\u30e5\u5316\u306e\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-34\">\u8a8d\u8a3c\u51e6\u7406\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-35\">\u4fdd\u5b88\u6027\u3092\u8003\u616e\u3057\u305f\u8a2d\u8a08\u30d1\u30bf\u30fc\u30f3<\/a>      <\/li>    <\/ul>  <\/li><\/ul>\n      <a href=\"#\" class=\"sgb-toc-button js-toc-button\" rel=\"nofollow\" data-open-dialog=\"true\"><i class=\"fa fa-list\"><\/i><span class=\"sgb-toc-button__text\">\u76ee\u6b21\u3078<\/span><\/a>\n    <\/div><\/div><h2 class=\"wp-block-heading\" id=\"i-0\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u6982\u8981<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306f\u3001Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u304a\u3051\u308b\u91cd\u8981\u306a\u57fa\u76e4\u6a5f\u80fd\u3067\u3059\u3002\u672c\u7ae0\u3067\u306f\u3001\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u5168\u4f53\u50cf\u304b\u3089\u5b9f\u88c5\u306b\u5fc5\u8981\u306a\u77e5\u8b58\u307e\u3067\u3001\u5305\u62ec\u7684\u306b\u89e3\u8aac\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-1\">Laravel\u8a8d\u8a3c\u6a5f\u80fd\u306e\u5168\u4f53\u50cf\u3068\u7279\u5fb4<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306f\u3001\u30bb\u30ad\u30e5\u30a2\u3067\u67d4\u8edf\u6027\u306e\u9ad8\u3044\u5b9f\u88c5\u3092\u53ef\u80fd\u306b\u3059\u308b\u69d8\u3005\u306a\u6a5f\u80fd\u3092\u63d0\u4f9b\u3057\u3066\u3044\u307e\u3059\u3002\u4e3b\u306a\u7279\u5fb4\u306f\u4ee5\u4e0b\u306e\u901a\u308a\u3067\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30de\u30eb\u30c1\u8a8d\u8a3c\u5bfe\u5fdc<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8907\u6570\u306e\u30ac\u30fc\u30c9\u3092\u4f7f\u7528\u3057\u305f\u7570\u306a\u308b\u8a8d\u8a3c\u65b9\u5f0f\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u3001\u30c8\u30fc\u30af\u30f3\u3001API\u306a\u3069\u69d8\u3005\u306a\u8a8d\u8a3c\u65b9\u5f0f\u3092\u30b5\u30dd\u30fc\u30c8<\/li>\n\n\n\n<li>\u540c\u4e00\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u5185\u3067\u306e\u8907\u6570\u8a8d\u8a3c\u306e\u5171\u5b58<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u67d4\u8edf\u306a\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u30b7\u30b9\u30c6\u30e0<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3001LDAP\u3001\u30ab\u30b9\u30bf\u30e0\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306a\u3069\u591a\u69d8\u306a\u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u30bd\u30fc\u30b9<\/li>\n\n\n\n<li>\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u62e1\u5f35\u3084\u72ec\u81ea\u5b9f\u88c5\u304c\u53ef\u80fd<\/li>\n\n\n\n<li>\u8907\u6570\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u9023\u643a\u6a5f\u80fd<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u6a5f\u80fd\u306e\u5145\u5b9f<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u30cf\u30c3\u30b7\u30e5\u5316<\/li>\n\n\n\n<li>\u30d6\u30eb\u30fc\u30c8\u30d5\u30a9\u30fc\u30b9\u653b\u6483\u5bfe\u7b56<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u30cf\u30a4\u30b8\u30e3\u30c3\u30af\u9632\u6b62<\/li>\n\n\n\n<li>CSRF\u4fdd\u8b77<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">\u8a8d\u8a3c\u306b\u95a2\u9023\u3059\u308b\u91cd\u8981\u306a\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u8a2d\u5b9a\u306f\u4e3b\u306b\u4ee5\u4e0b\u306e\u30d5\u30a1\u30a4\u30eb\u3067\u7ba1\u7406\u3055\u308c\u3066\u3044\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/auth.php - \u8a8d\u8a3c\u306e\u57fa\u672c\u8a2d\u5b9a\nreturn [\n    'defaults' =&gt; [\n        'guard' =&gt; 'web',\n        'passwords' =&gt; 'users',\n    ],\n\n    'guards' =&gt; [\n        'web' =&gt; [\n            'driver' =&gt; 'session',\n            'provider' =&gt; 'users',\n        ],\n        'api' =&gt; [\n            'driver' =&gt; 'token',\n            'provider' =&gt; 'users',\n        ],\n    ],\n\n    'providers' =&gt; [\n        'users' =&gt; [\n            'driver' =&gt; 'eloquent',\n            'model' =&gt; App\\Models\\User::class,\n        ],\n    ],\n];<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u91cd\u8981\u306a\u8a2d\u5b9a\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30d5\u30a9\u30eb\u30c8\u30ac\u30fc\u30c9\u306e\u6307\u5b9a<\/li>\n\n\n\n<li>\u8a8d\u8a3c\u30c9\u30e9\u30a4\u30d0\u30fc\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u5b9a\u7fa9<\/li>\n\n\n\n<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u30ea\u30bb\u30c3\u30c8\u8a2d\u5b9a<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-3\">\u8a8d\u8a3c\u6a5f\u80fd\u3067\u5b9f\u73fe\u3067\u304d\u308b\u3053\u3068<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u6a5f\u80fd\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u7684\u306a\u8a8d\u8a3c\u6a5f\u80fd<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30e6\u30fc\u30b6\u30fc\u767b\u9332<\/li>\n\n\n\n<li>\u30ed\u30b0\u30a4\u30f3\/\u30ed\u30b0\u30a2\u30a6\u30c8<\/li>\n\n\n\n<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u30ea\u30bb\u30c3\u30c8<\/li>\n\n\n\n<li>Remember Me\u6a5f\u80fd<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u9ad8\u5ea6\u306a\u8a8d\u8a3c\u6a5f\u80fd<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4e8c\u8981\u7d20\u8a8d\u8a3c<\/li>\n\n\n\n<li>\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u9023\u643a<\/li>\n\n\n\n<li>API\u30c8\u30fc\u30af\u30f3\u8a8d\u8a3c<\/li>\n\n\n\n<li>\u30b7\u30f3\u30b0\u30eb\u30b5\u30a4\u30f3\u30aa\u30f3<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u6a5f\u80fd<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30a2\u30ab\u30a6\u30f3\u30c8\u30ed\u30c3\u30af<\/li>\n\n\n\n<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u6709\u52b9\u671f\u9650<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/li>\n\n\n\n<li>\u30a2\u30af\u30bb\u30b9\u5236\u5fa1<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u53ef\u80fd\u306a\u6a5f\u80fd<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30ab\u30b9\u30bf\u30e0\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af<\/li>\n\n\n\n<li>\u72ec\u81ea\u306e\u30e6\u30fc\u30b6\u30fc\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc<\/li>\n\n\n\n<li>\u8a8d\u8a3c\u30a4\u30d9\u30f3\u30c8\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0<\/li>\n\n\n\n<li>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306b\u3088\u308b\u5236\u5fa1<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u6a5f\u80fd\u306f\u3001\u30d3\u30b8\u30cd\u30b9\u30cb\u30fc\u30ba\u306b\u5fdc\u3058\u3066\u67d4\u8edf\u306b\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u3059\u308b\u3053\u3068\u304c\u53ef\u80fd\u3067\u3059\u3002\u4ee5\u964d\u306e\u7ae0\u3067\u306f\u3001\u3053\u308c\u3089\u306e\u6a5f\u80fd\u306e\u5177\u4f53\u7684\u306a\u5b9f\u88c5\u65b9\u6cd5\u306b\u3064\u3044\u3066\u8a73\u3057\u304f\u89e3\u8aac\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-4\">\u57fa\u672c\u7684\u306a\u8a8d\u8a3c\u6a5f\u80fd\u306e\u5b9f\u88c5\u624b\u9806<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001Laravel\u306b\u304a\u3051\u308b\u8a8d\u8a3c\u6a5f\u80fd\u306e\u57fa\u672c\u7684\u306a\u5b9f\u88c5\u624b\u9806\u3092\u3001\u5177\u4f53\u7684\u306a\u30b3\u30fc\u30c9\u4f8b\u3068\u5171\u306b\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">\u8a8d\u8a3c\u30b9\u30ab\u30d5\u30a9\u30fc\u30eb\u30c9\u306e\u751f\u6210\u3068\u521d\u671f\u8a2d\u5b9a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Laravel 10\u4ee5\u964d\u3067\u306f\u3001Breeze\u30d1\u30c3\u30b1\u30fc\u30b8\u3092\u4f7f\u7528\u3057\u3066\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30b9\u30ab\u30d5\u30a9\u30fc\u30eb\u30c9\u3092\u7c21\u5358\u306b\u751f\u6210\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Breeze\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Breeze\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\ncomposer require laravel\/breeze --dev\n\n# Breeze\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u57fa\u672c\u8a2d\u5b9a\nphp artisan breeze:install\n\n# \u5fc5\u8981\u306anpm\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u30d3\u30eb\u30c9\nnpm install\nnpm run dev<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u8a8d\u8a3c\u95a2\u9023\u306e\u8a2d\u5b9a\u78ba\u8a8d<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/auth.php\nreturn [\n    'defaults' =&gt; [\n        'guard' =&gt; 'web',\n        'passwords' =&gt; 'users',\n    ],\n\n    \/\/ \u5fc5\u8981\u306b\u5fdc\u3058\u3066\u8a8d\u8a3c\u8a2d\u5b9a\u3092\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\n];\n\n\/\/ .env\nAPP_URL=http:\/\/localhost\nSESSION_DRIVER=file\nSESSION_LIFETIME=120<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">\u30e6\u30fc\u30b6\u30fc\u30e2\u30c7\u30eb\u3068\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e6\u30fc\u30b6\u30fc\u30e2\u30c7\u30eb\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Models\/User.php\nnamespace App\\Models;\n\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;\nuse Laravel\\Sanctum\\HasApiTokens;\n\nclass User extends Authenticatable\n{\n    use HasApiTokens, Notifiable;\n\n    protected $fillable = [\n        'name',\n        'email',\n        'password',\n        'phone',           \/\/ \u8ffd\u52a0\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\n        'last_login_at',   \/\/ \u30ed\u30b0\u30a4\u30f3\u6642\u523b\u8a18\u9332\u7528\n    ];\n\n    protected $hidden = [\n        'password',\n        'remember_token',\n    ];\n\n    protected $casts = [\n        'email_verified_at' =&gt; 'datetime',\n        'last_login_at' =&gt; 'datetime',\n    ];\n\n    \/\/ \u30ab\u30b9\u30bf\u30e0\u30e1\u30bd\u30c3\u30c9\u306e\u8ffd\u52a0\n    public function updateLastLoginTime()\n    {\n        $this-&gt;last_login_at = now();\n        $this-&gt;save();\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30de\u30a4\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u30d5\u30a1\u30a4\u30eb\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ database\/migrations\/xxxx_xx_xx_create_users_table.php\npublic function up()\n{\n    Schema::create('users', function (Blueprint $table) {\n        $table-&gt;id();\n        $table-&gt;string('name');\n        $table-&gt;string('email')-&gt;unique();\n        $table-&gt;string('phone')-&gt;nullable();  \/\/ \u8ffd\u52a0\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\n        $table-&gt;timestamp('email_verified_at')-&gt;nullable();\n        $table-&gt;string('password');\n        $table-&gt;timestamp('last_login_at')-&gt;nullable();  \/\/ \u30ed\u30b0\u30a4\u30f3\u6642\u523b\u8a18\u9332\u7528\n        $table-&gt;rememberToken();\n        $table-&gt;timestamps();\n    });\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-7\">\u30ed\u30b0\u30a4\u30f3\u30fb\u30ed\u30b0\u30a2\u30a6\u30c8\u6a5f\u80fd\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8a8d\u8a3c\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Controllers\/Auth\/LoginController.php\nnamespace App\\Http\\Controllers\\Auth;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Providers\\RouteServiceProvider;\nuse Illuminate\\Foundation\\Auth\\AuthenticatesUsers;\nuse Illuminate\\Http\\Request;\n\nclass LoginController extends Controller\n{\n    use AuthenticatesUsers;\n\n    protected $redirectTo = RouteServiceProvider::HOME;\n\n    \/\/ \u30ed\u30b0\u30a4\u30f3\u8a66\u884c\u56de\u6570\u5236\u9650\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\n    protected $maxAttempts = 5;\n    protected $decayMinutes = 30;\n\n    \/\/ \u30ed\u30b0\u30a4\u30f3\u6210\u529f\u6642\u306e\u51e6\u7406\u3092\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\n    protected function authenticated(Request $request, $user)\n    {\n        $user-&gt;updateLastLoginTime();\n\n        \/\/ \u30ed\u30b0\u30a4\u30f3\u5c65\u6b74\u306e\u8a18\u9332\n        activity()\n            -&gt;causedBy($user)\n            -&gt;log('\u30ed\u30b0\u30a4\u30f3\u3057\u307e\u3057\u305f');\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ed\u30b0\u30a4\u30f3\u30d5\u30a9\u30fc\u30e0\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ resources\/views\/auth\/login.blade.php\n&lt;form method=\"POST\" action=\"{{ route('login') }}\"&gt;\n    @csrf\n\n    &lt;div class=\"mb-4\"&gt;\n        &lt;label for=\"email\"&gt;{{ __('\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9') }}&lt;\/label&gt;\n        &lt;input id=\"email\" type=\"email\" name=\"email\" value=\"{{ old('email') }}\" required autofocus&gt;\n    &lt;\/div&gt;\n\n    &lt;div class=\"mb-4\"&gt;\n        &lt;label for=\"password\"&gt;{{ __('\u30d1\u30b9\u30ef\u30fc\u30c9') }}&lt;\/label&gt;\n        &lt;input id=\"password\" type=\"password\" name=\"password\" required&gt;\n    &lt;\/div&gt;\n\n    &lt;div class=\"mb-4\"&gt;\n        &lt;input type=\"checkbox\" name=\"remember\" id=\"remember\"&gt;\n        &lt;label for=\"remember\"&gt;{{ __('\u30ed\u30b0\u30a4\u30f3\u72b6\u614b\u3092\u4fdd\u6301\u3059\u308b') }}&lt;\/label&gt;\n    &lt;\/div&gt;\n\n    &lt;button type=\"submit\"&gt;\n        {{ __('\u30ed\u30b0\u30a4\u30f3') }}\n    &lt;\/button&gt;\n&lt;\/form&gt;<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u8a8d\u8a3c\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u6d3b\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ routes\/web.php\nRoute::middleware(['auth'])-&gt;group(function () {\n    Route::get('\/dashboard', function () {\n        return view('dashboard');\n    })-&gt;name('dashboard');\n\n    Route::get('\/profile', function () {\n        return view('profile');\n    })-&gt;name('profile');\n});\n\n\/\/ \u7279\u5b9a\u306e\u6761\u4ef6\u3067\u306e\u8a8d\u8a3c\u30c1\u30a7\u30c3\u30af\nRoute::middleware(['auth', 'verified'])-&gt;group(function () {\n    \/\/ \u30e1\u30fc\u30eb\u8a8d\u8a3c\u6e08\u307f\u30e6\u30fc\u30b6\u30fc\u306e\u307f\u30a2\u30af\u30bb\u30b9\u53ef\u80fd\u306a\u30eb\u30fc\u30c8\n});<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3092\u8003\u616e\u3057\u305f\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u9069\u5207\u306a\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30d3\u30ea\u30c6\u30a3\u306e\u5411\u4e0a<\/li>\n\n\n\n<li>\u30ed\u30b0\u30a4\u30f3\u72b6\u614b\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u30bb\u30ad\u30e5\u30a2\u306a\u53d6\u308a\u6271\u3044<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u57fa\u672c\u5b9f\u88c5\u3092\u571f\u53f0\u3068\u3057\u3066\u3001\u6b21\u7ae0\u4ee5\u964d\u3067\u8aac\u660e\u3059\u308b\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u3084\u6a5f\u80fd\u62e1\u5f35\u3092\u884c\u3063\u3066\u3044\u304f\u3053\u3068\u3067\u3001\u3088\u308a\u5805\u7262\u306a\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-8\">\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u65b9\u6cd5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306f\u9ad8\u5ea6\u306a\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u304c\u53ef\u80fd\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u969b\u306e\u30d3\u30b8\u30cd\u30b9\u30cb\u30fc\u30ba\u306b\u5fdc\u3058\u305f\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-9\">\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u306e\u4f5c\u6210\u3068\u8a2d\u5b9a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u72ec\u81ea\u306e\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u306e\u4f5c\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Auth\/Guards\/CustomGuard.php\nnamespace App\\Auth\\Guards;\n\nuse Illuminate\\Contracts\\Auth\\Guard;\nuse Illuminate\\Contracts\\Auth\\UserProvider;\nuse Illuminate\\Http\\Request;\n\nclass CustomGuard implements Guard\n{\n    protected $provider;\n    protected $request;\n    protected $user;\n\n    public function __construct(UserProvider $provider, Request $request)\n    {\n        $this-&gt;provider = $provider;\n        $this-&gt;request = $request;\n    }\n\n    public function check()\n    {\n        return $this-&gt;user() !== null;\n    }\n\n    public function user()\n    {\n        if ($this-&gt;user !== null) {\n            return $this-&gt;user;\n        }\n\n        \/\/ \u30ab\u30b9\u30bf\u30e0\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af\u306e\u5b9f\u88c5\n        $token = $this-&gt;request-&gt;header('X-Auth-Token');\n        if ($token) {\n            $this-&gt;user = $this-&gt;provider-&gt;retrieveByCredentials([\n                'auth_token' =&gt; $token\n            ]);\n        }\n\n        return $this-&gt;user;\n    }\n\n    \/\/ \u305d\u306e\u4ed6\u5fc5\u8981\u306a\u30e1\u30bd\u30c3\u30c9\u306e\u5b9f\u88c5\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ac\u30fc\u30c9\u306e\u767b\u9332<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Providers\/AuthServiceProvider.php\nnamespace App\\Providers;\n\nuse App\\Auth\\Guards\\CustomGuard;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Foundation\\Support\\Providers\\AuthServiceProvider as ServiceProvider;\n\nclass AuthServiceProvider extends ServiceProvider\n{\n    public function boot()\n    {\n        $this-&gt;registerPolicies();\n\n        Auth::extend('custom', function ($app, $name, array $config) {\n            return new CustomGuard(\n                Auth::createUserProvider($config['provider']),\n                $app['request']\n            );\n        });\n    }\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u8a2d\u5b9a\u306e\u8ffd\u52a0<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/auth.php\n'guards' =&gt; [\n    'custom' =&gt; [\n        'driver' =&gt; 'custom',\n        'provider' =&gt; 'users',\n    ],\n],<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-10\">\u72ec\u81ea\u306e\u8a8d\u8a3c\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ab\u30b9\u30bf\u30e0\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u72ec\u81ea\u306e\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u30bd\u30fc\u30b9\u3092\u5229\u7528\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Auth\/CustomUserProvider.php\nnamespace App\\Auth;\n\nuse Illuminate\\Contracts\\Auth\\UserProvider;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\nclass CustomUserProvider implements UserProvider\n{\n    public function retrieveById($identifier)\n    {\n        \/\/ ID\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u53d6\u5f97\u30ed\u30b8\u30c3\u30af\n        return User::find($identifier);\n    }\n\n    public function retrieveByToken($identifier, $token)\n    {\n        \/\/ Remember Me\u30c8\u30fc\u30af\u30f3\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u53d6\u5f97\n        return User::where('id', $identifier)\n            -&gt;where('remember_token', $token)\n            -&gt;first();\n    }\n\n    public function updateRememberToken(Authenticatable $user, $token)\n    {\n        $user-&gt;remember_token = $token;\n        $user-&gt;save();\n    }\n\n    public function retrieveByCredentials(array $credentials)\n    {\n        \/\/ \u30ab\u30b9\u30bf\u30e0\u8a8d\u8a3c\u60c5\u5831\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u53d6\u5f97\n        \/\/ \u4f8b\uff1aLDAP\u3084\u305d\u306e\u4ed6\u306e\u5916\u90e8\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3068\u306e\u9023\u643a\n        return $this-&gt;ldapAuthentication($credentials);\n    }\n\n    public function validateCredentials(Authenticatable $user, array $credentials)\n    {\n        \/\/ \u8a8d\u8a3c\u60c5\u5831\u306e\u691c\u8a3c\u30ed\u30b8\u30c3\u30af\n        return $this-&gt;validateWithExternalSystem($user, $credentials);\n    }\n\n    protected function ldapAuthentication($credentials)\n    {\n        \/\/ LDAP\u30b5\u30fc\u30d0\u30fc\u3068\u306e\u8a8d\u8a3c\u51e6\u7406\n        $ldap = ldap_connect($this-&gt;ldapServer);\n        \/\/ LDAP\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af\u306e\u5b9f\u88c5\n        return $user;\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-11\">\u8a8d\u8a3c\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7279\u5b9a\u306e\u6761\u4ef6\u306b\u57fa\u3065\u3044\u305f\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u4f5c\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Middleware\/CustomAuthentication.php\nnamespace App\\Http\\Middleware;\n\nuse Closure;\nuse Illuminate\\Auth\\Middleware\\Authenticate as Middleware;\n\nclass CustomAuthentication extends Middleware\n{\n    public function handle($request, Closure $next, ...$guards)\n    {\n        if ($this-&gt;authenticate($request, $guards) === false) {\n            return response()-&gt;json(['error' =&gt; '\u8a8d\u8a3c\u304c\u5fc5\u8981\u3067\u3059'], 401);\n        }\n\n        \/\/ \u8ffd\u52a0\u306e\u8a8d\u8a3c\u30c1\u30a7\u30c3\u30af\n        if (!$this-&gt;checkAdditionalConditions($request)) {\n            return response()-&gt;json(['error' =&gt; '\u8ffd\u52a0\u306e\u8a8d\u8a3c\u304c\u5fc5\u8981\u3067\u3059'], 403);\n        }\n\n        return $next($request);\n    }\n\n    protected function checkAdditionalConditions($request)\n    {\n        $user = $request-&gt;user();\n\n        \/\/ IP\u30a2\u30c9\u30ec\u30b9\u306e\u5236\u9650\u30c1\u30a7\u30c3\u30af\n        if (!$this-&gt;isAllowedIP($request-&gt;ip())) {\n            return false;\n        }\n\n        \/\/ \u30a2\u30af\u30bb\u30b9\u6642\u9593\u306e\u5236\u9650\u30c1\u30a7\u30c3\u30af\n        if (!$this-&gt;isWithinAllowedTime()) {\n            return false;\n        }\n\n        \/\/ \u4e8c\u8981\u7d20\u8a8d\u8a3c\u306e\u78ba\u8a8d\n        if ($user-&gt;requires_2fa &amp;&amp; !$user-&gt;has_completed_2fa) {\n            return false;\n        }\n\n        return true;\n    }\n\n    protected function isAllowedIP($ip)\n    {\n        $allowedIPs = config('auth.allowed_ips', []);\n        return in_array($ip, $allowedIPs);\n    }\n\n    protected function isWithinAllowedTime()\n    {\n        $currentHour = now()-&gt;hour;\n        return $currentHour &gt;= 9 &amp;&amp; $currentHour &lt; 18; \/\/ \u55b6\u696d\u6642\u9593\u5185\u306e\u307f\u30a2\u30af\u30bb\u30b9\u53ef\u80fd\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u767b\u9332<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Kernel.php\nprotected $routeMiddleware = [\n    'custom.auth' =&gt; \\App\\Http\\Middleware\\CustomAuthentication::class,\n];<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u4f7f\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ routes\/web.php\nRoute::middleware(['custom.auth'])-&gt;group(function () {\n    Route::get('\/secure-area', 'SecureController@index');\n});\n\n\/\/ \u8907\u6570\u306e\u8a8d\u8a3c\u6761\u4ef6\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\nRoute::middleware(['custom.auth', 'verified', 'role:admin'])-&gt;group(function () {\n    Route::get('\/admin-area', 'AdminController@index');\n});<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3092\u6700\u512a\u5148\u3057\u305f\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3078\u306e\u914d\u616e<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u5145\u5b9f<\/li>\n\n\n\n<li>\u30ed\u30b0\u8a18\u9332\u306e\u9069\u5207\u306a\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30c6\u30b9\u30c8\u5bb9\u6613\u6027\u306e\u78ba\u4fdd<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u6a5f\u80fd\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u30d3\u30b8\u30cd\u30b9\u8981\u4ef6\u306b\u9069\u3057\u305f\u67d4\u8edf\u306a\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-12\">\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u65b9\u6cd5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Laravel\u306e\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306f\u9ad8\u5ea6\u306a\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u304c\u53ef\u80fd\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u969b\u306e\u30d3\u30b8\u30cd\u30b9\u30cb\u30fc\u30ba\u306b\u5fdc\u3058\u305f\u8a8d\u8a3c\u6a5f\u80fd\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-13\">\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u306e\u4f5c\u6210\u3068\u8a2d\u5b9a<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u72ec\u81ea\u306e\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30ac\u30fc\u30c9\u306e\u4f5c\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Auth\/Guards\/CustomGuard.php\nnamespace App\\Auth\\Guards;\n\nuse Illuminate\\Contracts\\Auth\\Guard;\nuse Illuminate\\Contracts\\Auth\\UserProvider;\nuse Illuminate\\Http\\Request;\n\nclass CustomGuard implements Guard\n{\n    protected $provider;\n    protected $request;\n    protected $user;\n\n    public function __construct(UserProvider $provider, Request $request)\n    {\n        $this-&gt;provider = $provider;\n        $this-&gt;request = $request;\n    }\n\n    public function check()\n    {\n        return $this-&gt;user() !== null;\n    }\n\n    public function user()\n    {\n        if ($this-&gt;user !== null) {\n            return $this-&gt;user;\n        }\n\n        \/\/ \u30ab\u30b9\u30bf\u30e0\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af\u306e\u5b9f\u88c5\n        $token = $this-&gt;request-&gt;header('X-Auth-Token');\n        if ($token) {\n            $this-&gt;user = $this-&gt;provider-&gt;retrieveByCredentials([\n                'auth_token' =&gt; $token\n            ]);\n        }\n\n        return $this-&gt;user;\n    }\n\n    \/\/ \u305d\u306e\u4ed6\u5fc5\u8981\u306a\u30e1\u30bd\u30c3\u30c9\u306e\u5b9f\u88c5\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ac\u30fc\u30c9\u306e\u767b\u9332<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Providers\/AuthServiceProvider.php\nnamespace App\\Providers;\n\nuse App\\Auth\\Guards\\CustomGuard;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Illuminate\\Foundation\\Support\\Providers\\AuthServiceProvider as ServiceProvider;\n\nclass AuthServiceProvider extends ServiceProvider\n{\n    public function boot()\n    {\n        $this-&gt;registerPolicies();\n\n        Auth::extend('custom', function ($app, $name, array $config) {\n            return new CustomGuard(\n                Auth::createUserProvider($config['provider']),\n                $app['request']\n            );\n        });\n    }\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u8a2d\u5b9a\u306e\u8ffd\u52a0<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/auth.php\n'guards' =&gt; [\n    'custom' =&gt; [\n        'driver' =&gt; 'custom',\n        'provider' =&gt; 'users',\n    ],\n],<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">\u72ec\u81ea\u306e\u8a8d\u8a3c\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ab\u30b9\u30bf\u30e0\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u4f7f\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u72ec\u81ea\u306e\u30e6\u30fc\u30b6\u30fc\u8a8d\u8a3c\u30bd\u30fc\u30b9\u3092\u5229\u7528\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Auth\/CustomUserProvider.php\nnamespace App\\Auth;\n\nuse Illuminate\\Contracts\\Auth\\UserProvider;\nuse Illuminate\\Contracts\\Auth\\Authenticatable;\n\nclass CustomUserProvider implements UserProvider\n{\n    public function retrieveById($identifier)\n    {\n        \/\/ ID\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u53d6\u5f97\u30ed\u30b8\u30c3\u30af\n        return User::find($identifier);\n    }\n\n    public function retrieveByToken($identifier, $token)\n    {\n        \/\/ Remember Me\u30c8\u30fc\u30af\u30f3\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u53d6\u5f97\n        return User::where('id', $identifier)\n            -&gt;where('remember_token', $token)\n            -&gt;first();\n    }\n\n    public function updateRememberToken(Authenticatable $user, $token)\n    {\n        $user-&gt;remember_token = $token;\n        $user-&gt;save();\n    }\n\n    public function retrieveByCredentials(array $credentials)\n    {\n        \/\/ \u30ab\u30b9\u30bf\u30e0\u8a8d\u8a3c\u60c5\u5831\u306b\u3088\u308b\u30e6\u30fc\u30b6\u30fc\u53d6\u5f97\n        \/\/ \u4f8b\uff1aLDAP\u3084\u305d\u306e\u4ed6\u306e\u5916\u90e8\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3068\u306e\u9023\u643a\n        return $this-&gt;ldapAuthentication($credentials);\n    }\n\n    public function validateCredentials(Authenticatable $user, array $credentials)\n    {\n        \/\/ \u8a8d\u8a3c\u60c5\u5831\u306e\u691c\u8a3c\u30ed\u30b8\u30c3\u30af\n        return $this-&gt;validateWithExternalSystem($user, $credentials);\n    }\n\n    protected function ldapAuthentication($credentials)\n    {\n        \/\/ LDAP\u30b5\u30fc\u30d0\u30fc\u3068\u306e\u8a8d\u8a3c\u51e6\u7406\n        $ldap = ldap_connect($this-&gt;ldapServer);\n        \/\/ LDAP\u8a8d\u8a3c\u30ed\u30b8\u30c3\u30af\u306e\u5b9f\u88c5\n        return $user;\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-15\">\u8a8d\u8a3c\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u7279\u5b9a\u306e\u6761\u4ef6\u306b\u57fa\u3065\u3044\u305f\u30a2\u30af\u30bb\u30b9\u5236\u5fa1\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u4f5c\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Middleware\/CustomAuthentication.php\nnamespace App\\Http\\Middleware;\n\nuse Closure;\nuse Illuminate\\Auth\\Middleware\\Authenticate as Middleware;\n\nclass CustomAuthentication extends Middleware\n{\n    public function handle($request, Closure $next, ...$guards)\n    {\n        if ($this-&gt;authenticate($request, $guards) === false) {\n            return response()-&gt;json(['error' =&gt; '\u8a8d\u8a3c\u304c\u5fc5\u8981\u3067\u3059'], 401);\n        }\n\n        \/\/ \u8ffd\u52a0\u306e\u8a8d\u8a3c\u30c1\u30a7\u30c3\u30af\n        if (!$this-&gt;checkAdditionalConditions($request)) {\n            return response()-&gt;json(['error' =&gt; '\u8ffd\u52a0\u306e\u8a8d\u8a3c\u304c\u5fc5\u8981\u3067\u3059'], 403);\n        }\n\n        return $next($request);\n    }\n\n    protected function checkAdditionalConditions($request)\n    {\n        $user = $request-&gt;user();\n\n        \/\/ IP\u30a2\u30c9\u30ec\u30b9\u306e\u5236\u9650\u30c1\u30a7\u30c3\u30af\n        if (!$this-&gt;isAllowedIP($request-&gt;ip())) {\n            return false;\n        }\n\n        \/\/ \u30a2\u30af\u30bb\u30b9\u6642\u9593\u306e\u5236\u9650\u30c1\u30a7\u30c3\u30af\n        if (!$this-&gt;isWithinAllowedTime()) {\n            return false;\n        }\n\n        \/\/ \u4e8c\u8981\u7d20\u8a8d\u8a3c\u306e\u78ba\u8a8d\n        if ($user-&gt;requires_2fa &amp;&amp; !$user-&gt;has_completed_2fa) {\n            return false;\n        }\n\n        return true;\n    }\n\n    protected function isAllowedIP($ip)\n    {\n        $allowedIPs = config('auth.allowed_ips', []);\n        return in_array($ip, $allowedIPs);\n    }\n\n    protected function isWithinAllowedTime()\n    {\n        $currentHour = now()-&gt;hour;\n        return $currentHour &gt;= 9 &amp;&amp; $currentHour &lt; 18; \/\/ \u55b6\u696d\u6642\u9593\u5185\u306e\u307f\u30a2\u30af\u30bb\u30b9\u53ef\u80fd\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u767b\u9332<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Kernel.php\nprotected $routeMiddleware = [\n    'custom.auth' =&gt; \\App\\Http\\Middleware\\CustomAuthentication::class,\n];<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u4f7f\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ routes\/web.php\nRoute::middleware(['custom.auth'])-&gt;group(function () {\n    Route::get('\/secure-area', 'SecureController@index');\n});\n\n\/\/ \u8907\u6570\u306e\u8a8d\u8a3c\u6761\u4ef6\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\nRoute::middleware(['custom.auth', 'verified', 'role:admin'])-&gt;group(function () {\n    Route::get('\/admin-area', 'AdminController@index');\n});<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3092\u6700\u512a\u5148\u3057\u305f\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3078\u306e\u914d\u616e<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u5145\u5b9f<\/li>\n\n\n\n<li>\u30ed\u30b0\u8a18\u9332\u306e\u9069\u5207\u306a\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30c6\u30b9\u30c8\u5bb9\u6613\u6027\u306e\u78ba\u4fdd<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u6a5f\u80fd\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u30d3\u30b8\u30cd\u30b9\u8981\u4ef6\u306b\u9069\u3057\u305f\u67d4\u8edf\u306a\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-16\">\u591a\u8981\u7d20\u8a8d\u8a3c\u306e\u5b9f\u88c5\u30ac\u30a4\u30c9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u591a\u8981\u7d20\u8a8d\u8a3c\uff08MFA\uff09\u306f\u3001\u73fe\u4ee3\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u304a\u3044\u3066\u91cd\u8981\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u6a5f\u80fd\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001Laravel\u3067\u306e\u591a\u8981\u7d20\u8a8d\u8a3c\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u8a73\u3057\u304f\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">\u4e8c\u6bb5\u968e\u8a8d\u8a3c\u306e\u57fa\u672c\u8a2d\u5b9a\u3068\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5fc5\u8981\u306a\u30d1\u30c3\u30b1\u30fc\u30b8\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">composer require pragmarx\/google2fa-laravel\ncomposer require bacon\/bacon-qr-code<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ database\/migrations\/xxxx_xx_xx_add_2fa_columns_to_users.php\npublic function up()\n{\n    Schema::table('users', function (Blueprint $table) {\n        $table-&gt;boolean('two_factor_enabled')-&gt;default(false);\n        $table-&gt;string('two_factor_secret')-&gt;nullable();\n        $table-&gt;string('two_factor_recovery_codes')-&gt;nullable();\n    });\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Two-Factor\u8a8d\u8a3c\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Controllers\/TwoFactorController.php\nnamespace App\\Http\\Controllers;\n\nuse Google2FA;\nuse Illuminate\\Http\\Request;\nuse App\\Models\\User;\n\nclass TwoFactorController extends Controller\n{\n    public function enable(Request $request)\n    {\n        $user = $request-&gt;user();\n\n        \/\/ \u79d8\u5bc6\u9375\u306e\u751f\u6210\n        $secret = Google2FA::generateSecretKey();\n\n        \/\/ QR\u30b3\u30fc\u30c9\u306e\u751f\u6210\n        $qrCodeUrl = Google2FA::getQRCodeUrl(\n            config('app.name'),\n            $user-&gt;email,\n            $secret\n        );\n\n        \/\/ \u79d8\u5bc6\u9375\u306e\u4fdd\u5b58\uff08\u4e00\u6642\u7684\uff09\n        session(['2fa_secret' =&gt; $secret]);\n\n        return view('auth.2fa.enable', compact('qrCodeUrl'));\n    }\n\n    public function confirm(Request $request)\n    {\n        $request-&gt;validate([\n            'code' =&gt; 'required|numeric|digits:6'\n        ]);\n\n        $user = $request-&gt;user();\n        $secret = session('2fa_secret');\n\n        \/\/ \u30b3\u30fc\u30c9\u306e\u691c\u8a3c\n        $valid = Google2FA::verifyKey($secret, $request-&gt;code);\n\n        if ($valid) {\n            $user-&gt;two_factor_enabled = true;\n            $user-&gt;two_factor_secret = $secret;\n            $user-&gt;save();\n\n            session()-&gt;forget('2fa_secret');\n\n            return redirect()-&gt;route('profile')-&gt;with('success', '\u4e8c\u6bb5\u968e\u8a8d\u8a3c\u304c\u6709\u52b9\u5316\u3055\u308c\u307e\u3057\u305f');\n        }\n\n        return back()-&gt;withErrors(['code' =&gt; '\u7121\u52b9\u306a\u30b3\u30fc\u30c9\u3067\u3059']);\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-18\">\u30e1\u30fc\u30eb\u8a8d\u8a3c\u306e\u8ffd\u52a0\u65b9\u6cd5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e1\u30fc\u30eb\u8a8d\u8a3c\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/auth.php\n'verification' =&gt; [\n    'enable' =&gt; true,\n    'expire' =&gt; 60, \/\/ \u6709\u52b9\u671f\u9650\uff08\u5206\uff09\n],<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30e1\u30fc\u30eb\u8a8d\u8a3c\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Models\/User.php\nuse Illuminate\\Contracts\\Auth\\MustVerifyEmail;\n\nclass User extends Authenticatable implements MustVerifyEmail\n{\n    \/\/ \u5b9f\u88c5\u306f\u81ea\u52d5\u7684\u306b\u63d0\u4f9b\u3055\u308c\u307e\u3059\n}\n\n\/\/ app\/Http\/Controllers\/Auth\/EmailVerificationController.php\nnamespace App\\Http\\Controllers\\Auth;\n\nuse App\\Http\\Controllers\\Controller;\nuse Illuminate\\Foundation\\Auth\\EmailVerificationRequest;\nuse Illuminate\\Http\\Request;\n\nclass EmailVerificationController extends Controller\n{\n    public function notice(Request $request)\n    {\n        return $request-&gt;user()-&gt;hasVerifiedEmail()\n            ? redirect()-&gt;intended(config('fortify.home'))\n            : view('auth.verify-email');\n    }\n\n    public function verify(EmailVerificationRequest $request)\n    {\n        if ($request-&gt;user()-&gt;hasVerifiedEmail()) {\n            return redirect()-&gt;intended(config('fortify.home'));\n        }\n\n        $request-&gt;user()-&gt;markEmailAsVerified();\n\n        return redirect()-&gt;intended(config('fortify.home'))\n            -&gt;with('status', '\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u304c\u78ba\u8a8d\u3055\u308c\u307e\u3057\u305f');\n    }\n\n    public function resend(Request $request)\n    {\n        if ($request-&gt;user()-&gt;hasVerifiedEmail()) {\n            return redirect()-&gt;intended(config('fortify.home'));\n        }\n\n        $request-&gt;user()-&gt;sendEmailVerificationNotification();\n\n        return back()-&gt;with('status', '\u78ba\u8a8d\u30e1\u30fc\u30eb\u3092\u518d\u9001\u4fe1\u3057\u307e\u3057\u305f');\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-19\">SMS\u306b\u3088\u308b\u8a8d\u8a3c\u30b3\u30fc\u30c9\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Twilio\u306e\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">composer require twilio\/sdk<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>SMS\u8a8d\u8a3c\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Services\/SmsAuthService.php\nnamespace App\\Services;\n\nuse Twilio\\Rest\\Client;\n\nclass SmsAuthService\n{\n    protected $client;\n\n    public function __construct()\n    {\n        $this-&gt;client = new Client(\n            config('services.twilio.sid'),\n            config('services.twilio.token')\n        );\n    }\n\n    public function sendCode($phoneNumber, $code)\n    {\n        return $this-&gt;client-&gt;messages-&gt;create(\n            $phoneNumber,\n            [\n                'from' =&gt; config('services.twilio.from'),\n                'body' =&gt; \"\u8a8d\u8a3c\u30b3\u30fc\u30c9: {$code}\"\n            ]\n        );\n    }\n}\n\n\/\/ app\/Http\/Controllers\/SmsAuthController.php\nnamespace App\\Http\\Controllers;\n\nuse App\\Services\\SmsAuthService;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Str;\n\nclass SmsAuthController extends Controller\n{\n    protected $smsService;\n\n    public function __construct(SmsAuthService $smsService)\n    {\n        $this-&gt;smsService = $smsService;\n    }\n\n    public function sendCode(Request $request)\n    {\n        $code = Str::random(6);\n\n        \/\/ \u30b3\u30fc\u30c9\u306e\u4fdd\u5b58\uff08Redis\u63a8\u5968\uff09\n        cache()-&gt;put(\n            \"sms_code_{$request-&gt;user()-&gt;id}\",\n            $code,\n            now()-&gt;addMinutes(5)\n        );\n\n        $this-&gt;smsService-&gt;sendCode($request-&gt;user()-&gt;phone, $code);\n\n        return response()-&gt;json(['message' =&gt; '\u30b3\u30fc\u30c9\u3092\u9001\u4fe1\u3057\u307e\u3057\u305f']);\n    }\n\n    public function verify(Request $request)\n    {\n        $request-&gt;validate([\n            'code' =&gt; 'required|string|size:6'\n        ]);\n\n        $cacheKey = \"sms_code_{$request-&gt;user()-&gt;id}\";\n        $validCode = cache()-&gt;get($cacheKey);\n\n        if ($validCode &amp;&amp; $request-&gt;code === $validCode) {\n            cache()-&gt;forget($cacheKey);\n\n            \/\/ \u8a8d\u8a3c\u6210\u529f\u306e\u51e6\u7406\n            $request-&gt;user()-&gt;markPhoneAsVerified();\n\n            return response()-&gt;json(['message' =&gt; '\u8a8d\u8a3c\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f']);\n        }\n\n        return response()-&gt;json(['message' =&gt; '\u7121\u52b9\u306a\u30b3\u30fc\u30c9\u3067\u3059'], 422);\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30ec\u30fc\u30c8\u5236\u9650\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u5145\u5b9f<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30a2\u306a\u30b3\u30fc\u30c9\u751f\u6210<\/li>\n\n\n\n<li>\u6709\u52b9\u671f\u9650\u306e\u9069\u5207\u306a\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30d3\u30ea\u30c6\u30a3\u306e\u8003\u616e<\/li>\n\n\n\n<li>\u76e3\u67fb\u30ed\u30b0\u306e\u5b9f\u88c5<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30b3\u30fc\u30c9\u306e\u6709\u52b9\u671f\u9650\u3092\u77ed\u304f\u8a2d\u5b9a\uff085\u5206\u7a0b\u5ea6\uff09<\/li>\n\n\n\n<li>\u8a66\u884c\u56de\u6570\u306e\u5236\u9650\u3092\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30bb\u30ad\u30e5\u30a2\u306a\u4e71\u6570\u751f\u6210\u306e\u4f7f\u7528<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u306e\u9069\u5207\u306a\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30ea\u30ab\u30d0\u30ea\u30fc\u30b3\u30fc\u30c9\u306e\u63d0\u4f9b<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u591a\u8981\u7d20\u8a8d\u8a3c\u6a5f\u80fd\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u3088\u308a\u5805\u7262\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3092\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-20\">\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u306e\u5c0e\u5165\u65b9\u6cd5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u306f\u3001\u30e6\u30fc\u30b6\u30fc\u4f53\u9a13\u3092\u5411\u4e0a\u3055\u305b\u308b\u91cd\u8981\u306a\u6a5f\u80fd\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001Laravel Socialite\u3092\u4f7f\u7528\u3057\u305f\u5b89\u5168\u3067\u52b9\u7387\u7684\u306a\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-21\">Laravel Socialite\u306e\u8a2d\u5b9a\u3068\u57fa\u672c\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Socialite\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">composer require laravel\/socialite<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/services.php\nreturn [\n    'github' =&gt; [\n        'client_id' =&gt; env('GITHUB_CLIENT_ID'),\n        'client_secret' =&gt; env('GITHUB_CLIENT_SECRET'),\n        'redirect' =&gt; env('GITHUB_CALLBACK_URL'),\n    ],\n    'google' =&gt; [\n        'client_id' =&gt; env('GOOGLE_CLIENT_ID'),\n        'client_secret' =&gt; env('GOOGLE_CLIENT_SECRET'),\n        'redirect' =&gt; env('GOOGLE_CALLBACK_URL'),\n    ],\n    'facebook' =&gt; [\n        'client_id' =&gt; env('FACEBOOK_CLIENT_ID'),\n        'client_secret' =&gt; env('FACEBOOK_CLIENT_SECRET'),\n        'redirect' =&gt; env('FACEBOOK_CALLBACK_URL'),\n    ],\n];<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u6e96\u5099<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ database\/migrations\/xxxx_xx_xx_add_social_auth_fields_to_users_table.php\npublic function up()\n{\n    Schema::table('users', function (Blueprint $table) {\n        $table-&gt;string('provider')-&gt;nullable();\n        $table-&gt;string('provider_id')-&gt;nullable();\n        $table-&gt;string('avatar')-&gt;nullable();\n        $table-&gt;json('social_data')-&gt;nullable();\n    });\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-22\">\u4e3b\u8981SNS\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3068\u306e\u9023\u643a\u65b9\u6cd5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Controllers\/Auth\/SocialiteController.php\nnamespace App\\Http\\Controllers\\Auth;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Models\\User;\nuse Illuminate\\Support\\Facades\\Auth;\nuse Laravel\\Socialite\\Facades\\Socialite;\nuse Illuminate\\Support\\Str;\nuse Exception;\n\nclass SocialiteController extends Controller\n{\n    protected $providers = ['github', 'google', 'facebook'];\n\n    public function redirect($provider)\n    {\n        if (!in_array($provider, $this-&gt;providers)) {\n            return redirect()-&gt;route('login')\n                -&gt;with('error', '\u7121\u52b9\u306a\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3067\u3059\u3002');\n        }\n\n        return Socialite::driver($provider)-&gt;redirect();\n    }\n\n    public function callback($provider)\n    {\n        try {\n            $socialUser = Socialite::driver($provider)-&gt;user();\n\n            \/\/ \u30e6\u30fc\u30b6\u30fc\u306e\u691c\u7d22\u307e\u305f\u306f\u4f5c\u6210\n            $user = User::firstOrCreate(\n                [\n                    'provider' =&gt; $provider,\n                    'provider_id' =&gt; $socialUser-&gt;getId(),\n                ],\n                [\n                    'name' =&gt; $socialUser-&gt;getName(),\n                    'email' =&gt; $socialUser-&gt;getEmail(),\n                    'password' =&gt; bcrypt(Str::random(16)),\n                    'avatar' =&gt; $socialUser-&gt;getAvatar(),\n                    'social_data' =&gt; $this-&gt;sanitizeSocialData($socialUser),\n                ]\n            );\n\n            \/\/ \u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u306e\u66f4\u65b0\n            $this-&gt;updateUserSocialData($user, $socialUser, $provider);\n\n            \/\/ \u30ed\u30b0\u30a4\u30f3\n            Auth::login($user, true);\n\n            \/\/ \u30ed\u30b0\u30a4\u30f3\u5c65\u6b74\u306e\u8a18\u9332\n            activity()\n                -&gt;causedBy($user)\n                -&gt;log('\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3: ' . $provider);\n\n            return redirect()-&gt;intended(route('dashboard'));\n\n        } catch (Exception $e) {\n            logger()-&gt;error('\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u30a8\u30e9\u30fc', [\n                'provider' =&gt; $provider,\n                'error' =&gt; $e-&gt;getMessage()\n            ]);\n\n            return redirect()-&gt;route('login')\n                -&gt;with('error', '\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002');\n        }\n    }\n\n    protected function sanitizeSocialData($socialUser)\n    {\n        \/\/ \u30bb\u30f3\u30b7\u30c6\u30a3\u30d6\u30c7\u30fc\u30bf\u306e\u9664\u53bb\u3068\u5fc5\u8981\u306a\u30c7\u30fc\u30bf\u306e\u62bd\u51fa\n        return [\n            'name' =&gt; $socialUser-&gt;getName(),\n            'nickname' =&gt; $socialUser-&gt;getNickname(),\n            'avatar' =&gt; $socialUser-&gt;getAvatar(),\n            'email_verified' =&gt; !empty($socialUser-&gt;getEmail()),\n        ];\n    }\n\n    protected function updateUserSocialData($user, $socialUser, $provider)\n    {\n        $user-&gt;update([\n            'avatar' =&gt; $socialUser-&gt;getAvatar(),\n            'social_data' =&gt; array_merge(\n                $user-&gt;social_data ?? [],\n                [\n                    $provider =&gt; $this-&gt;sanitizeSocialData($socialUser),\n                    'last_login' =&gt; now()-&gt;toDateTimeString(),\n                ]\n            ),\n        ]);\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ routes\/web.php\nRoute::prefix('auth\/social')-&gt;group(function () {\n    Route::get('{provider}', [SocialiteController::class, 'redirect'])\n        -&gt;name('social.redirect');\n    Route::get('{provider}\/callback', [SocialiteController::class, 'callback'])\n        -&gt;name('social.callback');\n});<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-23\">\u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u306e\u53d6\u5f97\u3068\u4fdd\u5b58\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e6\u30fc\u30b6\u30fc\u30e2\u30c7\u30eb\u306e\u62e1\u5f35<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Models\/User.php\nclass User extends Authenticatable\n{\n    protected $fillable = [\n        'name',\n        'email',\n        'password',\n        'provider',\n        'provider_id',\n        'avatar',\n        'social_data',\n    ];\n\n    protected $casts = [\n        'email_verified_at' =&gt; 'datetime',\n        'social_data' =&gt; 'array',\n    ];\n\n    public function hasSocialLogin($provider)\n    {\n        return $this-&gt;provider === $provider;\n    }\n\n    public function getSocialProfile($provider)\n    {\n        return $this-&gt;social_data[$provider] ?? null;\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u30d3\u30e5\u30fc\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ resources\/views\/auth\/social-login.blade.php\n&lt;div class=\"social-login-buttons\"&gt;\n    @foreach(['github', 'google', 'facebook'] as $provider)\n        &lt;a href=\"{{ route('social.redirect', $provider) }}\" \n           class=\"btn btn-{{ $provider }}\"&gt;\n            &lt;i class=\"fab fa-{{ $provider }}\"&gt;&lt;\/i&gt;\n            {{ ucfirst($provider) }}\u3067\u30ed\u30b0\u30a4\u30f3\n        &lt;\/a&gt;\n    @endforeach\n&lt;\/div&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>CSRF\u5bfe\u7b56\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30b9\u30c6\u30fc\u30c8\u691c\u8a3c\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30bb\u30f3\u30b7\u30c6\u30a3\u30d6\u30c7\u30fc\u30bf\u306e\u9069\u5207\u306a\u51e6\u7406<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u4f8b\u5916\u51e6\u7406<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u30d5\u30ec\u30f3\u30c9\u30ea\u30fc\u306a\u30a8\u30e9\u30fc\u30e1\u30c3\u30bb\u30fc\u30b8<\/li>\n\n\n\n<li>\u30ed\u30b0\u306e\u8a18\u9332<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u7ba1\u7406<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30e6\u30fc\u30b6\u30fc\u30c7\u30fc\u30bf\u306e\u5b89\u5168\u306a\u4fdd\u5b58<\/li>\n\n\n\n<li>\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u60c5\u5831\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u30a2\u30d0\u30bf\u30fc\u753b\u50cf\u306e\u51e6\u7406<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30e6\u30fc\u30b6\u30d3\u30ea\u30c6\u30a3<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30b7\u30fc\u30e0\u30ec\u30b9\u306a\u8a8d\u8a3c\u30d5\u30ed\u30fc<\/li>\n\n\n\n<li>\u76f4\u611f\u7684\u306aUI\/UX<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30ea\u30c0\u30a4\u30ec\u30af\u30c8\u51e6\u7406<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30a2\u30af\u30bb\u30b9\u30c8\u30fc\u30af\u30f3\u306e\u5b89\u5168\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u30e6\u30fc\u30b6\u30fc\u30c7\u30fc\u30bf\u306e\u6697\u53f7\u5316<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u306e\u9069\u5207\u306a\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30d7\u30ed\u30d0\u30a4\u30c0\u30fcAPI\u306e\u9069\u5207\u306a\u5229\u7528<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u5b9f\u88c5\u306b\u3088\u308a\u3001\u5b89\u5168\u3067\u4f7f\u3044\u3084\u3059\u3044\u30bd\u30fc\u30b7\u30e3\u30eb\u30ed\u30b0\u30a4\u30f3\u6a5f\u80fd\u3092\u63d0\u4f9b\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-24\">\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5f37\u5316<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u30bb\u30ad\u30e5\u30a2\u306a\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u69cb\u7bc9\u306f\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u5b89\u5168\u6027\u3092\u78ba\u4fdd\u3059\u308b\u4e0a\u3067\u6700\u3082\u91cd\u8981\u306a\u8981\u7d20\u306e\u4e00\u3064\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5177\u4f53\u7684\u306a\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5f37\u5316\u624b\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-25\">\u30d1\u30b9\u30ef\u30fc\u30c9\u30dd\u30ea\u30b7\u30fc\u306e\u8a2d\u5b9a\u3068\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30d1\u30b9\u30ef\u30fc\u30c9\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30eb\u30fc\u30eb\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Rules\/PasswordRule.php\nnamespace App\\Rules;\n\nuse Illuminate\\Contracts\\Validation\\Rule;\nuse Illuminate\\Support\\Str;\n\nclass PasswordRule implements Rule\n{\n    public function passes($attribute, $value)\n    {\n        return Str::length($value) &gt;= 12 &amp;&amp;  \/\/ \u6700\u5c0f12\u6587\u5b57\n            preg_match('\/[A-Z]\/', $value) &amp;&amp; \/\/ \u5927\u6587\u5b57\u3092\u542b\u3080\n            preg_match('\/[a-z]\/', $value) &amp;&amp; \/\/ \u5c0f\u6587\u5b57\u3092\u542b\u3080\n            preg_match('\/[0-9]\/', $value) &amp;&amp; \/\/ \u6570\u5b57\u3092\u542b\u3080\n            preg_match('\/[^A-Za-z0-9]\/', $value); \/\/ \u7279\u6b8a\u6587\u5b57\u3092\u542b\u3080\n    }\n\n    public function message()\n    {\n        return '\u30d1\u30b9\u30ef\u30fc\u30c9\u306f12\u6587\u5b57\u4ee5\u4e0a\u3067\u3001\u5927\u6587\u5b57\u3001\u5c0f\u6587\u5b57\u3001\u6570\u5b57\u3001\u7279\u6b8a\u6587\u5b57\u3092\u542b\u3081\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002';\n    }\n}\n\n\/\/ app\/Http\/Requests\/PasswordUpdateRequest.php\nnamespace App\\Http\\Requests;\n\nuse App\\Rules\\PasswordRule;\nuse Illuminate\\Foundation\\Http\\FormRequest;\n\nclass PasswordUpdateRequest extends FormRequest\n{\n    public function rules()\n    {\n        return [\n            'current_password' =&gt; ['required', 'current_password'],\n            'password' =&gt; ['required', new PasswordRule, 'confirmed'],\n        ];\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30d1\u30b9\u30ef\u30fc\u30c9\u5c65\u6b74\u306e\u7ba1\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ database\/migrations\/xxxx_xx_xx_create_password_histories_table.php\npublic function up()\n{\n    Schema::create('password_histories', function (Blueprint $table) {\n        $table-&gt;id();\n        $table-&gt;foreignId('user_id')-&gt;constrained()-&gt;onDelete('cascade');\n        $table-&gt;string('password');\n        $table-&gt;timestamps();\n    });\n}\n\n\/\/ app\/Models\/PasswordHistory.php\nnamespace App\\Models;\n\nuse Illuminate\\Database\\Eloquent\\Model;\n\nclass PasswordHistory extends Model\n{\n    protected $fillable = ['user_id', 'password'];\n\n    public function user()\n    {\n        return $this-&gt;belongsTo(User::class);\n    }\n}\n\n\/\/ app\/Services\/PasswordService.php\nnamespace App\\Services;\n\nuse App\\Models\\PasswordHistory;\nuse Illuminate\\Support\\Facades\\Hash;\n\nclass PasswordService\n{\n    const PASSWORD_HISTORY_LIMIT = 5;\n\n    public function isPasswordReused($userId, $newPassword)\n    {\n        return PasswordHistory::where('user_id', $userId)\n            -&gt;orderBy('created_at', 'desc')\n            -&gt;take(self::PASSWORD_HISTORY_LIMIT)\n            -&gt;get()\n            -&gt;contains(function ($history) use ($newPassword) {\n                return Hash::check($newPassword, $history-&gt;password);\n            });\n    }\n\n    public function storePassword($userId, $password)\n    {\n        PasswordHistory::create([\n            'user_id' =&gt; $userId,\n            'password' =&gt; Hash::make($password)\n        ]);\n\n        \/\/ \u53e4\u3044\u5c65\u6b74\u306e\u524a\u9664\n        PasswordHistory::where('user_id', $userId)\n            -&gt;orderBy('created_at', 'desc')\n            -&gt;skip(self::PASSWORD_HISTORY_LIMIT)\n            -&gt;take(PHP_INT_MAX)\n            -&gt;delete();\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-26\">\u4e0d\u6b63\u30a2\u30af\u30bb\u30b9\u5bfe\u7b56\u306e\u5c0e\u5165<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ec\u30fc\u30c8\u5236\u9650\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Kernel.php\nprotected $routeMiddleware = [\n    'throttle.auth' =&gt; \\App\\Http\\Middleware\\ThrottleAuthAttempts::class,\n];\n\n\/\/ app\/Http\/Middleware\/ThrottleAuthAttempts.php\nnamespace App\\Http\\Middleware;\n\nuse Closure;\nuse Illuminate\\Cache\\RateLimiter;\nuse Illuminate\\Http\\Request;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass ThrottleAuthAttempts\n{\n    protected $limiter;\n\n    public function __construct(RateLimiter $limiter)\n    {\n        $this-&gt;limiter = $limiter;\n    }\n\n    public function handle(Request $request, Closure $next)\n    {\n        $key = $request-&gt;ip() . ':auth_attempts';\n\n        if ($this-&gt;limiter-&gt;tooManyAttempts($key, 5)) { \/\/ 5\u56de\/\u5206\u306e\u5236\u9650\n            $seconds = $this-&gt;limiter-&gt;availableIn($key);\n\n            return response()-&gt;json([\n                'error' =&gt; '\u8a66\u884c\u56de\u6570\u304c\u5236\u9650\u3092\u8d85\u3048\u307e\u3057\u305f\u3002',\n                'retry_after' =&gt; $seconds\n            ], Response::HTTP_TOO_MANY_REQUESTS);\n        }\n\n        $this-&gt;limiter-&gt;hit($key, 60); \/\/ 1\u5206\u9593\u6709\u52b9\n\n        return $next($request);\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u4e0d\u6b63\u30a2\u30af\u30bb\u30b9\u691c\u77e5\u3068\u901a\u77e5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Listeners\/LogFailedLogin.php\nnamespace App\\Listeners;\n\nuse Illuminate\\Auth\\Events\\Failed;\nuse Illuminate\\Support\\Facades\\Log;\nuse App\\Notifications\\SuspiciousLoginAttempt;\n\nclass LogFailedLogin\n{\n    public function handle(Failed $event)\n    {\n        $user = $event-&gt;user;\n        $credentials = $event-&gt;credentials;\n\n        Log::warning('\u30ed\u30b0\u30a4\u30f3\u5931\u6557', [\n            'ip' =&gt; request()-&gt;ip(),\n            'email' =&gt; $credentials['email'] ?? null,\n            'user_agent' =&gt; request()-&gt;userAgent(),\n        ]);\n\n        if ($user) {\n            \/\/ \u9023\u7d9a\u5931\u6557\u56de\u6570\u306e\u30ab\u30a6\u30f3\u30c8\n            $key = \"failed_login_{$user-&gt;id}\";\n            $attempts = cache()-&gt;increment($key);\n\n            \/\/ 5\u56de\u4ee5\u4e0a\u5931\u6557\u3067\u901a\u77e5\n            if ($attempts &gt;= 5) {\n                $user-&gt;notify(new SuspiciousLoginAttempt(\n                    request()-&gt;ip(),\n                    request()-&gt;userAgent()\n                ));\n                cache()-&gt;forget($key);\n            }\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-27\">\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u3068XSS\u5bfe\u7b56<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bb\u30ad\u30e5\u30a2\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/session.php\nreturn [\n    'driver' =&gt; env('SESSION_DRIVER', 'file'),\n    'lifetime' =&gt; env('SESSION_LIFETIME', 120),\n    'expire_on_close' =&gt; true,\n    'encrypt' =&gt; true,\n    'secure' =&gt; true,\n    'http_only' =&gt; true,\n    'same_site' =&gt; 'lax',\n];<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>XSS\u5bfe\u7b56\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Middleware\/SecureHeaders.php\nnamespace App\\Http\\Middleware;\n\nuse Closure;\n\nclass SecureHeaders\n{\n    public function handle($request, Closure $next)\n    {\n        $response = $next($request);\n\n        $response-&gt;headers-&gt;set('X-XSS-Protection', '1; mode=block');\n        $response-&gt;headers-&gt;set('X-Frame-Options', 'SAMEORIGIN');\n        $response-&gt;headers-&gt;set('X-Content-Type-Options', 'nosniff');\n        $response-&gt;headers-&gt;set('Referrer-Policy', 'strict-origin-when-cross-origin');\n        $response-&gt;headers-&gt;set('Content-Security-Policy', \"default-src 'self'\");\n\n        return $response;\n    }\n}\n\n\/\/ app\/Providers\/AppServiceProvider.php\npublic function boot()\n{\n    \\Blade::directive('sanitize', function ($expression) {\n        return \"&lt;?php echo e(htmlspecialchars($expression, ENT_QUOTES, 'UTF-8')); ?&gt;\";\n    });\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30bb\u30c3\u30b7\u30e7\u30f3\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e\u5f37\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Http\/Controllers\/Auth\/LoginController.php\nprotected function authenticated(Request $request, $user)\n{\n    \/\/ \u53e4\u3044\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u524a\u9664\n    $request-&gt;session()-&gt;regenerate();\n\n    \/\/ \u30c7\u30d0\u30a4\u30b9\u60c5\u5831\u306e\u8a18\u9332\n    $user-&gt;login_histories()-&gt;create([\n        'ip_address' =&gt; $request-&gt;ip(),\n        'user_agent' =&gt; $request-&gt;userAgent(),\n        'login_at' =&gt; now(),\n    ]);\n\n    \/\/ \u4e0d\u8981\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u30c7\u30fc\u30bf\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\n    $request-&gt;session()-&gt;forget(['temp_data', 'wizard_data']);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5f37\u529b\u306a\u30cf\u30c3\u30b7\u30e5\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u306e\u4f7f\u7528<\/li>\n\n\n\n<li>\u30d1\u30b9\u30ef\u30fc\u30c9\u6709\u52b9\u671f\u9650\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u5171\u901a\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u30d6\u30e9\u30c3\u30af\u30ea\u30b9\u30c8<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30a2\u30af\u30bb\u30b9\u5236\u5fa1<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>IP\u30d9\u30fc\u30b9\u306e\u30d6\u30ed\u30c3\u30af<\/li>\n\n\n\n<li>\u30c7\u30d0\u30a4\u30b9\u30d5\u30a3\u30f3\u30ac\u30fc\u30d7\u30ea\u30f3\u30c8<\/li>\n\n\n\n<li>\u5730\u7406\u7684\u5236\u9650\u306e\u5b9f\u88c5<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u6697\u53f7\u5316<\/li>\n\n\n\n<li>\u30bb\u30c3\u30b7\u30e7\u30f3ID\u306e\u518d\u751f\u6210<\/li>\n\n\n\n<li>\u30a2\u30a4\u30c9\u30eb\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u76e3\u8996\u3068\u30ed\u30b0<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30a4\u30d9\u30f3\u30c8\u306e\u8a18\u9332<\/li>\n\n\n\n<li>\u7570\u5e38\u691c\u77e5\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u7ba1\u7406\u8005\u901a\u77e5\u306e\u8a2d\u5b9a<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u5bfe\u7b56\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001\u3088\u308a\u5805\u7262\u306a\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-28\">\u6a5f\u80fd\u306e\u30c6\u30b9\u30c8\u3068\u8a8d\u8a3c\u904b\u7528<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u4fe1\u983c\u6027\u3092\u78ba\u4fdd\u3059\u308b\u305f\u3081\u306b\u306f\u3001\u5305\u62ec\u7684\u306a\u30c6\u30b9\u30c8\u3068\u9069\u5207\u306a\u904b\u7528\u7ba1\u7406\u304c\u4e0d\u53ef\u6b20\u3067\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30c6\u30b9\u30c8\u306e\u5b9f\u88c5\u65b9\u6cd5\u3068\u904b\u7528\u7ba1\u7406\u306e\u624b\u6cd5\u3092\u8a73\u3057\u304f\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-29\">\u8a8d\u8a3c\u6a5f\u80fd\u306e\u5358\u4f53\u30c6\u30b9\u30c8\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8a8d\u8a3c\u95a2\u9023\u306e\u30c6\u30b9\u30c8\u30b1\u30fc\u30b9\u4f5c\u6210<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ tests\/Unit\/Auth\/LoginTest.php\nnamespace Tests\\Unit\\Auth;\n\nuse Tests\\TestCase;\nuse App\\Models\\User;\nuse Illuminate\\Foundation\\Testing\\RefreshDatabase;\nuse Illuminate\\Support\\Facades\\Hash;\n\nclass LoginTest extends TestCase\n{\n    use RefreshDatabase;\n\n    private $user;\n    private $password = 'Test@12345';\n\n    protected function setUp(): void\n    {\n        parent::setUp();\n\n        $this-&gt;user = User::factory()-&gt;create([\n            'password' =&gt; Hash::make($this-&gt;password)\n        ]);\n    }\n\n    \/** @test *\/\n    public function user_can_login_with_correct_credentials()\n    {\n        $response = $this-&gt;post('\/login', [\n            'email' =&gt; $this-&gt;user-&gt;email,\n            'password' =&gt; $this-&gt;password,\n        ]);\n\n        $response-&gt;assertRedirect('\/dashboard');\n        $this-&gt;assertAuthenticated();\n    }\n\n    \/** @test *\/\n    public function user_cannot_login_with_incorrect_password()\n    {\n        $response = $this-&gt;post('\/login', [\n            'email' =&gt; $this-&gt;user-&gt;email,\n            'password' =&gt; 'wrong-password',\n        ]);\n\n        $response-&gt;assertSessionHasErrors('email');\n        $this-&gt;assertGuest();\n    }\n\n    \/** @test *\/\n    public function user_is_locked_after_too_many_attempts()\n    {\n        for ($i = 0; $i &lt; 6; $i++) {\n            $response = $this-&gt;post('\/login', [\n                'email' =&gt; $this-&gt;user-&gt;email,\n                'password' =&gt; 'wrong-password',\n            ]);\n        }\n\n        $response-&gt;assertStatus(429); \/\/ Too Many Requests\n    }\n}\n\n\/\/ tests\/Unit\/Auth\/PasswordPolicyTest.php\nclass PasswordPolicyTest extends TestCase\n{\n    use RefreshDatabase;\n\n    \/** @test *\/\n    public function password_must_meet_complexity_requirements()\n    {\n        $user = User::factory()-&gt;create();\n\n        $response = $this-&gt;actingAs($user)-&gt;post('\/user\/password', [\n            'current_password' =&gt; 'current-password',\n            'password' =&gt; 'weak',\n            'password_confirmation' =&gt; 'weak',\n        ]);\n\n        $response-&gt;assertSessionHasErrors('password');\n    }\n\n    \/** @test *\/\n    public function password_cannot_be_reused()\n    {\n        $user = User::factory()-&gt;create();\n        $oldPassword = 'Old@Password123';\n\n        \/\/ \u53e4\u3044\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5c65\u6b74\u306b\u8ffd\u52a0\n        $user-&gt;passwordHistories()-&gt;create([\n            'password' =&gt; Hash::make($oldPassword)\n        ]);\n\n        $response = $this-&gt;actingAs($user)-&gt;post('\/user\/password', [\n            'current_password' =&gt; 'current-password',\n            'password' =&gt; $oldPassword,\n            'password_confirmation' =&gt; $oldPassword,\n        ]);\n\n        $response-&gt;assertSessionHasErrors('password');\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-30\">\u7d71\u5408\u30c6\u30b9\u30c8\u306b\u3088\u308b\u8a8d\u8a3c\u30d5\u30ed\u30fc\u306e\u691c\u8a3c<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8a8d\u8a3c\u30d5\u30ed\u30fc\u306e\u7d71\u5408\u30c6\u30b9\u30c8<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ tests\/Feature\/Auth\/AuthenticationTest.php\nnamespace Tests\\Feature\\Auth;\n\nuse Tests\\TestCase;\nuse App\\Models\\User;\nuse Illuminate\\Support\\Facades\\Event;\nuse Illuminate\\Auth\\Events\\Registered;\nuse Illuminate\\Foundation\\Testing\\RefreshDatabase;\n\nclass AuthenticationTest extends TestCase\n{\n    use RefreshDatabase;\n\n    \/** @test *\/\n    public function complete_authentication_flow()\n    {\n        Event::fake();\n\n        \/\/ 1. \u30e6\u30fc\u30b6\u30fc\u767b\u9332\n        $response = $this-&gt;post('\/register', [\n            'name' =&gt; 'Test User',\n            'email' =&gt; 'test@example.com',\n            'password' =&gt; 'Password@123',\n            'password_confirmation' =&gt; 'Password@123',\n        ]);\n\n        Event::assertDispatched(Registered::class);\n        $response-&gt;assertRedirect('\/email\/verify');\n\n        $user = User::where('email', 'test@example.com')-&gt;first();\n\n        \/\/ 2. \u30e1\u30fc\u30eb\u8a8d\u8a3c\n        $this-&gt;actingAs($user)\n            -&gt;get('\/email\/verify\/' . sha1($user-&gt;email));\n\n        $user-&gt;refresh();\n        $this-&gt;assertTrue($user-&gt;hasVerifiedEmail());\n\n        \/\/ 3. \u30ed\u30b0\u30a2\u30a6\u30c8\n        $this-&gt;post('\/logout')\n            -&gt;assertRedirect('\/');\n        $this-&gt;assertGuest();\n\n        \/\/ 4. \u30ed\u30b0\u30a4\u30f3\n        $this-&gt;post('\/login', [\n            'email' =&gt; 'test@example.com',\n            'password' =&gt; 'Password@123',\n        ])-&gt;assertRedirect('\/dashboard');\n        $this-&gt;assertAuthenticated();\n\n        \/\/ 5. \u30d1\u30b9\u30ef\u30fc\u30c9\u5909\u66f4\n        $this-&gt;actingAs($user)\n            -&gt;post('\/user\/password', [\n                'current_password' =&gt; 'Password@123',\n                'password' =&gt; 'NewPassword@123',\n                'password_confirmation' =&gt; 'NewPassword@123',\n            ])\n            -&gt;assertSessionHasNoErrors();\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-31\">\u672c\u756a\u74b0\u5883\u3067\u306e\u76e3\u8996\u3068\u30ed\u30b0\u7ba1\u7406<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u8a8d\u8a3c\u30a4\u30d9\u30f3\u30c8\u306e\u30ed\u30b0\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Providers\/EventServiceProvider.php\nprotected $listen = [\n    'Illuminate\\Auth\\Events\\Login' =&gt; [\n        'App\\Listeners\\LogSuccessfulLogin',\n    ],\n    'Illuminate\\Auth\\Events\\Failed' =&gt; [\n        'App\\Listeners\\LogFailedLogin',\n    ],\n    'Illuminate\\Auth\\Events\\Logout' =&gt; [\n        'App\\Listeners\\LogSuccessfulLogout',\n    ],\n    'Illuminate\\Auth\\Events\\PasswordReset' =&gt; [\n        'App\\Listeners\\LogPasswordReset',\n    ],\n];\n\n\/\/ app\/Listeners\/LogSuccessfulLogin.php\nnamespace App\\Listeners;\n\nuse Illuminate\\Auth\\Events\\Login;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass LogSuccessfulLogin\n{\n    public function handle(Login $event)\n    {\n        Log::channel('auth')-&gt;info('\u30ed\u30b0\u30a4\u30f3\u6210\u529f', [\n            'user_id' =&gt; $event-&gt;user-&gt;id,\n            'email' =&gt; $event-&gt;user-&gt;email,\n            'ip' =&gt; request()-&gt;ip(),\n            'user_agent' =&gt; request()-&gt;userAgent(),\n        ]);\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u76e3\u8996\u30b7\u30b9\u30c6\u30e0\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Console\/Commands\/MonitorAuthActivity.php\nnamespace App\\Console\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse App\\Models\\LoginHistory;\nuse Illuminate\\Support\\Facades\\Notification;\nuse App\\Notifications\\SuspiciousActivityDetected;\n\nclass MonitorAuthActivity extends Command\n{\n    protected $signature = 'auth:monitor';\n    protected $description = '\u8a8d\u8a3c\u6d3b\u52d5\u306e\u76e3\u8996';\n\n    public function handle()\n    {\n        \/\/ \u7570\u5e38\u306a\u30ed\u30b0\u30a4\u30f3\u8a66\u884c\u306e\u691c\u51fa\n        $suspiciousAttempts = LoginHistory::where('created_at', '&gt;=', now()-&gt;subHour())\n            -&gt;groupBy('ip_address')\n            -&gt;havingRaw('COUNT(*) &gt; ?', [10])\n            -&gt;get();\n\n        if ($suspiciousAttempts-&gt;isNotEmpty()) {\n            Notification::route('slack', config('services.slack.webhook_url'))\n                -&gt;notify(new SuspiciousActivityDetected($suspiciousAttempts));\n        }\n\n        \/\/ \u540c\u6642\u30ed\u30b0\u30a4\u30f3\u306e\u691c\u51fa\n        $simultaneousLogins = LoginHistory::whereHas('user', function ($query) {\n            $query-&gt;where('created_at', '&gt;=', now()-&gt;subMinutes(5))\n                -&gt;groupBy('user_id')\n                -&gt;havingRaw('COUNT(DISTINCT ip_address) &gt; ?', [2]);\n        })-&gt;get();\n\n        if ($simultaneousLogins-&gt;isNotEmpty()) {\n            \/\/ \u7ba1\u7406\u8005\u306b\u901a\u77e5\n            $this-&gt;notifyAdmins($simultaneousLogins);\n        }\n    }\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30ed\u30b0\u30ed\u30fc\u30c6\u30fc\u30b7\u30e7\u30f3\u306e\u8a2d\u5b9a<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ config\/logging.php\n'channels' =&gt; [\n    'auth' =&gt; [\n        'driver' =&gt; 'daily',\n        'path' =&gt; storage_path('logs\/auth.log'),\n        'level' =&gt; 'info',\n        'days' =&gt; 14,\n    ],\n],<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c6\u30b9\u30c8\u8a2d\u8a08<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30a8\u30c3\u30b8\u30b1\u30fc\u30b9\u306e\u8003\u616e<\/li>\n\n\n\n<li>\u7570\u5e38\u7cfb\u306e\u30c6\u30b9\u30c8<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c6\u30b9\u30c8<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u7ba1\u7406<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d5\u30a1\u30af\u30c8\u30ea\u30fc\u306e\u9069\u5207\u306a\u4f7f\u7528<\/li>\n\n\n\n<li>\u30b7\u30fc\u30c0\u30fc\u306e\u6d3b\u7528<\/li>\n\n\n\n<li>\u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u76e3\u8996\u4f53\u5236<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u30a2\u30e9\u30fc\u30c8<\/li>\n\n\n\n<li>\u5b9a\u671f\u7684\u306a\u76e3\u67fb<\/li>\n\n\n\n<li>\u30e1\u30c8\u30ea\u30af\u30b9\u306e\u53ce\u96c6<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30ed\u30b0\u7ba1\u7406<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u69cb\u9020\u5316\u30ed\u30b0<\/li>\n\n\n\n<li>\u30ed\u30b0\u30ec\u30d9\u30eb\u306e\u9069\u5207\u306a\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30ed\u30b0\u306e\u4fdd\u6301\u671f\u9593\u7ba1\u7406<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u30c6\u30b9\u30c8\u3068\u904b\u7528\u7ba1\u7406\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u4fe1\u983c\u6027\u3068\u5b89\u5b9a\u6027\u3092\u78ba\u4fdd\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-32\">\u8a8d\u8a3c\u306b\u95a2\u3059\u308b\u958b\u767a\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u306e\u958b\u767a\u306b\u306f\u3001\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3001\u4fdd\u5b88\u6027\u306a\u3069\u3001\u591a\u304f\u306e\u8981\u7d20\u3092\u8003\u616e\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u3053\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u8df5\u7684\u306a\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-33\">\u30bb\u30ad\u30e5\u30a2\u306a\u30d1\u30b9\u30ef\u30fc\u30c9\u30cf\u30c3\u30b7\u30e5\u5316\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30d1\u30b9\u30ef\u30fc\u30c9\u30cf\u30c3\u30b7\u30e5\u5316\u30b5\u30fc\u30d3\u30b9\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Services\/PasswordHashingService.php\nnamespace App\\Services;\n\nuse Illuminate\\Support\\Facades\\Hash;\nuse Illuminate\\Contracts\\Hashing\\Hasher;\n\nclass PasswordHashingService\n{\n    private $hasher;\n\n    public function __construct(Hasher $hasher)\n    {\n        $this-&gt;hasher = $hasher;\n    }\n\n    public function hashPassword(string $password): string\n    {\n        \/\/ \u6700\u65b0\u306e\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u3068\u30b3\u30b9\u30c8\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u4f7f\u7528\n        $options = [\n            'memory_cost' =&gt; 1024,\n            'time_cost' =&gt; 2,\n            'threads' =&gt; 2,\n        ];\n\n        return $this-&gt;hasher-&gt;make($password, $options);\n    }\n\n    public function needsRehash(string $hashedPassword): bool\n    {\n        return $this-&gt;hasher-&gt;needsRehash($hashedPassword);\n    }\n\n    public function upgradeHashIfNeeded(string $password, string $currentHash): ?string\n    {\n        if ($this-&gt;needsRehash($currentHash)) {\n            return $this-&gt;hashPassword($password);\n        }\n\n        return null;\n    }\n}\n\n\/\/ app\/Http\/Controllers\/Auth\/LoginController.php\nclass LoginController extends Controller\n{\n    protected $passwordService;\n\n    public function __construct(PasswordHashingService $passwordService)\n    {\n        $this-&gt;passwordService = $passwordService;\n    }\n\n    protected function authenticated(Request $request, $user)\n    {\n        \/\/ \u30ed\u30b0\u30a4\u30f3\u6210\u529f\u6642\u306b\u30cf\u30c3\u30b7\u30e5\u306e\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3092\u78ba\u8a8d\n        if ($newHash = $this-&gt;passwordService-&gt;upgradeHashIfNeeded($request-&gt;password, $user-&gt;password)) {\n            $user-&gt;password = $newHash;\n            $user-&gt;save();\n        }\n    }\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-34\">\u8a8d\u8a3c\u51e6\u7406\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u6d3b\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Services\/UserAuthenticationService.php\nnamespace App\\Services;\n\nuse Illuminate\\Support\\Facades\\Cache;\nuse App\\Models\\User;\n\nclass UserAuthenticationService\n{\n    private const CACHE_TTL = 3600; \/\/ 1\u6642\u9593\n\n    public function getUserPermissions(User $user): array\n    {\n        $cacheKey = \"user_permissions_{$user-&gt;id}\";\n\n        return Cache::remember($cacheKey, self::CACHE_TTL, function () use ($user) {\n            return $user-&gt;permissions()\n                -&gt;with('role')\n                -&gt;get()\n                -&gt;toArray();\n        });\n    }\n\n    public function invalidateUserCache(User $user): void\n    {\n        Cache::forget(\"user_permissions_{$user-&gt;id}\");\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30af\u30a8\u30ea\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Models\/User.php\nclass User extends Authenticatable\n{\n    public function scopeWithAuthData($query)\n    {\n        return $query-&gt;select([\n            'id', 'email', 'password', 'remember_token',\n            'two_factor_enabled', 'last_login_at'\n        ])\n        -&gt;with(['roles' =&gt; function ($query) {\n            $query-&gt;select('id', 'name')\n                -&gt;with(['permissions' =&gt; function ($query) {\n                    $query-&gt;select('id', 'name', 'role_id');\n                }]);\n        }]);\n    }\n}\n\n\/\/ app\/Http\/Controllers\/Auth\/LoginController.php\nprotected function attemptLogin(Request $request)\n{\n    $credentials = $request-&gt;only('email', 'password');\n    $user = User::withAuthData()\n        -&gt;where('email', $credentials['email'])\n        -&gt;first();\n\n    if (!$user) {\n        return false;\n    }\n\n    return $this-&gt;guard()-&gt;attempt($credentials, $request-&gt;filled('remember'));\n}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-35\">\u4fdd\u5b88\u6027\u3092\u8003\u616e\u3057\u305f\u8a2d\u8a08\u30d1\u30bf\u30fc\u30f3<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Repository \u30d1\u30bf\u30fc\u30f3\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Repositories\/Contracts\/UserRepositoryInterface.php\nnamespace App\\Repositories\\Contracts;\n\ninterface UserRepositoryInterface\n{\n    public function findByEmail(string $email);\n    public function findByToken(string $token);\n    public function create(array $data);\n    public function update(int $id, array $data);\n}\n\n\/\/ app\/Repositories\/UserRepository.php\nnamespace App\\Repositories;\n\nuse App\\Models\\User;\nuse App\\Repositories\\Contracts\\UserRepositoryInterface;\nuse Illuminate\\Support\\Facades\\Cache;\n\nclass UserRepository implements UserRepositoryInterface\n{\n    private $model;\n\n    public function __construct(User $model)\n    {\n        $this-&gt;model = $model;\n    }\n\n    public function findByEmail(string $email)\n    {\n        return Cache::remember(\n            \"user_email_{$email}\",\n            now()-&gt;addMinutes(10),\n            fn() =&gt; $this-&gt;model-&gt;where('email', $email)-&gt;first()\n        );\n    }\n\n    public function findByToken(string $token)\n    {\n        return $this-&gt;model-&gt;where('remember_token', $token)-&gt;first();\n    }\n\n    public function create(array $data)\n    {\n        return $this-&gt;model-&gt;create($data);\n    }\n\n    public function update(int $id, array $data)\n    {\n        $user = $this-&gt;model-&gt;findOrFail($id);\n        $user-&gt;update($data);\n        return $user;\n    }\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Service \u30ec\u30a4\u30e4\u30fc\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Services\/AuthenticationService.php\nnamespace App\\Services;\n\nuse App\\Repositories\\Contracts\\UserRepositoryInterface;\nuse App\\Events\\LoginAttempted;\nuse Illuminate\\Support\\Facades\\Hash;\n\nclass AuthenticationService\n{\n    private $userRepository;\n    private $passwordService;\n\n    public function __construct(\n        UserRepositoryInterface $userRepository,\n        PasswordHashingService $passwordService\n    ) {\n        $this-&gt;userRepository = $userRepository;\n        $this-&gt;passwordService = $passwordService;\n    }\n\n    public function attemptLogin(array $credentials, bool $remember = false)\n    {\n        $user = $this-&gt;userRepository-&gt;findByEmail($credentials['email']);\n\n        if (!$user) {\n            event(new LoginAttempted(false, $credentials['email']));\n            return false;\n        }\n\n        if (!Hash::check($credentials['password'], $user-&gt;password)) {\n            event(new LoginAttempted(false, $credentials['email']));\n            return false;\n        }\n\n        \/\/ \u30d1\u30b9\u30ef\u30fc\u30c9\u30cf\u30c3\u30b7\u30e5\u306e\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u30c1\u30a7\u30c3\u30af\n        if ($newHash = $this-&gt;passwordService-&gt;upgradeHashIfNeeded(\n            $credentials['password'],\n            $user-&gt;password\n        )) {\n            $this-&gt;userRepository-&gt;update($user-&gt;id, [\n                'password' =&gt; $newHash\n            ]);\n        }\n\n        event(new LoginAttempted(true, $credentials['email']));\n\n        if ($remember) {\n            $this-&gt;handleRememberToken($user);\n        }\n\n        return $user;\n    }\n\n    private function handleRememberToken($user)\n    {\n        $token = hash('sha256', random_bytes(32));\n        $this-&gt;userRepository-&gt;update($user-&gt;id, [\n            'remember_token' =&gt; $token\n        ]);\n    }\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>DI\u30b3\u30f3\u30c6\u30ca\u306e\u6d3b\u7528<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/ app\/Providers\/AuthServiceProvider.php\nnamespace App\\Providers;\n\nuse Illuminate\\Support\\ServiceProvider;\nuse App\\Repositories\\Contracts\\UserRepositoryInterface;\nuse App\\Repositories\\UserRepository;\nuse App\\Services\\AuthenticationService;\nuse App\\Services\\PasswordHashingService;\n\nclass AuthServiceProvider extends ServiceProvider\n{\n    public function register()\n    {\n        $this-&gt;app-&gt;bind(UserRepositoryInterface::class, UserRepository::class);\n\n        $this-&gt;app-&gt;singleton(AuthenticationService::class, function ($app) {\n            return new AuthenticationService(\n                $app-&gt;make(UserRepositoryInterface::class),\n                $app-&gt;make(PasswordHashingService::class)\n            );\n        });\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u306e\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6700\u65b0\u306e\u30cf\u30c3\u30b7\u30e5\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u306e\u4f7f\u7528<\/li>\n\n\n\n<li>\u9069\u5207\u306a\u30b3\u30b9\u30c8\u30d1\u30e9\u30e1\u30fc\u30bf\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u5b9a\u671f\u7684\u306a\u30cf\u30c3\u30b7\u30e5\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u6226\u7565<\/li>\n\n\n\n<li>\u30af\u30a8\u30ea\u306e\u6700\u9069\u5316<\/li>\n\n\n\n<li>N+1\u554f\u984c\u306e\u56de\u907f<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u4fdd\u5b88\u6027<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8cac\u4efb\u306e\u660e\u78ba\u306a\u5206\u96e2<\/li>\n\n\n\n<li>\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u306e\u6d3b\u7528<\/li>\n\n\n\n<li>\u4f9d\u5b58\u6027\u306e\u9069\u5207\u306a\u7ba1\u7406<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u91cd\u8981\u306a\u64cd\u4f5c\u306e\u30ed\u30b0\u8a18\u9332<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e1\u30c8\u30ea\u30af\u30b9\u306e\u53ce\u96c6<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u76e3\u8996\u306e\u5b9f\u88c5<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3089\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u9069\u5207\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u3067\u3001\u30bb\u30ad\u30e5\u30a2\u3067\u4fdd\u5b88\u6027\u306e\u9ad8\u3044\u8a8d\u8a3c\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/ &#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[33,12],"tags":[],"class_list":["post-3294","post","type-post","status-publish","format-standard","category-php-laravel","category-php","nothumb"],"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/3294","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3294"}],"version-history":[{"count":2,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/3294\/revisions"}],"predecessor-version":[{"id":3296,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/3294\/revisions\/3296"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3294"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3294"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3294"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}