๐Ÿ“‹ ้กน็›ฎๆฆ‚่งˆ ่ฟ™ๆ˜ฏไธ€ไธชไผไธš็บง Spring Boot 3.x ๆจกๆฟ้กน็›ฎ๏ผŒๆไพ›ไบ†ๅฎŒๆ•ด็š„็”จๆˆท่ฎค่ฏไธŽๆƒ้™็ฎก็†็ณป็ปŸใ€‚ ๆ ธๅฟƒๅŠŸ่ƒฝ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๆ ธๅฟƒๅŠŸ่ƒฝๆจกๅ— โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ 1. ็”จๆˆท่ฎค่ฏ JWT + Refresh Token ๅŒTokenๆœบๅˆถ โ”‚ โ”‚ 2. ๆƒ้™็ฎก็† RBAC๏ผˆ่ง’่‰ฒ-ๆƒ้™๏ผ‰ๆจกๅž‹ โ”‚ โ”‚ 3. ่ดฆๆˆทๅฎ‰ๅ…จ ็™ปๅฝ•ๅคฑ่ดฅ้”ๅฎšใ€ๅฏ†็ ้‡็ฝฎใ€้™ๆต โ”‚ โ”‚ 4. ๅฎก่ฎกๆ—ฅๅฟ— ่ฎฐๅฝ•ๆ‰€ๆœ‰ๆ•ๆ„Ÿๆ“ไฝœ โ”‚ โ”‚ 5. ็ผ“ๅญ˜ๆ”ฏๆŒ Redis ็ผ“ๅญ˜ใ€ๅˆ†ๅธƒๅผ้” โ”‚ โ”‚ 6. ๆถˆๆฏ้˜Ÿๅˆ— RocketMQ ๅผ‚ๆญฅๅค„็† โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ๆŠ€ๆœฏๆ ˆ Searched for 1 pattern, read 1 file (ctrl+o to expand) โ— ็Žฐๅœจ่ฎฉๆˆ‘ไธบไฝ ่ฏฆ็ป†ๅ‰–ๆžๆ•ดไธช้กน็›ฎ๏ผš --- ไธ€ใ€้กน็›ฎๆŠ€ๆœฏๆ ˆ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๆŠ€ๆœฏ โ”‚ ็‰ˆๆœฌ โ”‚ ็”จ้€” โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Spring Boot โ”‚ 3.5.6 โ”‚ ๅŸบ็ก€ๆก†ๆžถ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Spring Security โ”‚ 6.x โ”‚ ่ฎค่ฏๆŽˆๆƒ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Spring Data JPA โ”‚ - โ”‚ ORM ๆ•ฐๆฎ่ฎฟ้—ฎ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ MySQL โ”‚ - โ”‚ ๅ…ณ็ณปๆ•ฐๆฎๅบ“ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Redis โ”‚ - โ”‚ ็ผ“ๅญ˜/ๅˆ†ๅธƒๅผ้” โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ RocketMQ โ”‚ 2.3.1 โ”‚ ๆถˆๆฏ้˜Ÿๅˆ— โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ JJWT โ”‚ 0.11.5 โ”‚ JWT Token โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Flyway โ”‚ - โ”‚ ๆ•ฐๆฎๅบ“็‰ˆๆœฌ็ฎก็† โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ Lombok โ”‚ - โ”‚ ็ฎ€ๅŒ–ไปฃ็  โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ --- ไบŒใ€้กน็›ฎ็›ฎๅฝ•็ป“ๆž„ โ— Bash(tree -L 4 -I 'target|.git' D:\tmp\springboot-template\src\main\java\com\aisi\template 2>/dev/null || find D:\tmp\springboot-template\src\main\java\com\aisi\tempโ€ฆ) โŽฟ ย find: โ€˜D:tmpspringboot-templatesrcmainjavacomaisitemplateโ€™: No such file or directory sed: -e expression #1, char 7: unterminated `s' command Searched for 1 pattern (ctrl+o to expand) โ— ้กน็›ฎๅˆ†ๅฑ‚ๆžถๆž„ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ annotation/ # ่‡ชๅฎšไน‰ๆณจ่งฃ๏ผˆAOPๅˆ‡็‚น๏ผ‰ โ”‚ โ”œโ”€โ”€ @AuditLog # ๅฎก่ฎกๆ—ฅๅฟ—ๆณจ่งฃ โ”‚ โ”œโ”€โ”€ @RateLimit # ้™ๆตๆณจ่งฃ โ”‚ โ””โ”€โ”€ @StrongPassword # ๅผบๅฏ†็ ๆ ก้ชŒๆณจ่งฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ aspect/ # AOPๅˆ‡้ข โ”‚ โ”œโ”€โ”€ AuditLogAspect # ๅฎก่ฎกๆ—ฅๅฟ—ๅˆ‡้ข โ”‚ โ””โ”€โ”€ RateLimitAspect # ้™ๆตๅˆ‡้ข โ”‚ โ”œโ”€โ”€ ๐Ÿ“ config/ # ้…็ฝฎ็ฑป โ”‚ โ”œโ”€โ”€ SecurityConfig # Spring Security ๅฎ‰ๅ…จ้…็ฝฎ โ”‚ โ”œโ”€โ”€ RedisConfig # Redis ้…็ฝฎ โ”‚ โ”œโ”€โ”€ JpaConfig # JPA ๅฎก่ฎก้…็ฝฎ โ”‚ โ”œโ”€โ”€ WebConfig # Web MVC ้…็ฝฎ โ”‚ โ”œโ”€โ”€ JacksonConfig # JSON ๅบๅˆ—ๅŒ–้…็ฝฎ โ”‚ โ””โ”€โ”€ OpenApiConfig # Swagger ๆ–‡ๆกฃ้…็ฝฎ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ constants/ # ๅธธ้‡ๅฎšไน‰ โ”‚ โ”œโ”€โ”€ AppConstants # ๅบ”็”จๅธธ้‡ โ”‚ โ””โ”€โ”€ SecurityConstants # ๅฎ‰ๅ…จๅธธ้‡ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ controller/ # ๆŽงๅˆถๅฑ‚๏ผˆAPIๆŽฅๅฃ๏ผ‰ โ”‚ โ”œโ”€โ”€ UserController # ็”จๆˆทๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ RoleController # ่ง’่‰ฒๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ MenuController # ่œๅ•ๆŽฅๅฃ โ”‚ โ””โ”€โ”€ PermissionController # ๆƒ้™ๆŽฅๅฃ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ domain/ # ้ข†ๅŸŸๆจกๅž‹ โ”‚ โ”œโ”€โ”€ entity/ # ๅฎžไฝ“็ฑป๏ผˆๆ•ฐๆฎๅบ“่กจ๏ผ‰ โ”‚ โ”œโ”€โ”€ dto/ # ๆ•ฐๆฎไผ ่พ“ๅฏน่ฑก๏ผˆ่ฏทๆฑ‚ๅ‚ๆ•ฐ๏ผ‰ โ”‚ โ”œโ”€โ”€ vo/ # ่ง†ๅ›พๅฏน่ฑก๏ผˆๅ“ๅบ”ๆ•ฐๆฎ๏ผ‰ โ”‚ โ””โ”€โ”€ enums/ # ๆžšไธพ็ฑป โ”‚ โ”œโ”€โ”€ ๐Ÿ“ service/ # ๆœๅŠกๅฑ‚๏ผˆไธšๅŠก้€ป่พ‘๏ผ‰ โ”‚ โ””โ”€โ”€ impl/ # ๆœๅŠกๅฎž็Žฐ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ repository/ # ๆ•ฐๆฎ่ฎฟ้—ฎๅฑ‚๏ผˆDAO๏ผ‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ filter/ # ่ฟ‡ๆปคๅ™จ โ”‚ โ””โ”€โ”€ JwtAuthenticationFilter # JWT ่ฎค่ฏ่ฟ‡ๆปคๅ™จ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ handler/ # ๅค„็†ๅ™จ โ”‚ โ””โ”€โ”€ GlobalExceptionHandler # ๅ…จๅฑ€ๅผ‚ๅธธๅค„็† โ”‚ โ”œโ”€โ”€ ๐Ÿ“ interceptor/ # ๆ‹ฆๆˆชๅ™จ โ”‚ โ””โ”€โ”€ LoggingInterceptor # ๆ—ฅๅฟ—ๆ‹ฆๆˆชๅ™จ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ mq/ # ๆถˆๆฏ้˜Ÿๅˆ— โ”‚ โ”œโ”€โ”€ producer/ # ๆถˆๆฏ็”Ÿไบง่€… โ”‚ โ”œโ”€โ”€ consumer/ # ๆถˆๆฏๆถˆ่ดน่€… โ”‚ โ””โ”€โ”€ message/ # ๆถˆๆฏๅฎšไน‰ โ”‚ โ”œโ”€โ”€ ๐Ÿ“ utils/ # ๅทฅๅ…ท็ฑป โ”‚ โ”œโ”€โ”€ JwtUtil # JWT ๅทฅๅ…ท โ”‚ โ”œโ”€โ”€ SecurityUtils # ๅฎ‰ๅ…จๅทฅๅ…ท โ”‚ โ”œโ”€โ”€ RedisCache # Redis ็ผ“ๅญ˜ โ”‚ โ””โ”€โ”€ RedisLock # ๅˆ†ๅธƒๅผ้” โ”‚ โ”œโ”€โ”€ ๐Ÿ“ examples/ # ไฝฟ็”จ็คบไพ‹ โ”‚ โ”œโ”€โ”€ RedisUsageExample # Redis ไฝฟ็”จ็คบไพ‹ โ”‚ โ””โ”€โ”€ TransactionExample # ไบ‹ๅŠกไฝฟ็”จ็คบไพ‹ โ”‚ โ””โ”€โ”€ ๐Ÿ“ exception/ # ๅผ‚ๅธธ็ฑป โ”œโ”€โ”€ BusinessException # ไธšๅŠกๅผ‚ๅธธ โ””โ”€โ”€ RateLimitExceededException # ้™ๆตๅผ‚ๅธธ --- ไธ‰ใ€ๆ ธๅฟƒๆžถๆž„่ฎพ่ฎก 3.1 ่ฎค่ฏๆžถๆž„ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ่ฎค่ฏๆต็จ‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ๅฎขๆˆท็ซฏ Spring Boot JWT Token โ”‚ โ”‚ โ”‚ โ”‚ 1. POST /api/v1/user/login โ”‚ โ”‚ โ”‚ {username, password} โ”‚ โ”‚ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 2. ้ชŒ่ฏ็”จๆˆทๅๅฏ†็  โ”‚ โ”‚ โ”‚ 3. ๆฃ€ๆŸฅ่ดฆๆˆท้”ๅฎš็Šถๆ€ โ”‚ โ”‚ โ”‚ 4. ็”Ÿๆˆ Access Token(1h) โ”‚ โ”‚ โ”‚ 5. ็”Ÿๆˆ Refresh Token(7d) โ”‚ โ”‚ โ”‚ 6. ๅญ˜ๅ‚จ Refresh Token โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 7. ่ฟ”ๅ›ž Token โ”‚ โ”‚ โ”‚ {accessToken, โ”‚ โ”‚ โ”‚ refreshToken} โ”‚ โ”‚ โ”‚ <โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 8. ๅŽ็ปญ่ฏทๆฑ‚ๆบๅธฆ Token โ”‚ โ”‚ โ”‚ Authorization: Bearer โ”‚ โ”‚ โ”‚ {accessToken} โ”‚ โ”‚ โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€>โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 9. JwtAuthenticationFilter โ”‚ โ”‚ โ”‚ ่งฃๆž Token โ”‚ โ”‚ โ”‚ ้ชŒ่ฏ็ญพๅ โ”‚ โ”‚ โ”‚ ๆฃ€ๆŸฅ้ป‘ๅๅ• โ”‚ โ”‚ โ”‚ ่ฎพ็ฝฎ่ฎค่ฏไธŠไธ‹ๆ–‡ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ 10. ่ฟ”ๅ›žไธšๅŠกๆ•ฐๆฎ โ”‚ โ”‚ โ”‚ <โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ โ”‚ 3.2 ๆƒ้™ๆจกๅž‹๏ผˆRBAC๏ผ‰ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ RBAC ๆƒ้™ๆจกๅž‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ็”จๆˆท(User) โ”€โ”€ๅคšๅฏนๅคšโ”€โ”€ ่ง’่‰ฒ(Role) โ”€โ”€ๅคšๅฏนๅคšโ”€โ”€ ๆƒ้™(Permission) โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ having having having accounts roles permissions ็คบไพ‹๏ผš ็”จๆˆท alice โ”€โ”€ๆ‹ฅๆœ‰โ”€โ”€> [ROLE_USER, ROLE_ADMIN] โ”‚ โ””โ”€โ”€ ROLE_ADMIN โ”€โ”€ๆ‹ฅๆœ‰โ”€โ”€> [user:read, user:write, role:read, menu:read] ๆƒ้™่ฎก็ฎ—๏ผš alice ็š„ๆƒ้™ = ROLE_USER็š„ๆƒ้™ โˆช ROLE_ADMIN็š„ๆƒ้™ ไฝฟ็”จ @PreAuthorize ๆณจ่งฃๆŽงๅˆถ่ฎฟ้—ฎ๏ผš @PreAuthorize("hasAuthority('user:write')") public void updateUser() { ... } 3.3 ๅฎ‰ๅ…จ้˜ฒๆŠคๆœบๅˆถ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅฎ‰ๅ…จ้˜ฒๆŠคไฝ“็ณป โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ้˜ฒๆŠค็ฑปๅž‹ ๅฎž็Žฐๆ–นๅผ ้˜ˆๅ€ผ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ็™ปๅฝ•ๅคฑ่ดฅ้”ๅฎš Redis่ฎกๆ•ฐๅ™จ + ๆ—ถ้—ด็ช—ๅฃ 5ๆฌก/30ๅˆ†้’Ÿ ๅฏ†็ ้‡็ฝฎ้™ๆต Redis่ฎกๆ•ฐๅ™จ + ๅ†ทๅดๆ—ถ้—ด 1ๆฌก/60็ง’ APIๆŽฅๅฃ้™ๆต Redis + @RateLimitๅˆ‡้ข ๅฏ้…็ฝฎ ๅฏ†็ ๅผบๅบฆๆ ก้ชŒ @StrongPasswordๆณจ่งฃ ๅคงๅฐๅ†™+ๆ•ฐๅญ—+็‰นๆฎŠๅญ—็ฌฆ Token้ป‘ๅๅ• Redis + JTI ็™ปๅ‡บๆ—ถๅŠ ๅ…ฅ Token่ฝฎๆข Refresh Tokenๆœบๅˆถ ๅˆทๆ–ฐๅŽๆ—งTokenๅคฑๆ•ˆ ๅฏ†็ ้‡็ฝฎ ้‚ฎ็ฎฑ้ชŒ่ฏ็  6ไฝ/10ๅˆ†้’Ÿๆœ‰ๆ•ˆ --- ๅ››ใ€ๆ•ฐๆฎๅบ“่ฎพ่ฎก Searched for 1 pattern, read 2 files (ctrl+o to expand) โ— ๆ•ฐๆฎๅบ“่กจ็ป“ๆž„ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๆ•ฐๆฎๅบ“่กจๅ…ณ็ณปๅ›พ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ๆ ธๅฟƒ่กจๅ…ณ็ณป๏ผš users (็”จๆˆท่กจ) โ”‚ โ”œโ”€โ”€ 1:N โ”€โ”€> refresh_tokens (ๅˆทๆ–ฐไปค็‰Œ่กจ) โ”‚ โ”œโ”€โ”€ N:M โ”€โ”€> sys_role (่ง’่‰ฒ่กจ) โ”€โ”€ N:M โ”€โ”€> sys_permission (ๆƒ้™่กจ) โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ N:M โ”€โ”€> sys_menu (่œๅ•่กจ) โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ 1:N โ”€โ”€> password_reset_codes (ๅฏ†็ ้‡็ฝฎ็ ่กจ) ่พ…ๅŠฉ่กจ๏ผš โ”œโ”€โ”€ sys_audit_log (ๅฎก่ฎกๆ—ฅๅฟ—่กจ) โ””โ”€โ”€ sys_user_role (็”จๆˆท-่ง’่‰ฒๅ…ณ่”่กจ) sys_role_permission (่ง’่‰ฒ-ๆƒ้™ๅ…ณ่”่กจ) sys_role_menu (่ง’่‰ฒ-่œๅ•ๅ…ณ่”่กจ) โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ่กจๅ โ”‚ ็”จ้€” โ”‚ ๅ…ณ้”ฎๅญ—ๆฎต โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ users โ”‚ ็”จๆˆทไฟกๆฏ โ”‚ username, password, email, status, failed_login_count, locked_until โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ sys_role โ”‚ ่ง’่‰ฒๅฎšไน‰ โ”‚ role_code(ROLE_ADMIN็ญ‰), role_name, status โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ sys_permission โ”‚ ๆƒ้™ๅฎšไน‰ โ”‚ permission_code(user:read็ญ‰), resource, action โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ sys_menu โ”‚ ่œๅ•ๅฎšไน‰ โ”‚ menu_name, menu_type(็›ฎๅฝ•/้กต้ข/ๆŒ‰้’ฎ), parent_id โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ refresh_tokens โ”‚ ๅˆทๆ–ฐไปค็‰Œ โ”‚ token_hash(SHA-256), expires_at, revoked โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ sys_audit_log โ”‚ ๅฎก่ฎกๆ—ฅๅฟ— โ”‚ user_id, action(LOGIN/CREATE็ญ‰), resource, status โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ --- ไบ”ใ€ๆ ธๅฟƒๆจกๅ—่ฏฆ่งฃ 5.1 ่ฎค่ฏๆจกๅ— ๆ ธๅฟƒ็ฑปๅ…ณ็ณป๏ผš SecurityConfig (้…็ฝฎๅ…ฅๅฃ) โ”‚ โ”œโ”€โ”€ CustomUserDetailsService (ๅŠ ่ฝฝ็”จๆˆท) โ”‚ โ””โ”€โ”€ UserRepository (ๆŸฅ่ฏข็”จๆˆท) โ”‚ โ”œโ”€โ”€ JwtAuthenticationFilter (JWT่ฟ‡ๆปคๅ™จ) โ”‚ โ”œโ”€โ”€ JwtUtil (Token็”Ÿๆˆ/่งฃๆž) โ”‚ โ””โ”€โ”€ TokenService (Token็ฎก็†) โ”‚ โ””โ”€โ”€ RefreshTokenRepository โ”‚ โ””โ”€โ”€ PasswordEncoder (BCryptๅŠ ๅฏ†) ่ฎค่ฏๆต็จ‹่ฏฆ่งฃ๏ผš Read 1 file (ctrl+o to expand) โ— JWT่ฎค่ฏ่ฟ‡ๆปคๅ™จ่ฏฆ่งฃ๏ผš Read 1 file (ctrl+o to expand) โ— ่ฎค่ฏๆต็จ‹ๅ›พ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅฎŒๆ•ด่ฎค่ฏๆต็จ‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 1. ็”จๆˆท็™ปๅฝ• โ”‚ โ”œโ”€> UserController.login() โ”‚ โ”‚ โ”‚ โ”œโ”€> UserService.login() โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> 1. ๆ ก้ชŒ็”จๆˆทๅๅฏ†็  โ”‚ โ”‚ โ”œโ”€> 2. ๆฃ€ๆŸฅ่ดฆๆˆท้”ๅฎš็Šถๆ€ โ”‚ โ”‚ โ”œโ”€> 3. ๆฃ€ๆŸฅ่ดฆๆˆทๅฏ็”จ็Šถๆ€ โ”‚ โ”‚ โ”œโ”€> 4. ่ฎฐๅฝ•็™ปๅฝ•ๅคฑ่ดฅ(ๆˆๅŠŸๅˆ™ๆธ…้™ค) โ”‚ โ”‚ โ”œโ”€> 5. ็”Ÿๆˆ Access Token (1ๅฐๆ—ถ) โ”‚ โ”‚ โ”œโ”€> 6. ็”Ÿๆˆ Refresh Token (7ๅคฉ) โ”‚ โ”‚ โ”œโ”€> 7. ๅญ˜ๅ‚จ Refresh Token ๅˆฐๆ•ฐๆฎๅบ“ โ”‚ โ”‚ โ”œโ”€> 8. ๅ‘้€็™ปๅฝ•ๆถˆๆฏๅˆฐ MQ โ”‚ โ”‚ โ””โ”€> 9. ่ฟ”ๅ›ž LoginResponseVo โ”‚ โ”‚ โ”‚ โ””โ”€> ่ฟ”ๅ›ž { accessToken, refreshToken, expiresIn, userInfo } 2. ๅŽ็ปญ่ฏทๆฑ‚ โ”‚ โ”œโ”€> JwtAuthenticationFilter.doFilterInternal() โ”‚ โ”‚ โ”‚ โ”œโ”€> 1. ๆๅ– Token (Authorization: Bearer {token}) โ”‚ โ”œโ”€> 2. ้ชŒ่ฏ Token ็ญพๅ โ”‚ โ”œโ”€> 3. ๆฃ€ๆŸฅ้ป‘ๅๅ• (็™ปๅ‡บๆ—ถๅŠ ๅ…ฅ) โ”‚ โ”œโ”€> 4. ๆฃ€ๆŸฅ่ฟ‡ๆœŸๆ—ถ้—ด โ”‚ โ”œโ”€> 5. ๅŠ ่ฝฝ็”จๆˆท่ฏฆๆƒ… โ”‚ โ””โ”€> 6. ่ฎพ็ฝฎ่ฎค่ฏๅˆฐ SecurityContext โ”‚ โ””โ”€> ็ปง็ปญๅค„็†่ฏทๆฑ‚ 3. Token ๅˆทๆ–ฐ โ”‚ โ”œโ”€> UserController.refreshToken() โ”‚ โ”‚ โ”‚ โ””โ”€> TokenService.refreshToken() โ”‚ โ”‚ โ”‚ โ”œโ”€> 1. ้ชŒ่ฏ Refresh Token โ”‚ โ”œโ”€> 2. ๆ’ค้”€ๆ—ง Refresh Token โ”‚ โ”œโ”€> 3. ็”Ÿๆˆๆ–ฐ็š„ Access Token โ”‚ โ”œโ”€> 4. ็”Ÿๆˆๆ–ฐ็š„ Refresh Token โ”‚ โ””โ”€> 5. ่ฟ”ๅ›žๆ–ฐ Token 4. ็”จๆˆท็™ปๅ‡บ โ”‚ โ”œโ”€> UserController.logout() โ”‚ โ”‚ โ”‚ โ””โ”€> TokenService.addTokenToBlacklist() โ”‚ โ”‚ โ”‚ โ”œโ”€> 1. ๆๅ– Token ็š„ JTI โ”‚ โ”œโ”€> 2. ่ฎก็ฎ—ๅ‰ฉไฝ™่ฟ‡ๆœŸๆ—ถ้—ด โ”‚ โ”œโ”€> 3. ๅญ˜ๅ…ฅ Redis ้ป‘ๅๅ• โ”‚ โ””โ”€> 4. ๆ’ค้”€ๆ‰€ๆœ‰ Refresh Token 5.2 ๆƒ้™ๆจกๅ—่ฏฆ่งฃ Read 1 file (ctrl+o to expand) โ— ๆƒ้™ๅŠ ่ฝฝๆต็จ‹ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๆƒ้™ๅŠ ่ฝฝไธŽ้ชŒ่ฏๆต็จ‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 1. ็”จๆˆท็™ปๅฝ•ๆ—ถๅŠ ่ฝฝๆƒ้™ โ”‚ โ”œโ”€> CustomUserDetailsService.loadUserByUsername(username) โ”‚ โ”‚ โ”‚ โ”œโ”€> ๆŸฅ่ฏข็”จๆˆท: User + Roles (JOIN FETCH) โ”‚ โ”‚ โ”‚ โ”œโ”€> ้ๅކ็”จๆˆท็š„ๆฏไธช่ง’่‰ฒ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€> ่ง’่‰ฒ ROLE_ADMIN โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> ๆทปๅŠ ่ง’่‰ฒๆƒ้™: "ROLE_ADMIN" โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€> ๆทปๅŠ ่ง’่‰ฒไธ‹็š„ๅ…ทไฝ“ๆƒ้™ โ”‚ โ”‚ โ”œโ”€โ”€ "user:create" โ”‚ โ”‚ โ”œโ”€โ”€ "user:read" โ”‚ โ”‚ โ”œโ”€โ”€ "user:update" โ”‚ โ”‚ โ”œโ”€โ”€ "role:read" โ”‚ โ”‚ โ””โ”€โ”€ ... โ”‚ โ”‚ โ”‚ โ””โ”€> ๅˆๅนถๆ‰€ๆœ‰่ง’่‰ฒ็š„ๆƒ้™ โ†’ Set 2. ๆŽฅๅฃๆƒ้™้ชŒ่ฏ โ”‚ โ”œโ”€> @PreAuthorize("hasAuthority('user:create')") โ”‚ โ”‚ โ”‚ โ””โ”€> Spring Security ๆฃ€ๆŸฅๅฝ“ๅ‰็”จๆˆท็š„ๆƒ้™ๅˆ—่กจ โ”‚ โ”‚ โ”‚ โ””โ”€> ๅŒ…ๅซ "user:create" โ†’ โœ… ๅ…่ฎธ่ฎฟ้—ฎ โ”‚ ไธๅŒ…ๅซ โ†’ โŒ ๆ‹’็ป่ฎฟ้—ฎ 3. ๆƒ้™็ฑปๅž‹่ฏดๆ˜Ž โ”‚ โ”œโ”€โ”€ ่ง’่‰ฒๆƒ้™ (ROLE_XXX) โ”‚ โ”œโ”€โ”€ ็”จไบŽ็ฒ—็ฒ’ๅบฆๆŽงๅˆถ๏ผšๅˆคๆ–ญ็”จๆˆท่ง’่‰ฒ โ”‚ โ””โ”€โ”€ ไฝฟ็”จๆ–นๅผ: @PreAuthorize("hasRole('ADMIN')") โ”‚ ็ญ‰ไปทไบŽ: hasAuthority('ROLE_ADMIN') โ”‚ โ””โ”€โ”€ ๅ…ทไฝ“ๆƒ้™ (XXX:YYY) โ”œโ”€โ”€ ็”จไบŽ็ป†็ฒ’ๅบฆๆŽงๅˆถ๏ผšๅ…ทไฝ“ๆ“ไฝœ โ””โ”€โ”€ ไฝฟ็”จๆ–นๅผ: @PreAuthorize("hasAuthority('user:create')") 5.3 ๅฎ‰ๅ…จ้˜ฒๆŠคๆจกๅ— Read 1 file (ctrl+o to expand) โ— ๅฎ‰ๅ…จ้˜ฒๆŠคๆœบๅˆถ่ฏฆ่งฃ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅฎ‰ๅ…จ้˜ฒๆŠคๆœบๅˆถ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 1. ็™ปๅฝ•ๅคฑ่ดฅ้”ๅฎšๆœบๅˆถ โ”‚ โ”œโ”€> ่งฆๅ‘ๆกไปถ๏ผš่ฟž็ปญ็™ปๅฝ•ๅคฑ่ดฅ 5 ๆฌก๏ผˆๅฏ้…็ฝฎ๏ผ‰ โ”‚ โ”œโ”€> ้”ๅฎšๆ—ถ้•ฟ๏ผš30 ๅˆ†้’Ÿ๏ผˆๅฏ้…็ฝฎ๏ผ‰ โ”‚ โ”œโ”€> ๅฎž็Žฐๆ–นๅผ๏ผš โ”‚ โ”œโ”€โ”€ users ่กจๅญ˜ๅ‚จๅคฑ่ดฅๆฌกๆ•ฐๅ’Œ้”ๅฎšๆ—ถ้—ด โ”‚ โ”œโ”€โ”€ failed_login_count๏ผšๅคฑ่ดฅๆฌกๆ•ฐ โ”‚ โ””โ”€โ”€ locked_until๏ผš้”ๅฎšๅˆฐๆœŸๆ—ถ้—ด โ”‚ โ””โ”€> ่‡ชๅŠจ่งฃ้”๏ผš้”ๅฎšๆ—ถ้—ดๅˆฐๆœŸๅŽ่‡ชๅŠจ่งฃ้” 2. ้™ๆตๆœบๅˆถ (@RateLimit) โ”‚ โ”œโ”€> ๆ”ฏๆŒ็š„้™ๆต็ฑปๅž‹๏ผš โ”‚ โ”œโ”€โ”€ IP๏ผšๆŒ‰ IP ๅœฐๅ€้™ๆต โ”‚ โ”œโ”€โ”€ USER๏ผšๆŒ‰็”จๆˆท ID ้™ๆต โ”‚ โ””โ”€โ”€ GLOBAL๏ผšๅ…จๅฑ€้™ๆต โ”‚ โ”œโ”€> ไฝฟ็”จๆ–นๅผ๏ผš โ”‚ @RateLimit(permitted = 10, seconds = 60, limitType = LimitType.USER) โ”‚ public void sensitiveOperation() { ... } โ”‚ โ””โ”€> ๅฎž็ŽฐๅŽŸ็†๏ผš โ”œโ”€โ”€ Redis ๅญ˜ๅ‚จ่ฎกๆ•ฐๅ™จ โ”œโ”€โ”€ ๆ ผๅผ๏ผšrate_limit:user:{userId} โ”œโ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผšๆ—ถ้—ด็ช—ๅฃ โ””โ”€โ”€ ่ถ…่ฟ‡้™ๅˆถ๏ผšๆŠ›ๅ‡บ RateLimitExceededException 3. ๅฏ†็ ้‡็ฝฎๆœบๅˆถ โ”‚ โ”œโ”€> ้ชŒ่ฏ็ ่ง„ๅˆ™๏ผš โ”‚ โ”œโ”€โ”€ 6 ไฝ้šๆœบๆ•ฐๅญ— โ”‚ โ”œโ”€โ”€ 10 ๅˆ†้’Ÿๆœ‰ๆ•ˆๆœŸ โ”‚ โ”œโ”€โ”€ ๆœ€ๅคšๅฐ่ฏ• 5 ๆฌก โ”‚ โ””โ”€โ”€ 60 ็ง’่ฏทๆฑ‚ๅ†ทๅดๆ—ถ้—ด โ”‚ โ”œโ”€> ้˜ฒ็”จๆˆทๅๆžšไธพ๏ผš โ”‚ โ””โ”€โ”€ ๆ— ่ฎบ้‚ฎ็ฎฑๆ˜ฏๅฆๅญ˜ๅœจ๏ผŒ้ƒฝ่ฟ”ๅ›ž็›ธๅŒๆถˆๆฏ โ”‚ โ””โ”€> ้ชŒ่ฏ็ ๅญ˜ๅ‚จ๏ผš โ”œโ”€โ”€ ๅ“ˆๅธŒๅญ˜ๅ‚จ๏ผˆSHA-256๏ผ‰ โ””โ”€โ”€ ๆ•ฐๆฎๅบ“ + ็Šถๆ€็ฎก็† 4. Token ้ป‘ๅๅ•ๆœบๅˆถ โ”‚ โ”œโ”€> ่งฆๅ‘ๅœบๆ™ฏ๏ผš โ”‚ โ”œโ”€โ”€ ็”จๆˆทไธปๅŠจ็™ปๅ‡บ โ”‚ โ”œโ”€โ”€ ็ฎก็†ๅ‘˜ๅผบๅˆถไธ‹็บฟ โ”‚ โ””โ”€โ”€ ไฟฎๆ”นๅฏ†็ ๅŽ โ”‚ โ”œโ”€> ๅญ˜ๅ‚จๆ–นๅผ๏ผšRedis โ”‚ โ””โ”€> ้”ฎๆ ผๅผ๏ผštoken:blacklist:{jti} โ”‚ โ””โ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผšToken ๅ‰ฉไฝ™ๆœ‰ๆ•ˆๆœŸ 5.4 ๅฎก่ฎกๆ—ฅๅฟ—ๆจกๅ— Read 1 file (ctrl+o to expand) โ— ๅฎก่ฎกๆ—ฅๅฟ—ไฝฟ็”จ็คบไพ‹ // ๅœจๆ–นๆณ•ไธŠๆทปๅŠ  @AuditLog ๆณจ่งฃ @AuditLog( action = "CREATE", resource = "user", description = "ๅˆ›ๅปบ็”จๆˆท {0}" ) public void createUser(String username) { // ไธšๅŠก้€ป่พ‘ } // AOP ๅˆ‡้ขไผš่‡ชๅŠจ่ฎฐๅฝ•๏ผš // - ๆ“ไฝœ็”จๆˆท๏ผˆไปŽ SecurityContext ่Žทๅ–๏ผ‰ // - ๆ“ไฝœ็ฑปๅž‹๏ผšCREATE // - ่ต„ๆบ็ฑปๅž‹๏ผšuser // - ๆ“ไฝœๆ่ฟฐ๏ผš"ๅˆ›ๅปบ็”จๆˆท alice" // - ่ฏทๆฑ‚ไฟกๆฏ๏ผšIPใ€User-Agent // - ๆ“ไฝœ็ป“ๆžœ๏ผšๆˆๅŠŸ/ๅคฑ่ดฅ 5.5 Redis ็ผ“ๅญ˜ๆจกๅ— Read 1 file (ctrl+o to expand) โ— Redis ็ผ“ๅญ˜ไฝฟ็”จๅœบๆ™ฏ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Redis ไฝฟ็”จๅœบๆ™ฏๆ€ป็ป“ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 1. Token ้ป‘ๅๅ• โ”œโ”€โ”€ ้”ฎๆ ผๅผ๏ผštoken:blacklist:{jti} โ”œโ”€โ”€ ๅ€ผ๏ผš1๏ผˆๅ›บๅฎšๅ€ผ๏ผŒๅชๅˆคๆ–ญ้”ฎๆ˜ฏๅฆๅญ˜ๅœจ๏ผ‰ โ””โ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผšToken ๅ‰ฉไฝ™ๆœ‰ๆ•ˆๆœŸ 2. Refresh Token ๅญ˜ๅ‚จ โ”œโ”€โ”€ ้”ฎๆ ผๅผ๏ผšrefresh_token:{tokenHash} โ”œโ”€โ”€ ๅ€ผ๏ผš็”จๆˆทID โ””โ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผš7 ๅคฉ 3. ้™ๆต่ฎกๆ•ฐๅ™จ โ”œโ”€โ”€ ้”ฎๆ ผๅผ๏ผšrate_limit:{type}:{identifier} โ”‚ โ”œโ”€โ”€ type: ip / user / global โ”‚ โ””โ”€โ”€ identifier: IPๅœฐๅ€ / ็”จๆˆทID โ”œโ”€โ”€ ๅ€ผ๏ผšๅฝ“ๅ‰่ฏทๆฑ‚ๆฌกๆ•ฐ โ””โ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผšๆ—ถ้—ด็ช—ๅฃ 4. ็™ปๅฝ•ๅคฑ่ดฅ่ฎกๆ•ฐ โ”œโ”€โ”€ ้”ฎๆ ผๅผ๏ผšlogin_attempts:{username} โ”œโ”€โ”€ ๅ€ผ๏ผšๅคฑ่ดฅๆฌกๆ•ฐ โ””โ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผš้”ๅฎšๆ—ถ้•ฟ๏ผˆ30ๅˆ†้’Ÿ๏ผ‰ 5. ๅˆ†ๅธƒๅผ้” โ”œโ”€โ”€ ้”ฎๆ ผๅผ๏ผšlock:{resource}:{id} โ”œโ”€โ”€ ๅ€ผ๏ผšๅ”ฏไธ€ๆ ‡่ฏ†๏ผˆUUID๏ผ‰ โ”œโ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผš้”่ถ…ๆ—ถๆ—ถ้—ด๏ผˆ้˜ฒๆญขๆญป้”๏ผ‰ โ””โ”€โ”€ ๅฎž็Žฐๆ–นๅผ๏ผšSET NX EX + Lua ่„šๆœฌ 6. ็”จๆˆทไผš่ฏ็ผ“ๅญ˜ โ”œโ”€โ”€ ้”ฎๆ ผๅผ๏ผšsession:user:{userId} โ”œโ”€โ”€ ๅ€ผ๏ผšไผš่ฏไฟกๆฏ๏ผˆ็™ปๅฝ•IPใ€ๆ—ถ้—ด็ญ‰๏ผ‰ โ””โ”€โ”€ ่ฟ‡ๆœŸๆ—ถ้—ด๏ผš24 ๅฐๆ—ถ 7. ๅฏ†็ ้‡็ฝฎ้ชŒ่ฏ็  โ”œโ”€โ”€ ๅญ˜ๅ‚จ๏ผšๆ•ฐๆฎๅบ“ password_reset_codes ่กจ โ””โ”€โ”€ ้™ๅˆถ๏ผšRedis + ๅ†ทๅดๆ—ถ้—ด 5.6 ๆถˆๆฏ้˜Ÿๅˆ—ๆจกๅ— Read 3 files (ctrl+o to expand) โ— RocketMQ ๆถˆๆฏ้˜Ÿๅˆ—ๆžถๆž„ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ RocketMQ ๆถˆๆฏๆต็จ‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ็”จๆˆท็™ปๅฝ• โ”‚ โ”œโ”€> UserService.login() โ”‚ โ”‚ โ”‚ โ”œโ”€> 1. ้ชŒ่ฏ็”จๆˆท โ”‚ โ”œโ”€> 2. ็”Ÿๆˆ Token โ”‚ โ”œโ”€> 3. ๅ‘้€็™ปๅฝ•ๆถˆๆฏๅˆฐ MQ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€> UserMessageProducer.sendLoginMessage() โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€> rocketMQTemplate.asyncSend(user-topic, message) โ”‚ โ”‚ โ”‚ โ””โ”€> 4. ่ฟ”ๅ›žๅ“ๅบ”๏ผˆไธ็ญ‰ๅพ… MQ ๅค„็†๏ผ‰ โ”‚ โ”‚ โ””โ”€> RocketMQ: user-topic โ”‚ โ””โ”€> UserMessageConsumer.onMessage() โ”‚ โ”œโ”€> 1. ๆ”ถๅˆฐๆถˆๆฏ โ”œโ”€> 2. ๆ นๆฎ messageType ่ทฏ็”ฑ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ REGISTER โ†’ handleRegisterMessage() โ”‚ โ”‚ โ””โ”€> ๅ‘้€ๆฌข่ฟŽ้‚ฎไปถใ€ๅˆๅง‹ๅŒ–ๆ•ฐๆฎ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ LOGIN โ†’ handleLoginMessage() โ”‚ โ”‚ โ””โ”€> ่ฎฐๅฝ•็™ปๅฝ•ๆ—ฅๅฟ—ใ€ๆ›ดๆ–ฐๆœ€ๅŽ็™ปๅฝ•ๆ—ถ้—ด โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ LOGOUT โ†’ handleLogoutMessage() โ”‚ โ”‚ โ””โ”€> ๆธ…็†็ผ“ๅญ˜ใ€็™ปๅ‡บๅ…ถไป–่ฎพๅค‡ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ UPDATE โ†’ handleUpdateMessage() โ”‚ โ”‚ โ””โ”€> ๅŒๆญฅๆ•ฐๆฎๅˆฐๆ•ฐๆฎไป“ๅบ“ใ€ๆธ…้™ค็ผ“ๅญ˜ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ DELETE โ†’ handleDeleteMessage() โ”‚ โ””โ”€> ๆธ…็†ๅ…ณ่”ๆ•ฐๆฎใ€ๅฝ’ๆกฃ โ”‚ โ””โ”€> 3. ๅค„็†ๅฎŒๆˆ๏ผŒ็กฎ่ฎคๆถˆ่ดน --- ๅ…ญใ€้…็ฝฎๆ–‡ไปถ่งฃๆž Searched for 1 pattern, read 2 files (ctrl+o to expand) โ— ้…็ฝฎๆ–‡ไปถ็ป“ๆž„ๆ€ป็ป“ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ้…็ฝฎๆ–‡ไปถ็ป“ๆž„ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ application.yaml (ไธป้…็ฝฎ) โ”œโ”€โ”€ spring.profiles.active: dev (ๆฟ€ๆดป็Žฏๅขƒ) โ”œโ”€โ”€ server.port: 8080 โ””โ”€โ”€ ๆ”ฏๆŒ็Žฏๅขƒๅ˜้‡่ฆ†็›– (.env ๆ–‡ไปถ) application-dev.yaml (ๅผ€ๅ‘็Žฏๅขƒ) โ”œโ”€โ”€ ๆ•ฐๆฎๅบ“้…็ฝฎ (MySQL) โ”‚ โ”œโ”€โ”€ url: jdbc:mysql://localhost:3306/template โ”‚ โ”œโ”€โ”€ username: root โ”‚ โ””โ”€โ”€ password: root โ”œโ”€โ”€ JPA ้…็ฝฎ โ”‚ โ”œโ”€โ”€ ddl-auto: validate (ไธ่‡ชๅŠจๅปบ่กจ๏ผŒ็”ฑ Flyway ็ฎก็†) โ”‚ โ””โ”€โ”€ show-sql: false โ”œโ”€โ”€ Flyway (ๆ•ฐๆฎๅบ“็‰ˆๆœฌ็ฎก็†) โ”‚ โ”œโ”€โ”€ enabled: true โ”‚ โ””โ”€โ”€ locations: classpath:db/migration โ”œโ”€โ”€ Redis ้…็ฝฎ โ”‚ โ”œโ”€โ”€ host: localhost:6379 โ”‚ โ””โ”€โ”€ database: 0 โ”œโ”€โ”€ ้‚ฎไปถ้…็ฝฎ (SMTP) โ”‚ โ”œโ”€โ”€ host: smtp.163.com โ”‚ โ””โ”€โ”€ port: 465 โ”œโ”€โ”€ JWT ้…็ฝฎ โ”‚ โ”œโ”€โ”€ secret: ๅฏ†้’ฅ๏ผˆHS512 ้œ€่ฆ 64 ๅญ—็ฌฆ๏ผ‰ โ”‚ โ”œโ”€โ”€ access-token-expiration: 3600 ็ง’ (1 ๅฐๆ—ถ) โ”‚ โ””โ”€โ”€ refresh-token-expiration: 604800 ็ง’ (7 ๅคฉ) โ”œโ”€โ”€ ไธšๅŠก้…็ฝฎ โ”‚ โ”œโ”€โ”€ ็™ปๅฝ•ๅคฑ่ดฅ้”ๅฎš: 5 ๆฌก / 30 ๅˆ†้’Ÿ โ”‚ โ””โ”€โ”€ ๅฏ†็ ้‡็ฝฎ: 6 ไฝ้ชŒ่ฏ็  / 10 ๅˆ†้’Ÿๆœ‰ๆ•ˆ โ””โ”€โ”€ RocketMQ ้…็ฝฎ โ””โ”€โ”€ name-server: localhost:9876 --- ไธƒใ€่ฏทๆฑ‚ๅค„็†ๅฎŒๆ•ดๆต็จ‹ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ ๅฎŒๆ•ด่ฏทๆฑ‚ๅค„็†ๆต็จ‹ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ 1. ๅฎขๆˆท็ซฏๅ‘่ตท่ฏทๆฑ‚ โ”‚ โ”œโ”€> GET /api/v1/user/info โ”‚ Headers: โ”‚ - Authorization: Bearer {accessToken} โ”‚ - Content-Type: application/json โ”‚ โ”‚ 2. ่ฟ›ๅ…ฅ Spring MVC ๅค„็†้“พ โ”‚ โ”œโ”€> LoggingInterceptor (ๆ‹ฆๆˆชๅ™จ) โ”‚ โ”œโ”€โ”€ ่ฎฐๅฝ•่ฏทๆฑ‚ๅผ€ๅง‹ๆ—ถ้—ด โ”‚ โ”œโ”€โ”€ ็”Ÿๆˆ่ฏทๆฑ‚ ID (8 ไฝ UUID) โ”‚ โ”œโ”€โ”€ ่Žทๅ–ๅฝ“ๅ‰็”จๆˆทไฟกๆฏ โ”‚ โ””โ”€โ”€ ่ฎฐๅฝ•่ฏทๆฑ‚ๅผ€ๅง‹ๆ—ฅๅฟ— โ”‚ โ”‚ 3. ่ฟ›ๅ…ฅ Filter ้“พ โ”‚ โ”œโ”€> JwtAuthenticationFilter (JWT ่ฟ‡ๆปคๅ™จ) โ”‚ โ”œโ”€โ”€ ๆๅ– Token: Bearer {token} โ”‚ โ”œโ”€โ”€ ้ชŒ่ฏ Token ็ญพๅ (HS512) โ”‚ โ”œโ”€โ”€ ๆฃ€ๆŸฅ้ป‘ๅๅ• (Redis: token:blacklist:{jti}) โ”‚ โ”œโ”€โ”€ ๆฃ€ๆŸฅ่ฟ‡ๆœŸๆ—ถ้—ด โ”‚ โ”œโ”€โ”€ ๅŠ ่ฝฝ็”จๆˆท่ฏฆๆƒ… โ”‚ โ””โ”€โ”€ ่ฎพ็ฝฎ่ฎค่ฏๅˆฐ SecurityContext โ”‚ โ”‚ 4. ๆƒ้™้ชŒ่ฏ โ”‚ โ”œโ”€> @PreAuthorize("hasAuthority('user:read')") โ”‚ โ””โ”€โ”€ ๆฃ€ๆŸฅ็”จๆˆทๆƒ้™ๅˆ—่กจๆ˜ฏๅฆๅŒ…ๅซ "user:read" โ”‚ โ”‚ 5. AOP ๅˆ‡้ขๅค„็† โ”‚ โ”œโ”€> AuditLogAspect (ๅฎก่ฎกๆ—ฅๅฟ—ๅˆ‡้ข) โ”‚ โ”œโ”€โ”€ ๆฃ€ๆŸฅ @AuditLog ๆณจ่งฃ โ”‚ โ”œโ”€โ”€ ๆๅ–่ฏทๆฑ‚ไฟกๆฏ โ”‚ โ”œโ”€โ”€ ๆๅ–ๅฝ“ๅ‰็”จๆˆทไฟกๆฏ โ”‚ โ””โ”€โ”€ ๅ‡†ๅค‡่ฎฐๅฝ•ๅฎก่ฎกๆ—ฅๅฟ—๏ผˆๅผ‚ๆญฅๆ‰ง่กŒ๏ผ‰ โ”‚ โ”‚ 6. ๆ‰ง่กŒ Controller โ”‚ โ”œโ”€> UserController.getUserInfo() โ”‚ โ”œโ”€โ”€ ไปŽ SecurityContext ่Žทๅ–็”จๆˆท โ”‚ โ””โ”€โ”€ ่ฟ”ๅ›ž็”จๆˆทไฟกๆฏ (UserVo) โ”‚ โ”‚ 7. ๅ“ๅบ”ๅค„็† โ”‚ โ”œโ”€> ๅ…จๅฑ€ๅผ‚ๅธธๅค„็† โ”‚ โ””โ”€โ”€ GlobalExceptionHandler โ”‚ โ”œโ”€โ”€ ๆ•่ŽทไธšๅŠกๅผ‚ๅธธ โ”‚ โ”œโ”€โ”€ ๆ•่Žทๅ‚ๆ•ฐๆ ก้ชŒๅผ‚ๅธธ โ”‚ โ””โ”€โ”€ ๆ•่Žท็ณป็ปŸๅผ‚ๅธธ โ”‚ โ”‚ 8. ๅŽ็ฝฎๅค„็† โ”‚ โ”œโ”€> LoggingInterceptor.afterCompletion() โ”‚ โ”œโ”€โ”€ ่ฎก็ฎ—่ฏทๆฑ‚่€—ๆ—ถ โ”‚ โ””โ”€โ”€ ่ฎฐๅฝ•่ฏทๆฑ‚ๅฎŒๆˆๆ—ฅๅฟ— โ”‚ โ””โ”€> ่ฟ”ๅ›žๅ“ๅบ” โ””โ”€> RestBean โ”œโ”€โ”€ code: 200 โ”œโ”€โ”€ message: "ๆ“ไฝœๆˆๅŠŸ" โ””โ”€โ”€ data: UserVo --- ๅ…ซใ€ๆ€ป็ป“ไธŽๅปบ่ฎฎ ้กน็›ฎไผ˜ๅŠฟ 1. ๆžถๆž„ๆธ…ๆ™ฐ๏ผšๅˆ†ๅฑ‚ๆ˜Ž็กฎ๏ผŒ่Œ่ดฃๅ•ไธ€ 2. ๅฎ‰ๅ…จๅฎŒๅ–„๏ผšJWT + RBAC + ๅคš้‡้˜ฒๆŠค 3. ๆ‰ฉๅฑ•ๆ€งๅผบ๏ผšๆจกๅ—ๅŒ–่ฎพ่ฎก๏ผŒๆ˜“ไบŽๆ‰ฉๅฑ• 4. ๆ–‡ๆกฃๅฎŒๆ•ด๏ผšๅ…จไธญๆ–‡ๆณจ้‡Š๏ผŒไพฟไบŽ็ปดๆŠค 5. ๅผ‚ๆญฅ่งฃ่€ฆ๏ผšRocketMQ ๆถˆๆฏ้˜Ÿๅˆ— ้€‚็”จๅœบๆ™ฏ - ไผไธšๅ†…้ƒจ็ฎก็†็ณป็ปŸ - SaaS ๅคš็งŸๆˆทๅนณๅฐ - ้œ€่ฆ RBAC ๆƒ้™็š„็ณป็ปŸ - ้œ€่ฆๅฎก่ฎกๆ—ฅๅฟ—็š„็ณป็ปŸ - ้œ€่ฆ้ซ˜ๅฎ‰ๅ…จๆ€ง็š„็ณป็ปŸ ๆ‰ฉๅฑ•ๅปบ่ฎฎ 1. ๆ•ฐๆฎๆƒ้™๏ผšๅœจ RBAC ๅŸบ็ก€ไธŠๅขžๅŠ ๆ•ฐๆฎๆƒ้™ๆŽงๅˆถ 2. ๅคš็งŸๆˆท๏ผšๆทปๅŠ ็งŸๆˆท้š”็ฆปๆœบๅˆถ 3. OAuth2๏ผšๆ”ฏๆŒ็ฌฌไธ‰ๆ–น็™ปๅฝ•๏ผˆๅพฎไฟกใ€GitHub็ญ‰๏ผ‰ 4. ็›‘ๆŽงๅ‘Š่ญฆ๏ผš้›†ๆˆ Prometheus + Grafana 5. ๆ—ฅๅฟ—ๆ”ถ้›†๏ผš้›†ๆˆ ELK๏ผˆElasticsearch + Logstash + Kibana๏ผ‰