jjug ccc 2017 spring ccc_g1

jjug ccc 2017 spring ccc_g1

681560628decbdf2f4ea68552fb4ec33?s=128

yuichiro umezawa

May 19, 2017
Tweet

Transcript

  1. ඇػೳཁ݅ͱ4QSJOH#PPU ++6($$$4QSJOH DDD@H

  2. ++6($$$4QSJOHDDD@H ໨࣍ͱΰʔϧ w ໨࣍ w ࣗݾ঺հ w ඇػೳཁ݅ͱ͸ w 4QSJOH#PPU"DUVBUPS

    w 4QSJOH4FDVSJUZ0UIFST w ·ͱΊ w ΰʔϧ w ඇػೳཁ݅ͷ೉͠͞ͱରԠͷίπΛ஌Δ w εʔπͱςοΫͷڮ౉͠ 
  3. ++6($$$4QSJOHDDD@H ࣗݾ঺հ w കᖒ༤Ұ࿠ ͏Ί͟ΘΏ͏͍ͪΖ͏  w ͕Δ͕΂ !HBSCBHFUPXO 

    w גࣜձࣾ%54 w 4*FS w ೥݄ೖࣾ w ೥݄ҟಈ w डୗ։ൃˠݚڀ։ൃɾٕज़ࢧԉ w ՝֎׆ಈ w 1MBZ'SBNFXPSLؔ࿈ͰدߘɺొஃͳͲ w ͨ·ʹυϥϜΛୟ͘ 
  4. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸ 

  5. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸  ඇػೳཁ݅(Non-functional requirement)ͱ͸ɺγεςϜઃܭ΍৘ใγε ςϜ։ൃ্ͷཁٻ෼ੳʹ͓͍ͯɺཁ݅ɺγεςϜཁ݅ͱ͍ͬͨػೳ໘Ҏ ֎ͷશൠΛࢦ͢ɻ ػೳཁ݅Λ࣮૷͢ΔͨΊͷઃܭ͕γεςϜઃܭͰ͋Γɺඇػೳཁ݅Λ࣮ ૷͢ΔͨΊͷઃܭ͕γεςϜΞʔΩςΫνϟͱͳΔɻ ޿ٛʹ͸ɺػೳཁ݅ͱ͸γεςϜ͕ಈ࡞͢Δ಺༰ʹ͍ͭͯఆٛ͠ɺඇػ

    ೳཁ݅ͱ͸γεςϜ͕ಈ࡞͢Δํ๏Λఆٛ͢Δͱݴ͑Δɻ (ུ) ඇػೳཁ݅͸ɺγεςϜͷશମతͳಛੑͱͯ͠ɺ։ൃϓϩδΣΫτ͕੒ ޭ͔ࣦͨ͠ഊ͔ͨ͠Ͳ͏͔ͷࢦඪͱͯ͠΋ར༻͞ΕΔɻ https://ja.wikipedia.org/wiki/ඇػೳཁ݅
  6. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸  http://cloudeo.jp/word/glossary/function_nonfunction.html

  7. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸  http://cloudeo.jp/word/glossary/function_nonfunction.html

  8. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸  ࢲ͸ɺඇػೳͱ͍͏༻ޠ͕޷͖Ͱ͸͋Γ·ͤΜɻ ͜ͷ༻ޠ͕ࣔ͢Ұ෦ͷ໘͸࣮ࡍʹ͸ͱͯ΋ػೳ తʹࢥ͑·͢ɻ ಉ྅ͷҰਓSarah Taraporewalla ͸ɺ୅ΘΓʹ ػೳԣஅཁ݅ʢCFRɿCross-Functional

    Requirementsʣͱ͍͏༻ޠΛ࡞Γग़͠·ͨ͠ɻ ࢲ͸͜ͷදݱͷํ͕ͣͬͱ޷͖Ͱ͢ɻͪ͜Βͷ ํ͕ɺγεςϜͷৼΔ෣͍͸࣮͸ଟ͘ͷԣஅత ͳॲཧͷ݁Ռͱͯ͠ॳΊͯݱΕΔͱ͍͏ࣄ࣮Λ Α͘ද͍ͯ͠·͢ɻ https://www.oreilly.co.jp/books/9784873117607/
  9. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸ w ඇػೳཁ݅ఆٛ͸Ή͔͍ͣ͠ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠ w

    ࠷ߴ଎౓ˠ w ৐ंఆһˠ w αϙʔτɾΞϑλʔέΞˠ w ౪೉ରࡦˠ w ೩අˠ w FUDˠ 
  10. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸ w ඇػೳཁ݅ఆٛ͸Ή͔͍ͣ͠ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠ͍ͭͰ΋޷͖ͳͱ͖ʹ৐Γ͍ͨʂ w

    ࠷ߴ଎౓ˠ w ৐ंఆһˠ w αϙʔτɾΞϑλʔέΞˠ w ౪೉ରࡦˠ౪·ΕΔͷ͸ઈରʹࠔΔʂ w ೩අˠ w FUDˠ  ઈରʹৡΕͳ͍ ͱࢥ͍ࠐΜͰ͍Δ  ཁٻ
  11. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸ w ඇػೳཁ݅ఆٛ͸Ή͔͍ͣ͠ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠ͍ͭͰ΋޷͖ͳͱ͖ʹ৐Γ͍ͨʂ w

    ࠷ߴ଎౓ˠLNI w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ w ౪೉ରࡦˠ౪·ΕΔͷ͸ઈରʹࠔΔʂ w ೩අˠ w FUDˠ  ۩ମతͳ Α͏ʹݟ͑Δ ཁٻ
  12. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸ w ඇػೳཁ݅ఆٛ͸Ή͔͍ͣ͠ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠ͍ͭͰ΋޷͖ͳͱ͖ʹ৐Γ͍ͨʂ w

    ࠷ߴ଎౓ˠLNI w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ΋ͪΖΜॆ࣮͍ͯ͠Δ΄͏͕ w ౪೉ରࡦˠ౪·ΕΔͷ͸ઈରʹࠔΔʂ w ೩අˠͦͦ͜͜ྑ͚Ε͹ w FUDˠ  අ༻ରޮՌΛ۩ମతʹ ΠϝʔδͰ͖ͳ͍߲໨
  13. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ͸ w ඇػೳཁ݅ఆٛ͸Ή͔͍ͣ͠ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠ͍ͭͰ΋޷͖ͳͱ͖ʹ৐Γ͍ͨʂ w

    ࠷ߴ଎౓ˠLNI w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ΋ͪΖΜॆ࣮͍ͯ͠Δ΄͏͕ w ౪೉ରࡦˠ౪·ΕΔͷ͸ઈରʹࠔΔʂ w ೩අˠͦͦ͜͜ྑ͚Ε͹ w FUDˠͦͷଞʹݕ౼͢΂͖ࣄ߲͸ʁ  ͦ΋ͦ΋ݕ౼͢΂͖ ߲໨͕Α͘෼͔Βͳ͍
  14. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ  https://www.ipa.go.jp/files/000005076.pdf

  15. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ 

  16. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ 

  17. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ 

  18. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ 

  19. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ 

  20. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ 

  21. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ w ϞσϧγεςϜΛબΜͰ֤߲໨ͷϨϕϧΛௐઅ͢Δ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠ͍ͭͰ΋޷͖ͳͱ͖ʹ৐Γ͍ͨʂ w

    ࠷ߴ଎౓ˠLNI w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ΋ͪΖΜॆ࣮͍ͯ͠Δ΄͏͕ w ౪೉ରࡦˠ౪·ΕΔͷ͸ઈରʹࠔΔʂ w ೩අˠͦͦ͜͜ྑ͚Ε͹ w FUDˠͦͷଞʹݕ౼͢΂͖ࣄ߲͸ʁ 
  22. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ w ϞσϧγεςϜΛબΜͰ֤߲໨ͷϨϕϧΛௐઅ͢Δ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠฏ೔னؒ ʙ

    ͸ར༻͠ͳ͍ w ࠷ߴ଎౓ˠLNI w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ΋ͪΖΜॆ࣮͍ͯ͠Δ΄͏͕ w ౪೉ରࡦˠ԰಺றं৔ʴϋϯυϧϩοΫ w ೩අˠͦͦ͜͜ྑ͚Ε͹ w FUDˠͦͷଞʹݕ౼͢΂͖ࣄ߲͸ʁ  ݱ࣮తͳϨϕϧʹ ௐઅͨ͠ཁٻ
  23. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ w ϞσϧγεςϜΛબΜͰ֤߲໨ͷϨϕϧΛௐઅ͢Δ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠฏ೔னؒ ʙ

    ͸ར༻͠ͳ͍ w ࠷ߴ଎౓ˠLNI ۓٸ࣌ͷΈLNI  w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ΋ͪΖΜॆ࣮͍ͯ͠Δ΄͏͕ w ౪೉ରࡦˠ԰಺றं৔ʴϋϯυϧϩοΫ w ೩අˠͦͦ͜͜ྑ͚Ε͹ w FUDˠͦͷଞʹݕ౼͢΂͖ࣄ߲͸ʁ  ݕ౼ͷ݁Ռɺௐઅͨ͠ཁٻ
  24. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ w ϞσϧγεςϜΛબΜͰ֤߲໨ͷϨϕϧΛௐઅ͢Δ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠฏ೔னؒ ʙ

    ͸ར༻͠ͳ͍ w ࠷ߴ଎౓ˠLNI ۓٸ࣌ͷΈLNI  w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠ΋ͪΖΜॆ࣮͍ͯ͠Δ΄͏͕ w ౪೉ରࡦˠ԰಺றं৔ʴϋϯυϧϩοΫ w ೩අˠͦͦ͜͜ྑ͚Ε͹ w FUDˠͦͷଞʹݕ౼͢΂͖ࣄ߲͸ʁ  ݕ౼ͷ݁Ռɺௐઅͷඞཁ͕ ແ͔ͬͨཁٻ
  25. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ w ϞσϧγεςϜΛબΜͰ֤߲໨ͷϨϕϧΛௐઅ͢Δ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠฏ೔னؒ ʙ

    ͸ར༻͠ͳ͍ w ࠷ߴ଎౓ˠLNI ۓٸ࣌ͷΈLNI  w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠֹ݄̋ઍԁͷอݥʹՃೖ w ౪೉ରࡦˠ԰಺றं৔ʴϋϯυϧϩοΫ w ೩අˠLNὙҎ্ w FUDˠͦͷଞʹݕ౼͢΂͖ࣄ߲͸ʁ  අ༻ରޮՌΛ۩ମతʹ Πϝʔδܾͯ͠ఆͨ͠ཁٻ
  26. ++6($$$4QSJOHDDD@H ඇػೳཁٻάϨʔυ w ϞσϧγεςϜΛબΜͰ֤߲໨ͷϨϕϧΛௐઅ͢Δ w ྫ ࣗಈंͷߪೖΛߟ͑Δ w ར༻࣌ؒˠฏ೔னؒ ʙ

    ͸ར༻͠ͳ͍ w ࠷ߴ଎౓ˠLNI ۓٸ࣌ͷΈLNI  w ৐ंఆһˠਓ w αϙʔτɾΞϑλʔέΞˠֹ݄̋ઍԁͷอݥʹՃೖ w ౪೉ରࡦˠ԰಺றं৔ʴϋϯυϧϩοΫ w ೩අˠLNὙҎ্ w ೩ྉλϯΫ༰ྔˠὙҎ্ w ഉؾྔˠ DDʙ DD  ౰ॳ͸ݕ౼͔Β࿙Ε͍ͯͨ ߲໨
  27. ++6($$$4QSJOHDDD@H w ඇػೳཁ߲݅໨Λͭͷେ߲໨ɺͷத߲໨ʹ෼ྨ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ

    ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ ඇػೳཁٻάϨʔυ
  28. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ4QSJOH#PPU w ඇػೳཁ݅ͷҰ෦͸ϑϨʔϜϫʔΫ΍ϥΠϒϥϦͰରԠͰ͖Δ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Cloud (ຊηογϣϯର৅֎)
  29. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ4QSJOH#PPU w ඇػೳཁ݅ͷҰ෦͸ϑϨʔϜϫʔΫ΍ϥΠϒϥϦͰରԠͰ͖Δ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Boot Actuator
  30. ++6($$$4QSJOHDDD@H ඇػೳཁ݅ͱ4QSJOH#PPU w ඇػೳཁ݅ͷҰ෦͸ϑϨʔϜϫʔΫ΍ϥΠϒϥϦͰରԠͰ͖Δ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Security & Others
  31. ++6($$$4QSJOHDDD@H Spring Boot Actuator  (͜͜·Ͱ10෼ͳΒΑ͍ϖʔε)

  32. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPS w ӡ༻ɾอकੑʹؔ͢Δཁ݅ͱ4QSJOH#PPU"DUVBUPS  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ w ӡ༻࣌ؒ w όοΫΞοϓ w ӡ༻؂ࢹ w ࣌ࠁಉظ Spring Boot Actuator w ܭըఀࢭ w ӡ༻ෛՙܰݮ w ύονద༻ϙϦγʔ w ׆ੑอक w ఆظอकස౓ w ༧๷อकϨϕϧ
  33. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPS w ӡ༻ɾอकੑʹؔ͢Δཁ݅ͱ4QSJOH#PPU"DUVBUPS  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ w ӡ༻࣌ؒ w όοΫΞοϓ w ӡ༻؂ࢹ w ࣌ࠁಉظ w ܭըఀࢭ w ӡ༻ෛՙܰݮ w ύονద༻ϙϦγʔ w ׆ੑอक w ఆظอकස౓ w ༧๷อकϨϕϧ Spring Boot Actuator
  34. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPSͱ͸ w l1SPEVDUJPOSFBEZGFBUVSFTz w )551·ͨ͸+.9ͰΞϓϦέʔγϣϯΛϞχλϦϯάɺ؅ཧ w 4QSJOH#PPU͔Β44)͸ඇਪ঑ w 4QSJOH.7$্Ͱಈ࡞

    w ؆୯Πϯετʔϧ w ߴ͍ΧελϚΠζੑͱ֦ுੑ 
  35. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPSͱ͸ w Πϯετʔϧ͸ґଘੑΛ௥Ճ͢Δ͚ͩ  <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> EndpointHandlerMapping

    : Mapped "{[/beans || /beans.json],methods=[GET],produces=[a EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[GET],produces=[appl EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[POST],consumes=[app EndpointHandlerMapping : Mapped "{[/loggers || /loggers.json],methods=[GET],produce EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[appl EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produce EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[app EndpointHandlerMapping : Mapped "{[/auditevents || /auditevents.json],methods=[GET] EndpointHandlerMapping : Mapped "{[/health || /health.json],methods=[GET],produces= EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[app EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[applicat EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[appli EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produ EndpointHandlerMapping : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produ EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET] EndpointHandlerMapping : Mapped "{[/trace || /trace.json],methods=[GET],produces=[a EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],p ΤϯυϙΠϯτ͕ ଟ਺௥Ճ͞ΕΔ
  36. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPSͱ͸  ௥Ճ͞ΕͨΤϯυϙΠϯτʹ )551ͰΞΫηε͢Δͱ ֤छ৘ใΛ+40/ͰऔಘͰ͖Δ

  37. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPSͱ͸  +40/7JFXϓϥάΠϯͳͲͰ ੔ܗͯ͠ݕࡧ͢Δͱݟқ͍

  38. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPSͱ͸  $ curl localhost:8080/configprops \ > | jq

    .dataSource.properties.poolProperties { "dbProperties": { "user": "sa", "password": "******" }, "url": “jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON... "driverClassName": "org.h2.Driver", "defaultAutoCommit": null, "defaultReadOnly": null, "defaultTransactionIsolation": -1, "defaultCatalog": null, "connectionProperties": null, "initialSize": 10, "maxActive": 100, "maxIdle": 100, "maxWait": 3000, ... DVSMͰΞΫηεͯ͠KRͰ੔ܗɺ ϑΟϧλͯ͠΋ྑ͍
  39. ++6($$$4QSJOHDDD@H ઃఆ w "DUVBUPSશମͷઃఆ w BQQMJDBUJPOQSPQFSUJFTʹlNBOBHFNFOU zͰઃఆ  # ઀ଓΛड͚෇͚ΔΞυϨεͱϙʔτ൪߸

    management.address=127.0.0.1 management.port=9999 # Actuatorͷϧʔτύε management.context-path=/ops # ActuatorͷηΩϡϦςΟઃఆΛແޮʹ͢Δ # ΞυϨε΍ϙʔτ൪߸ͰηΩϡϦςΟ͕֬อͰ͖͍ͯΔ৔߹ͷΈઃఆ͢Δ͜ͱ management.security.enabled=false # ActuatorʹΞΫηεͰ͖Δϩʔϧ # Spring Boot 1.5͔ΒσϑΥϧτ͸'ACTUATOR'ʹมߋ #management.security.roles=ADMIN
  40. ++6($$$4QSJOHDDD@H ઃఆ w ΤϯυϙΠϯτ͝ͱͷઃఆ w BQQMJDBUJPOQSPQFSUJFTʹlFOEQPJOUT\JE^ zͰઃఆ  # ࢦఆͨ͠ΤϯυϙΠϯτΛ༗ޮ/ແޮʹ͢Δ

    endpoints.shutdown.enabled=true # ࢦఆͨ͠ΤϯυϙΠϯτΛೝূཁ/ෆཁʹ͢Δ endpoints.health.sensitive=false # ࢦఆͨ͠ΤϯυϙΠϯτͷIDΛมߋ͢Δ endpoints.dump.id=threaddump # idΛলུͯ͢͠΂ͯͷΤϯυϙΠϯτΛҰׅઃఆ͢Δ͜ͱ΋Ͱ͖Δ #endpoints.enabled=false
  41. ++6($$$4QSJOHDDD@H &OEQPJOUT 

  42. ++6($$$4QSJOHDDD@H &OEQPJOUT  ຊηογϣϯͷઆ໌ର৅

  43. ++6($$$4QSJOHDDD@H BVEJUFWFOUT w 4QSJOH4FDVSJUZ͕ൃߦͨ͠ೝূɾೝՄΠϕϯτ w ϝϞϦʹ஝ੵ͞ΕͨΠϕϯτΛશ݅දࣔ  $ curl localhost:9999/ops/auditevents

    | jq '.events[0]' { "timestamp": "2017-05-07T14:24:01+0000", "principal": "anonymousUser", "type": "AUTHORIZATION_FAILURE", "data": { "details": { "remoteAddress": "0:0:0:0:0:0:0:1", "sessionId": null }, "type": “org.springframework.security.access.AccessDeniedException", "message": "Access is denied" } }
  44. ++6($$$4QSJOHDDD@H BVEJUFWFOUT w ϦΫΤετύϥϝʔλͰϑΟϧλϦϯά w QSJODJQBM ೝূϢʔβ  w BGUFS

    Πϕϯτൃੜ೔࣌  w UZQF Πϕϯτछผ  $ curl http://localhost:9999/ops/auditevents\ > \?principal=admin\ > \&after=2017-05-07T12%3A00%3A00%2B0000\ > \&type=AUTHENTICATION_FAILURE | jq '.events[0]' { "timestamp": "2017-05-07T14:24:13+0000", "principal": "admin", "type": "AUTHENTICATION_FAILURE", ... } bBENJO`Ϣʔβ͕ Ҏ߱ʹ ೝূࣦഊͨ͠ΠϕϯτΛநग़
  45. ++6($$$4QSJOHDDD@H IFBMUI w ΞϓϦέʔγϣϯ΍ϛυϧ΢ΣΞͷࢮ׆؂ࢹ w σϑΥϧτͰ֤छ3%#.4 3BCCJU.2 3FEJTͳͲʹରԠ w ಠࣗʹ؂ࢹ߲໨Λ௥Ճ͢Δ͜ͱ΋Ͱ͖Δ

     $ curl localhost:9999/ops/health | jq . { "status": "UP", "diskSpace": { "status": "UP", "total": 250140434432, "free": 26348904448, "threshold": 10485760 }, "db": { "status": "UP", "database": "H2", "hello": 1 } } ࢮ׆؂ࢹํ๏͸੡඼ґଘͰɺ )ͷ৔߹͸l4&-&$5zΛ ౤͛Δ σϑΥϧτ͸TFOTJUJWFUSVFͳͷͰ ະೝূঢ়ଶͰ͸ΞϓϦέʔγϣϯ TUBUVTͷΈදࣔ͞ΕΔ
  46. ++6($$$4QSJOHDDD@H JOGP w σϑΥϧτ͸Կ΋දࣔ͞Εͳ͍ w BQQMJDBUJPOQSPQFSUJFTʹlJOGP z͕͋Ε͹දࣔ w l!!ͰQPNYNMͷ಺༰ΛࢦఆͰ͖Δ w

    l\^ͰγεςϜϓϩύςΟͷ಺༰ΛࢦఆͰ͖Δ  info.app.name=@project.name@ info.app.version=@project.version@ info.app.os_name=${os.name} info.app.misc=xxx $ curl localhost:9999/ops/info | jq '.app' { "version": "0.0.1-SNAPSHOT", "name": "ccc-g1", “os_name”: "Mac OS X", "misc": "xxx" }
  47. ++6($$$4QSJOHDDD@H JOGP w HJUQSPQFSUJFT͕͋Ε͹දࣔ w HJUDPNNJUJEQMVHJOΛQPNYNMʹ௥Ճ͢Δ  <build><plugins> <plugin> <groupId>pl.project13.maven</groupId>

    <artifactId>git-commit-id-plugin</artifactId> </plugin> </plugins></build> $ curl localhost:9999/ops/info | jq '.git' { "commit": { "time": 1493972299000, "id": "b2c28ef" }, "branch": "master" } ΞϓϦέʔγϣϯͷϒϥϯν΍ ίϛοτ*%͕දࣔ͞ΕΔ
  48. ++6($$$4QSJOHDDD@H JOGP w .&5"*/'CVJMEJOGPQSPQFSUJFT͕͋Ε͹දࣔ w TQSJOHCPPUCVJMEJOGPΰʔϧΛ࣮ߦ͢Δ  $ curl localhost:9999/ops/info

    | jq '.build' { "version": "0.0.1-SNAPSHOT", "artifact": "ccc-g1", "name": "ccc-g1", "group": "com.example", "time": 1494213456000 } $ mvn spring-boot:build-info (snip) $ ls target/classes/META-INF build-info.properties ΞϓϦέʔγϣϯͷόʔδϣϯ΍ Ϗϧυ͕ͨ࣌ؒ͠දࣔ͞ΕΔ
  49. ++6($$$4QSJOHDDD@H MPHHFST w ϩάϨϕϧΛ֬ೝͰ͖Δ w l\OBNF^zͰ໊લΛࢦఆͯ֬͠ೝ͢Δ͜ͱ΋Ͱ͖Δ  $ curl localhost:9999/ops/loggers/\

    > org.springframework.security | jq . { "configuredLevel": null, "effectiveLevel": "INFO" } $ curl localhost:9999/ops/loggers | jq '.loggers' { "ROOT": { "configuredLevel": "INFO", "effectiveLevel": "INFO" }, ... } 4QSJOH4FDVSJUZͷ ϩάϨϕϧ͸*/'0
  50. ++6($$$4QSJOHDDD@H MPHHFST w 4QSJOH#PPU͔Βӡ༻தʹϩάϨϕϧͷมߋ͕Մೳʹʂ  $ curl -X POST \

    > -H 'Content-Type: application/json' \ > -d '{"configuredLevel":"DEBUG"}' \ > localhost:9999/ops/loggers/\ > org.springframework.security ... o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHo o.s.security.web.FilterChainProxy : / at position 10 of 13 in a o.s.security.web.FilterChainProxy : / at position 11 of 13 in a o.s.security.web.FilterChainProxy : / at position 12 of 13 in a o.s.security.web.FilterChainProxy : / at position 13 of 13 in a o.s.s.w.u.matcher.AntPathRequestMatcher : Request 'GET /' doesn't mat o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : o.s.s.w.u.matcher.AntPathRequestMatcher : Checking match of request : o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvoca o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: o o.s.s.access.vote.AffirmativeBased : Voter: org.springframework. o.s.s.w.a.ExceptionTranslationFilter : Access is denied (user is a ... 4QSJOH4FDVSJUZͷϩάϨϕϧΛ %&#6(ʹมߋ͢Δͱେྔʹϩά͕ ग़ྗ͞Εͯศར MPHHFS໊Λࢦఆͯ͠ ϩάϨϕϧΛ+40/Ͱ 1045͢Δ
  51. ++6($$$4QSJOHDDD@H NFUSJDT w $16ɺϝϞϦɺεϨουͳͲͷϝτϦΫε৘ใ  $ curl localhost:9999/ops/metrics | jq

    . { "mem": 463639, "mem.free": 283297, "processors": 2, "instance.uptime": 1234698, "uptime": 1252089, "systemload.average": 5.4296875, "heap.committed": 385024, "heap.init": 65536, "heap.used": 101726, "heap": 932352, "nonheap.committed": 87360, "nonheap.init": 2496, "nonheap.used": 84960, ... }
  52. ++6($$$4QSJOHDDD@H NFUSJDT w &YQPSUFSΛ࣮૷ͯ͠3FEJT΍4UBUT%ʹΤΫεϙʔτͰ͖Δ w σϑΥϧτͰ.#FBO&YQPSUFS͕༗ޮ 

  53. ++6($$$4QSJOHDDD@H USBDF w ௚ۙ݅ͷΞΫηεϩά  $ curl localhost:9999/ops/trace | jq

    '.[0]' { "timestamp": 1494175457526, "info": { "method": "GET", "path": "/", "headers": { "request": { "host": “localhost:8080", ... }, "response": { ... "status": "200" } } } }
  54. ++6($$$4QSJOHDDD@H ͦͷଞ w ։ൃ޻ఔ w BVUPDPOpHˠ4QSJOH#PPU͕ࣗಈతʹߦͬͨઃఆ৘ใ w CFBOTˠ%*ίϯςφʹొ࿥͞Εͨ#FBOͷҰཡ w QSPQDPOpHTˠΞϓϦέʔγϣϯͷઃఆ৘ใ

    w ӡ༻ɾอक޻ఔ w EVNQˠεϨουμϯϓ w IFBQEVNQˠώʔϓμϯϓΛμ΢ϯϩʔυͰ͖Δ w MPHpMFˠϩάϑΝΠϧΛμ΢ϯϩʔυͰ͖Δ w NBQQJOHTˠϦΫΤετϚοϐϯάҰཡ  (͜͜·Ͱ20෼ͳΒΑ͍ϖʔε)
  55. ++6($$$4QSJOHDDD@H &OEQPJOUΛ௥Ճ͢Δ w ಠࣗͷΤϯυϙΠϯτΛ௥Ճ͢Δ  // package and imports... @Component

    public class CustomEndpoint implements Endpoint<List<String>> { @Override public String getId() { return "custom"; } @Override public boolean isEnabled() { return true; } @Override public boolean isSensitive() { return true; } @Override public List<String> invoke() { return Arrays.asList("one", "two", "three"); } } &OEQPJOU5Λ࣮૷͢Δ μϛʔͷ࣮૷ྫɻ ࣮ࡍ͸%*ίϯςφ΍σʔλϕʔε ͔Β஋Λऔಘͯ͠ฦ͢
  56. ++6($$$4QSJOHDDD@H &OEQPJOUΛ௥Ճ͢Δ w ࣗಈతʹݕग़͞ΕͯΤϯυϙΠϯτʹ௥Ճ͞ΕΔ  $ curl localhost:9999/ops/custom | jq

    . [ "one", "two", "three" ] ... EndpointHandlerMapping : Mapped “{[/ops/loggers/{name:.*}],methods EndpointHandlerMapping : Mapped “{[/ops/loggers/{name:.*}],methods EndpointHandlerMapping : Mapped “{[/ops/loggers || /loggers.json], EndpointHandlerMapping : Mapped “{[/ops/custom || /custom.json],me EndpointHandlerMapping : Mapped “{[/ops/trace || /trace.json],meth EndpointHandlerMapping : Mapped “{[/ops/actuator || /actuator.json EndpointHandlerMapping : Mapped “{[/ops/heapdump || /heapdump.json ...
  57. ++6($$$4QSJOHDDD@H &OEQPJOUΛ֦ு͢Δ w طଘͷΤϯυϙΠϯτΛ֦ு͢Δ  // package and imports... @Component

    public class MappingsMvcEndpoint extends EndpointMvcAdapter { private final RequestMappingEndpoint delegate; public MappingsMvcEndpoint( RequestMappingEndpoint delegate) { super(delegate); this.delegate = delegate; } @GetMapping("html") public ModelAndView html() { Map<String, Object> result = delegate.invoke(); return new ModelAndView("mappings", "result", result); } } طଘͷΤϯυϙΠϯτʹॲཧΛ ҕৡ͠ɺ݁ՌΛՃ޻ɾදࣔ͢Δ &OEQPJOU.WD"EBQUFSΛ ܧঝ͢Δ
  58. ++6($$$4QSJOHDDD@H &OEQPJOUΛ֦ு͢Δ 

  59. ++6($$$4QSJOHDDD@H &OEQPJOUΛ֦ு͢Δ w ΤΫηϧΤϯυϙΠϯτ  <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.16</version> </dependency>

    @GetMapping("xlsx") public ModelAndView xlsx() { return new ModelAndView(new AbstractXlsxView() { @Override protected void buildExcelDocument( Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { // (snip) } }); } PSHTQSJOHGSBNFXPSLXFCTFSWMFU WJFXEPDVNFOU"CTUSBDU9MTY7JFX Λ࡞ͬͯฦ͢
  60. ++6($$$4QSJOHDDD@H &OEQPJOUΛ֦ு͢Δ 

  61. ++6($$$4QSJOHDDD@H 4QSJOH#PPU"DUVBUPS·ͱΊ w l1SPEVDUJPOSFBEZGFBUVSFTz w )551·ͨ͸+.9ͰΞϓϦέʔγϣϯΛϞχλϦϯάɺ؅ཧ w ؆қͳӡ༻ɾอकʹ࠷ద w ։ൃʹ΋༗༻

    w ؆୯Πϯετʔϧ w ґଘੑΛ௥Ճ͢Δ͚ͩ w ߴ͍ΧελϚΠζੑͱ֦ுੑ w BQQMJDBUJPOQSPQFSUJFTͰॊೈʹઃఆ w ΤϯυϙΠϯτͷ௥Ճɺ֦ு΋Մೳ 
  62. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ 0UIFST  (͜͜·Ͱ25෼ͳΒΑ͍ϖʔε) (ਫΛҿΉ)

  63. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST w ηΩϡϦςΟʹؔ͢Δཁ݅ͱ4QSJOH4FDVSJUZͦͷଞ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Security & Others
  64. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST w ΞΫηεɾར༻੍ݶ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Security & Others w ೝূػೳ w ར༻੍ݶ w ؅ཧํ๏
  65. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST w ΞΫηεɾར༻੍ݶ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Security & Others w ೝূػೳ w ύεϫʔυॳظԽ w ύεϫʔυϙϦγʔ w ύεϫʔυޡೖྗ๷ࢭ w ύεϫʔυ༗ޮظݶ w ύεϫʔυੈ୅؅ཧ w ΞΧ΢ϯτϩοΫ w ΞΧ΢ϯτແޮԽ w FUD w ར༻੍ݶ w ؅ཧํ๏ ຊηογϣϯͷઆ໌ൣғ
  66. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυϙϦγʔ w lจࣈҎ্ɺେখ਺ࣈΛͦΕͧΕจࣈҎ্zΈ͍ͨͳΞϨ  http://www.passay.org/

  67. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυϙϦγʔ w ґଘੑΛ௥Ճͯ͠ΞϊςʔγϣϯΛ࡞Δ  <dependency> <groupId>org.passay</groupId> <artifactId>passay</artifactId> <version>1.2.0</version>

    </dependency> // package and imports... @Documented @Retention(RUNTIME) @Target(FIELD) @Constraint(validatedBy = PasswordConstraintValidator.class) public @interface ValidPassword { String message() default "Invalid Password"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ۩ମతͳݕূॲཧ͸1BTTBZͰ ࣮૷͢Δ
  68. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυϙϦγʔ w ۩ମతͳݕূॲཧ͸ϧʔϧΛ૊Έ߹Θͤͯ࡞Δ  // package and imports...

    import static org.passay.EnglishCharacterData.*; public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> { // (snip) @Override public boolean isValid(String password, ConstraintValidatorContext context) { PasswordValidator validator = new PasswordValidator(Arrays.asList( new LengthRule(8), new CharacterRule(UpperCase, 1), new CharacterRule(LowerCase, 1), new CharacterRule(Digit, 1))); RuleResult result = validator.validate(new PasswordData(password)); // (snip) } จࣈҎ্ɺ େจࣈɺখจࣈɺ਺ࣈ ͦΕͧΕจࣈҎ্
  69. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυϙϦγʔ w ϑΥʔϜʹద༻ͯ͠ίϯτϩʔϥͰར༻͢Δ  // package and imports...

    public class PasswordForm { @ValidPassword private String password; // getters/setters... } ϙϦγʔΛద༻͍ͨ͠ϑΟʔϧυΛ ࡞੒ͨ͠ΞϊςʔγϣϯͰ஫ऍ͢Δ // package and imports... @Controller public class PasswordController { // (snip) @PostMapping("update") String update(@Validated PasswordForm form, BindingResult result) { if (result.hasErrors()) { ... } // (snip) } } l!7BMJEBUFEzͰೖྗ஋ͷଥ౰ੑΛ ݕূ͢Δ
  70. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυϙϦγʔ w εΫϦʔϯγϣοτషΔ 

  71. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυޡೖྗ๷ࢭ w l֬ೝ༻ύεϫʔυ͕Ұக͠·ͤΜz͍ͬͯ͏ΞϨ w ૬ؔνΣοΫ༻ΞϊςʔγϣϯΛ5&3"40-6/" ˞ ͕ఏڙ w

    ͔Β.BWFO$FOUSBMʹΞοϓϩʔυ͞Ε͍ͯΔͷͰɹ ґଘੑΛ௥Ճ͢Δ͚ͩͰར༻Ͱ͖Δ  <dependency> <groupId>org.terasoluna.gfw</groupId> <artifactId>terasoluna-gfw-validator</artifactId> <version>5.3.0.RELEASE</version> </dependency> ※ TERASOLUNAʢςϥιϧφʣ͸ɺNTTσʔλ͕ఏڙ͢ΔʮϑϨʔϜϫʔΫʯɺʮ։ൃϓϩηεʯɺʮϓϩ δΣΫτ؅ཧʯͳͲͷٕज़΍ϊ΢ϋ΢Λ૊߹ͤɺ γεςϜ։ൃΛแׅతʹαϙʔτ͢ΔιϦϡʔγϣϯ http://terasolunaorg.github.io
  72. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυޡೖྗ๷ࢭ w ΫϥεΛl!$PNQBSFzͰ஫ऍ͢Δ w lMFGUz lSJHIUzʹ૬ؔνΣοΫ͢Δ߲໨໊Λࢦఆ͢Δ w lPQFSBUPSzʹνΣοΫ಺༰Λࢦఆ͢Δ

    w lNFTTBHFzʹΤϥʔϝοηʔδΛࢦఆ͢Δ  // package and imports... import org.terasoluna.gfw.common.validator.constraints.Compare; @Compare(left = "password", right = “password2", operator = Compare.Operator.EQUAL, message = "passwords do not match!”) public class PasswordForm { @ValidPassword private String password; private String password2; // getters/setters... }
  73. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ύεϫʔυޡೖྗ๷ࢭ 

  74. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ΞΧ΢ϯτϩοΫ w lೝূࣦഊ࿈ଓճͰΞΧ΢ϯτϩοΫzΈ͍ͨͳΞϨ w 4QSJOH'SBNFXPSL͸l!&WFOU-JTUFOFSzͰ༷ʑͳΠϕϯτΛ ϋϯυϦϯάͰ͖Δ w 4QSJOH4FDVSJUZͷΞΧ΢ϯτʹ͸ҎԼͷঢ়ଶΛઃఆͰ͖Δɻ

    GBMTFͷ৔߹͸ೝূʹࣦഊ͢Δ w ΞΧ΢ϯτ͸༗ޮظݶ͕੾Ε͍ͯͳ͍͔ w ΞΧ΢ϯτ͸ϩοΫ͞Ε͍ͯͳ͍͔ w ύεϫʔυ͸༗ޮظݶ͕੾Ε͍ͯͳ͍͔ w ΞΧ΢ϯτ͸༗ޮ͔ 
  75. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ΞΧ΢ϯτϩοΫ w ೝূࣦഊճ਺Λ࣋ͭΞΧ΢ϯτͱ໊લͰݕࡧͰ͖ΔϦϙδτϦ  // package and imports...

    @Entity public class Account { @Id @GeneratedValue(strategy = AUTO) private Integer id; private String username; private String password; private int loginFailureTimes; private String authorities; // other fields, getters/setters... } // package and imports... public interface AccountRepository extends JpaRepository<Account, Integer> { Account findByUsername(String username); }
  76. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ΞΧ΢ϯτϩοΫ w ೝূΠϕϯτΛϋϯυϦϯά͢Δ  // package and imports...

    @Component public class AuthenticationEventListeners { // fields and constructor... @EventListener public void handle( AuthenticationFailureBadCredentialsEvent e) { Account account = accountRepository.findByUsername( (String) e.getAuthentication().getPrincipal()); if (account == null) { return; } account.setLoginFailureTimes( account.getLoginFailureTimes() + 1); accountRepository.save(account); } } l!&WFOU-JTUFOFSzͰ஫ऍͯ͠ Ҿ਺ʹࢦఆͨ͠ܕͷΠϕϯτΛ ϋϯυϦϯά͢Δ ೝূࣦഊճ਺Λ ΠϯΫϦϝϯτͯ͠ อଘ͢Δ
  77. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ΞΧ΢ϯτϩοΫ w 6TFS%FUBJMTΛ࣮૷͢Δ  // package and imports...

    public class MyUserDetails extends User { private final Account account; public MyUserDetails(Account account, boolean accountNonLocked) { super(account.getUsername(), account.getPassword(), true, // enabled true, // accountNonExpired true, // credentialsNonExpired accountNonLocked, AuthorityUtils. commaSeparatedStringToAuthorityList( account.getAuthorities())); this.account = account; } } PSHTQSJOHGSBNFXPSL TFDVSJUZDPSFVTFSEFUBJMT 6TFSΛܧঝ͢Δͱศར ਌ΫϥεͷίϯετϥΫλʹ ΞΧ΢ϯτঢ়ଶΛઃఆ͢Δɻ ͜͜Ͱ͸ΞΧ΢ϯτϩοΫɹ Ҏ֎͸શͯUSVF ༗ޮ ʹઃఆ ίϯετϥΫλͰ"DDPVOU ͱΞΧ΢ϯτϩοΫঢ়ଶΛ ड͚Δ
  78. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ΞΧ΢ϯτϩοΫ w 6TFS%FUBJMT4FSWJDFΛ࣮૷͢Δ w %*ίϯςφʹొ࿥͢Ε͹4QSJOH4FDVSJUZʹࣗಈద༻͞ΕΔ  // package

    and imports... @Service public class MyUserDetailsService implements UserDetailsService { // fields and constructor... @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Account account = accountRepository.findByUsername(username); // error handling... boolean accountNonLocked = account.getLoginFailureTimes() < 3; return new MyUserDetails(account, accountNonLocked); } } ೝূࣦഊճ਺্͕ݶΛ௒͍͑ͯΔ͔ ൑ఆͯ͠ίϯετϥΫλʹ౉͢
  79. ++6($$$4QSJOHDDD@H ΞΫηεɾར༻੍ݶ ΞΧ΢ϯτϩοΫ  ΞΧ΢ϯτϩοΫঢ়ଶͷ৔߹͸ ύεϫʔυ͕ਖ਼ͯ͘͠΋ೝূʹ ࣦഊ͢Δ ύεϫʔυ࿈ଓࣦഊճ਺্ݶʹ ຬͨͳ͍৔߹

  80. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST w σʔλͷൿಗ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Security & Others w σʔλ҉߸Խ w ύεϫʔυͷϋογϡԽ w ػີ৘ใͷ҉߸Խ (͜͜·Ͱ30෼ͳΒΑ͍ϖʔε)
  81. ++6($$$4QSJOHDDD@H w 4)" Ͱͳ͘#DSZQU·ͨ͸4DSZQUΛ࢖͏΂͖ w ฏจ͸࿦֎ɺ.%ɺ4)"͸ίϦδϣϯ͕ݟ͔͍ͭͬͯΔ w ͦͷଞͷ4)" ΋ڧ౓ΑΓ଎౓༏ઌ 

    https://rietta.com/blog/2016/02/05/bcrypt-not-sha-for-passwords/ σʔλͷൿಗ ύεϫʔυͷϋογϡԽ
  82. ++6($$$4QSJOHDDD@H σʔλͷൿಗ ύεϫʔυͷϋογϡԽ w 4QSJOH4FDVSJUZ͕#DSZQU 4DSZQU࣮૷Λఏڙ w 1BTTXPSE&ODPEFSͷ࣮૷Λ%*ίϯςφʹొ࿥͢Ε͹ 4QSJOH4FDVSJUZʹࣗಈద༻͞ΕΔ 

    // package and imports... @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } // other configurations... }
  83. ++6($$$4QSJOHDDD@H σʔλͷൿಗ ύεϫʔυͷϋογϡԽ w ೚ҙͷαʔϏεʹΠϯδΣΫγϣϯͯ͠࢖͏  // package and imports...

    @Service public class AccountService { private final PasswordEncoder encoder; public AccountService(PasswordEncoder encoder) { this.encoder = encoder; } public boolean updatePassword(String username, String rawPassword) { String oldPassword = // get oldPassword from database... if (encoder.matches(rawPassword, oldPassword) {...} String encoded = encoder.encode(password); return repository.updatePassword(username, encoded); } } ฏจύεϫʔυͱͷಥ߹΍ϋογϡԽ
  84. ++6($$$4QSJOHDDD@H σʔλͷൿಗ ػີ৘ใͷ҉߸Խ w ֤छ઀ଓ৘ใͳͲΛ ϋογϡԽͰ͸ͳ͘ ҉߸Խ͢Δ  http://www.jasypt.org/

  85. ++6($$$4QSJOHDDD@H σʔλͷൿಗ ػີ৘ใͷ҉߸Խ w KBTZQUTQSJOHCPPUTUBSUFS͕ఏڙ͞Ε͍ͯΔ  https://github.com/ulisesbocchio/jasypt-spring-boot

  86. ++6($$$4QSJOHDDD@H σʔλͷൿಗ ػີ৘ใͷ҉߸Խ w ґଘੑΛ௥Ճͯ͠҉߸Λ࡞Δ  <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>1.12</version>

    </dependency> $ java -cp \ > ~/.m2/repository/org/jasypt/jasypt/1.9.2/jasypt-1.9.2.jar \ > org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI \ > input=sa \ > password=secretkey \ > algorithm=PBEWithMD5AndDES ... ----OUTPUT---------------------- 0L8y9EPEsdYc76viCxUAsQ== ಘΒΕͨ҉߸ )ͷ઀ଓϢʔβ໊Λ҉߸Խ͢Δྫɻ ҉߸Խ͢Δ஋ɺ伴ɺΞϧΰϦζϜΛ ࢦఆͯ͠҉߸ԽॲཧΛ࣮ߦ
  87. ++6($$$4QSJOHDDD@H σʔλͷൿಗ ػີ৘ใͷ҉߸Խ w BQQMJDBUJPOQSPQFSUJFTʹ҉߸ͱݤΛઃఆ͢Δ  # DATASOURCE spring.datasource.username=ENC(0L8y9EPEsdYc76viCxUAsQ==) #

    JASYPT #jasypt.encryptor.password=secretkey jasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD} $ export JASYPT_ENCRYPTOR_PASSWORD=secretkey l&/$  zͰғΜͰෳ߹ର৅Ͱ ͋Δ͜ͱΛࣔ͢ 伴Λ௚઀ઃఆ͢ΔͱηΩϡϦςΟڧ౓తʹ ͋·Γҙຯ͕ͳ͍  ؀ڥม਺ͳͲ͔ΒಡΈࠐΜͰࢦఆ͢Δ
  88. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST w ෆਖ਼௥੻ɾ؂ࢹ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ Spring Security & Others w ෆਖ਼؂ࢹ w τϥοΩϯάϩά w τϨʔεϩά w σʔλϕʔεϩά w σʔλݕূ (͜͜·Ͱ35෼ͳΒΑ͍ϖʔε)
  89. ++6($$$4QSJOHDDD@H ෆਖ਼௥੻ɾ؂ࢹ τϥοΩϯάϩά w ϩάΠϯϢʔβ*%ͱηογϣϯ͝ͱʹҰҙͳτϥοΩϯά*%Λ ϩάग़ྗ͢Δ w ϑΟϧλͱ.%$ ˞ Ͱ࣮ݱ͢ΔػೳΛ5&3"40-6/"͕ఏڙɹ

     <dependency> <groupId>org.terasoluna.gfw</groupId> <artifactId>terasoluna-gfw-security-web</artifactId> <version>5.3.0.RELEASE</version> </dependency> ※ Mapped Diagnostic Context (Ϛοϓ͞Εͨ਍அίϯςΩετ)ɻεϨουϩʔΧϧͳMapΛ಺෦ʹ΋ͪɺΩʔ ʹରͯ͠஋Λput͢Ε͹remove͞ΕΔ·Ͱϩάʹputͨ͠஋Λग़ྗ͢Δ͜ͱ͕Ͱ͖Δ
  90. ++6($$$4QSJOHDDD@H w 4QSJOH4FDVSJUZͷϑΟϧλͰ.%$ʹϢʔβ*%Λઃఆ͢Δ  // package and imports… import org.terasoluna.gfw.security.web.logging.UserIdMDCPutFilter;

    @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Bean public UserIdMDCPutFilter userIdMDCPutFilter() { return new UserIdMDCPutFilter(); } @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterAfter(userIdMDCPutFilter(), AnonymousAuthenticationFilter.class); } } 4QSJOH4FDVSJUZͷ "OPOZNPVT"VUIFOUJDBUJPO 'JMUFSͷޙʹઃఆ͢Δ ෆਖ਼௥੻ɾ؂ࢹ τϥοΩϯάϩά
  91. ++6($$$4QSJOHDDD@H w ϑΟϧλͰ.%$ʹτϥοΩϯά*%Λઃఆ͢Δ w 'JMUFS࣮૷ΫϥεΛ%*ίϯςφʹొ࿥͢Ε͹ࣗಈద༻  // package and imports…

    import org.terasoluna.gfw.web.logging.mdc.MDCClearFilter; import org.terasoluna.gfw.web.logging.mdc.XTrackMDCPutFilter; @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Bean MDCClearFilter mdcClearFilter() { return new MDCClearFilter(); } @Bean XTrackMDCPutFilter xTrackMdcPutFilter() { return new XTrackMDCPutFilter(); } // other settings... } એݴॱʹద༻͞ΕΔͷͰ .%$$MFBS'JMUFSΛઌʹએݴ͢Δ ෆਖ਼௥੻ɾ؂ࢹ τϥοΩϯάϩά
  92. ++6($$$4QSJOHDDD@H w l9\^zͰ.%$ͷ஋Λϩάʹग़ྗ͢Δ w ؆қʹઃఆ͢Δ৔߹͸BQQMJDBUJPOQSPQFSUJFTͰࢦఆ͢Δ w ຊ֨తʹઃఆ͢Δ৔߹͸MPHCBDLTQSJOHYNMΛ༻ҙ͢Δ  logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS}

    %X{X-Track} %X{USER} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%15.15t] %-40.40logger{39} : %m%n 2017-05-10 21:56:23.203 - INFO 85415 --- [nio-8080-exec-1] o.s.web.s 2017-05-10 21:56:23.584 anonymousUser - INFO 85415 --- [nio-8080-exec 2017-05-10 21:56:46.753 ca2210ef6b2a4c3cae2c2d0b8865ecfb admin - INFO 2017-05-10 21:57:10.175 3c5815e5239f4b17bcec65b460ab0d94 admin - INFO 2017-05-10 21:57:11.671 f68c33bed90d4193891da0291ef4e334 admin - INFO 2017-05-10 21:57:12.230 50890f1d81b749a0b881935c2cb456a8 admin - INFO 2017-05-10 21:57:12.407 49ca6af952684adba2654d92a57d4903 admin - INFO 2017-05-10 22:02:07.611 261699113495481e811426a2d618eddb demo - INFO 8 2017-05-10 22:02:10.088 11b3ce0f57a946cfae89a91ad8874b1c demo - INFO 8 2017-05-10 22:02:12.052 567b78ca7e8547a2b986781808d155e1 admin - INFO ෆਖ਼௥੻ɾ؂ࢹ τϥοΩϯάϩά
  93. ++6($$$4QSJOHDDD@H ෆਖ਼௥੻ɾ؂ࢹ τϨʔεϩά w ίϯτϩʔϥͷݺͼग़͠։࢝ɾऴྃͱॲཧ࣌ؒΛϩάग़ྗ͢Δ w ΠϯλʔηϓλͰ࣮ݱ͢ΔػೳΛ5&3"40-6/"͕ఏڙ  // package

    and imports... import org.terasoluna.gfw.web.logging.TraceLoggingInterceptor; @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Bean TraceLoggingInterceptor traceLoggingInterceptor() { return new TraceLoggingInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(traceLoggingInterceptor()) .addPathPatterns("/**") .excludePathPatterns(“/resources/**"); } } SFTPVSDFTΛআ͘શύεʹద༻
  94. ++6($$$4QSJOHDDD@H w ϩάϨϕϧΛUSBDFʹઃఆ  logging.level.org.terasoluna.gfw.web.logging=trace 2017-05-10 22:33:57.278 5bf75eb81f3847759805f8a278973d22 demo -TRACE

    87708 --- [nio-8080-exec-4] o.t.g.w.logging.TraceLoggingInterceptor : [START CONTROLLER] HomeController.home(Locale,LoggedInUser,Model) 2017-05-10 22:33:57.283 5bf75eb81f3847759805f8a278973d22 demo -TRACE 87708 --- [nio-8080-exec-4] o.t.g.w.logging.TraceLoggingInterceptor : [END CONTROLLER ] HomeController.home(Locale,LoggedInUser,Model)-> view=welcome/home, model={account=Account(username=demo, password=$2a $10$oxSJl.keBwxmsMLkcT9lPeAIxfNTPNQxpeywMrF7A3kVszwUTqfTK, roles=[USER]), org.springframework.validation.BindingResult.account=org.springframewo rk.validation.BeanPropertyBindingResult: 0 errors} 2017-05-10 22:33:57.283 5bf75eb81f3847759805f8a278973d22 demo -TRACE 87708 --- [nio-8080-exec-4] o.t.g.w.logging.TraceLoggingInterceptor : [HANDLING TIME ] HomeController.home(Locale,LoggedInUser,Model)-> 4,358,195 ns ηΩϡΞͳ಺༰΋ग़ྗ͞ΕΔͷͰ औΓѻ͍ʹ͸஫ҙ͢Δ ෆਖ਼௥੻ɾ؂ࢹ τϨʔεϩά ։࢝ϩάͱऴྃϩά ॲཧ࣌ؒ
  95. ++6($$$4QSJOHDDD@H ෆਖ਼௥੻ɾ؂ࢹ σʔλϕʔεϩά w 4QSJOH%BUB+1"ʹΑΔ؂ࠪϩά w ొ࿥೔࣌ɺొ࿥ऀɺߋ৽೔࣌ɺߋ৽ऀΛࣗಈతʹه࿥͢Δ  // package

    and imports... @Entity @EntityListeners(AuditingEntityListener.class) public class Account { // @Id, @Version... @CreatedDate private Date createdAt; @CreatedBy private String createdBy; @LastModifiedDate private Date updatedAt; @LastModifiedBy private String updatedBy; // getters/setters... } ΫϥεΛl!&OUJUZ-JTUFOFSzͰ ஫ऍͯ͠ɺl!$SFBUFEʙͱ z!-BTU.PEJpFEʙzͰ஫ऍͨ͠ ϑΟʔϧυΛ௥Ճ͢Δ
  96. ++6($$$4QSJOHDDD@H  w 4QSJOH4FDVSJUZͷೝূ৘ใΛऔಘ͢Δ // package and imports... @Configuration @EnableJpaAuditing

    public class PersistenceConfig { @Bean AuditorAware<String> auditorProvider() { return new AuditorAware<String>() { @Override public String getCurrentAuditor() { Authentication auth = SecurityContextHolder .getContext().getAuthentication(); // return null if the user is not authenticated... return ((User) auth.getPrincipal()).getUsername(); } }; } } +1"؂ࠪΛ༗ޮʹ͢Δ 4QSJOH4FDVSJUZͷೝূ৘ใ͔Β Ϣʔβ໊Λऔಘ͢Δ ෆਖ਼௥੻ɾ؂ࢹ σʔλϕʔεϩά
  97. ++6($$$4QSJOHDDD@H w σʔλొ࿥  $ curl -X POST -u user:password

    \ > -H 'Content-Type: application/json' \ > -d '{"name":"nameA"}' http://localhost:8080/accounts/ { "createdAt" : "2017-05-06T07:44:02.113+0000", "createdBy" : "user", "updatedAt" : "2017-05-06T07:44:02.113+0000", "updatedBy" : "user", "name" : “nameA", ... } ొ࿥೔࣌ɺొ࿥ऀɺߋ৽೔࣌ɺ ߋ৽ऀ͕ࣗಈతʹه࿥͞ΕΔ ෆਖ਼௥੻ɾ؂ࢹ σʔλϕʔεϩά
  98. ++6($$$4QSJOHDDD@H w σʔλߋ৽  $ curl -X PATCH -u user:password

    \ > -H 'Content-Type: application/json' \ > -d '{"name":"nameB"}' http://localhost:8080/accounts/1 { "createdAt" : "2017-05-06T07:44:02.113+0000", "createdBy" : "user", "updatedAt" : "2017-05-06T08:07:53.632+0000", "updatedBy" : "user", "name" : “nameB", ... } ߋ৽೔࣌ɺߋ৽ऀ͕ࣗಈతʹ ه࿥͞ΕΔ ෆਖ਼௥੻ɾ؂ࢹ σʔλϕʔεϩά
  99. ++6($$$4QSJOHDDD@H w ਌Ϋϥεʹू໿͢Δͱศར  // package and imports... @MappedSuperclass @EntityListeners(AuditingEntityListener.class)

    public abstract class AbstractEntity { // @Id, @Version... @CreatedDate protected Date createdAt; @CreatedBy protected String createdBy; @LastModifiedDate protected Date updatedAt; @LastModifiedBy protected String updatedBy; // getters/setters... } // package and imports... @Entity public class Account extends AbstractEntity { // other fields, getters/setters... } ΤϯςΟςΟͷ਌Ϋϥε͸ l!.BQQFE4VQFSDMBTTzͰ஫ऍ͢Δ ΤϯςΟςΟ͸਌ΫϥεΛܧঝ͢Δ ෆਖ਼௥੻ɾ؂ࢹ σʔλϕʔεϩά
  100. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST w 8FCରࡦ  େ߲໨ த߲໨ Մ༻ੑ ܧଓੑɺ଱ো֐ੑɺࡂ֐ରࡦɺճ෮ੑ ੑೳɾ֦ுੑ

    ۀ຿ॲཧྔɺੑೳ໨ඪ஋ɺϦιʔε֦ுੑɺੑೳ඼࣭อূ ӡ༻ɾอकੑ ௨ৗӡ༻ɺอकӡ༻ɺো֐࣌ӡ༻ɺӡ༻؀ڥɺαϙʔτମ੍ɺͦͷଞӡ༻ ؅ཧํ਑ Ҡߦੑ Ҡߦ࣌ظɺҠߦํࣜɺҠߦର৅ʢػثʣɺҠߦର৅ʢσʔλʣɺҠߦܭը ηΩϡϦςΟ લఏ৚݅ɾ੍໿৚݅ɺηΩϡϦςΟϦεΫରԠɺηΩϡϦςΟ਍அɺ ηΩϡϦςΟϦεΫ؅ཧɺΞΫηεɾར༻੍ݶɺσʔλͷൿಗɺ ෆਖ਼௥੻ɾ؂ࢹɺωοτϫʔΫରࡦɺϚϧ΢ΣΞରࡦɺWebରࡦ ؀ڥɾ Τίϩδʔ γεςϜ੍໿/લఏ৚݅ɺγεςϜಛੑɺద߹ن֨ɺػࡐઃஔ؀ڥ৚݅ɺ ؀ڥϚωδϝϯτ w 8FC࣮૷ରࡦ w ηογϣϯ؅ཧ w 944ରࡦ w $43'ରࡦ w ηΩϡϦςΟϔομ Spring Security & Others (͜͜·Ͱ40෼ͳΒΑ͍ϖʔε)
  101. ++6($$$4QSJOHDDD@H 8FCରࡦ ηογϣϯ؅ཧ w ηογϣϯݻఆ߈ܸରࡦ  c c ϩάΠϯʹ੒ޭ͢Δͱ৽͍͠ ηογϣϯ*%͕෷͍ग़͞ΕΔ

  102. ++6($$$4QSJOHDDD@H 8FCରࡦ 944ରࡦ w 5IZNFMFBG͸σϑΥϧτͰಛघจࣈΛΤεέʔϓ͢Δ w SBXͰग़ྗ͍ͨ͠৔߹͸lUIVUFYU\^zͰ໌ࣔ͢Δ  // (snip)

    @GetMapping("xss") String xss(Model model) { model.addAttribute(“script", "<script>alert('Hello, Xss!');</script>"); model.addAttribute(“marquee", "<marquee>Hello, Xss!</marquee>"); return "xss"; // (snip) <!—- snip —-> <h1>xss</h1> <div th:text=“${script}"></div> <div th:utext="${marquee}"></div> <!—- snip —->
  103. ++6($$$4QSJOHDDD@H 8FCରࡦ 944ରࡦ  c c

  104. ++6($$$4QSJOHDDD@H 8FCରࡦ $43'ରࡦ w σϑΥϧτͰ$43'τʔΫϯ͕IJEEFOʹग़ྗ͞ΕΔ  c

  105. ++6($$$4QSJOHDDD@H 8FCରࡦ $43'ରࡦ w 4QSJOH4FDVSJUZ͔Β$PPLJFʹอଘ͢Δ͜ͱ΋Ͱ͖Δ  // package and imports...

    @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().csrfTokenRepository( new CookieCsrfTokenRepository()) } } "OHVMBSͷ࢓༷ʹ߹Θͤͯ l943'50,&/zͷ໊લͰग़ྗ
  106. ++6($$$4QSJOHDDD@H 8FCରࡦ ηΩϡϦςΟϔομ w σϑΥϧτͰଟ਺ͷηΩϡϦςΟϔομ͕ઃఆ͞ΕΔ  c c c

  107. ++6($$$4QSJOHDDD@H 4QSJOH4FDVSJUZ0UIFST·ͱΊ w ΞΫηεɾར༻੍ݶ w 4QSJOH4FDVSJUZ 1BTTBZ 5&3"40-6/" w σʔλͷൿಗ

    w 4QSJOH4FDVSJUZ KBTZQU w ෆਖ਼௥੻ɾ؂ࢹ w 4QSJOH4FDVSJUZ +1" 5&3"40-6/" w 8FCରࡦ w 4QSJOH4FDVSJUZ 5IZNFMFBG 
  108. ++6($$$4QSJOHDDD@H ·ͱΊ 

  109. ++6($$$4QSJOHDDD@H ·ͱΊ w ඇػೳཁ݅ͱ͸ w ඇػೳཁٻάϨʔυΛ׆༻͢Δ w lૣظʹzlޡղͳ͘zl࿙Βͣ͞ʹzఆٛ͢Δ w 4QSJOH#PPU"DUVBUPS

    w ؆୯Πϯετʔϧɺߴ͍ΧελϚΠζੑͱ֦ுੑ w ӡ༻ɾอक͸΋ͪΖΜɺ։ൃʹ΋༗༻ w 4QSJOH4FDVSJUZ0UIFST w 4QSJOH4FDVSJUZΛத৺ͱͨ͠ϥΠϒϥϦ܈Λ׆༻͢Δ 
  110. ++6($$$4QSJOHDDD@H 5IBOLZPV