├── README.md
├── sp-board
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── sp
│ │ └── sec
│ │ ├── board
│ │ ├── controller
│ │ │ └── BoardController.java
│ │ ├── domain
│ │ │ ├── Comment.java
│ │ │ ├── SpBoard.java
│ │ │ └── SpBoardSummary.java
│ │ ├── repository
│ │ │ └── SpBoardRepository.java
│ │ └── service
│ │ │ └── SpBoardService.java
│ │ └── config
│ │ └── SpBoardModule.java
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── board
│ ├── SpBoardTestApplication.java
│ └── service
│ ├── SpBoardServiceTest.java
│ └── SpBoardTestHelper.java
├── sp-jwt-security
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ ├── config
│ │ │ └── SpJwtSecurityModule.java
│ │ │ └── web
│ │ │ └── config
│ │ │ ├── JWTCheckFilter.java
│ │ │ ├── JWTLoginFilter.java
│ │ │ ├── JWTUtil.java
│ │ │ ├── RefreshableJWTLoginFilter.java
│ │ │ ├── SpJwtProperties.java
│ │ │ ├── UserLogin.java
│ │ │ └── VerifyResult.java
│ └── resources
│ │ └── META-INF
│ │ └── spring-configuration-metadata.json
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ ├── SpIntegrationTest.java
│ ├── SpJwtRefreshableTwoUserIntegrationTest.java
│ ├── SpJwtTwoUserIntegrationTest.java
│ ├── SpJwtUserAdminIntegrationTest.java
│ ├── SpRefreshableIntegrationTest.java
│ └── Tokens.java
├── sp-web-util
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ └── util
│ └── RestResponsePage.java
├── user-authority
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── sp
│ │ └── sec
│ │ ├── config
│ │ └── UserAuthorityModule.java
│ │ └── user
│ │ ├── controller
│ │ └── UserController.java
│ │ ├── domain
│ │ ├── Authority.java
│ │ └── User.java
│ │ ├── repository
│ │ └── UserRepository.java
│ │ └── service
│ │ └── UserService.java
│ └── test
│ ├── java
│ └── com
│ │ └── sp
│ │ └── sec
│ │ └── user
│ │ ├── UserAuthorityJpaTest.java
│ │ ├── UserAuthorityTestApplication.java
│ │ ├── UserTestHelper.java
│ │ └── WithUserTest.java
│ └── resources
│ └── applicaiton-test.yml
├── user-oauth2-support
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── sp
│ │ └── sec
│ │ ├── config
│ │ └── UserOAuth2SupportModule.java
│ │ └── user
│ │ └── oauth2
│ │ ├── domain
│ │ ├── ExtendedUser.java
│ │ └── ProvidedOAuth2User.java
│ │ ├── repository
│ │ ├── ExtendedUserRepository.java
│ │ └── ProvidedOAuth2UserRepository.java
│ │ └── service
│ │ ├── ExtendedUserService.java
│ │ └── ProvidedOAuth2UserService.java
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── user
│ └── oauth2
│ ├── ExtendedUserServiceTest.java
│ ├── ExtendedUserTestHelper.java
│ ├── FacebookUserLoginTest.java
│ ├── GoogleUserLoginTest.java
│ ├── KakaoUserLoginTest.java
│ ├── NaverUserLoginTest.java
│ ├── OAuth2UserSample.java
│ ├── UserOAuth2SupportApp.java
│ └── WithExtendedUserTest.java
└── web
├── auth-server-1
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── AuthServer1Application.java
│ │ │ └── config
│ │ │ ├── DBInit.java
│ │ │ └── SecurityConfig.java
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ └── AuthServer1ApplicationTests.java
├── google-client-4
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── GoogleClient4Application.java
│ │ │ ├── config
│ │ │ ├── SecurityConfig.java
│ │ │ ├── SpGoogleUser.java
│ │ │ └── SpGoogleUserToMyUserFilter.java
│ │ │ └── controller
│ │ │ ├── HomeController.java
│ │ │ └── SecuredService.java
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ └── GoogleClient4ApplicationTests.java
├── jwt-refresh-token-test
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── JwtRefreshTokenTestApplication.java
│ │ │ └── config
│ │ │ └── SecurityConfig.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ └── RefreshTokenTest.java
├── jwt-user-web
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── JwtUserWebApplication.java
│ │ │ └── config
│ │ │ └── SecurityConfig.java
│ └── resources
│ │ ├── application-test.yml
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ ├── JWTLoginFilterTest.java
│ ├── JWTTokenTest.java
│ ├── JwtUserWebApplicationTests.java
│ └── UserControllerIntegrationTest.java
├── oauth2-client-test
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── Oauth2ClientTestApplication.java
│ │ │ ├── config
│ │ │ ├── SecurityConfig.java
│ │ │ └── SpOidcUserToSiteUserFilter.java
│ │ │ ├── controller
│ │ │ └── HomeController.java
│ │ │ └── service
│ │ │ ├── SpOAuth2UserService.java
│ │ │ └── SpOidcUserService.java
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ └── Oauth2ClientTestApplicationTests.java
├── resource-server-1
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── ResourceServer1Application.java
│ │ │ └── config
│ │ │ └── SecurityConfig.java
│ └── resources
│ │ └── application.yml
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ ├── AuthResourceTest.java
│ └── ResourceServer1ApplicationTests.java
├── security-basic
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ ├── basic
│ │ │ ├── HomeController.java
│ │ │ ├── SecurityBasicApplication.java
│ │ │ ├── SecurityMessage.java
│ │ │ ├── TestController.java
│ │ │ └── config
│ │ │ │ └── SecurityConfig.java
│ │ │ └── user
│ │ │ └── UserAuthorityApplication.java
│ └── resources
│ │ ├── application.properties
│ │ ├── application.yml
│ │ ├── static
│ │ ├── login.css
│ │ └── style.css
│ │ └── templates
│ │ ├── index.html
│ │ └── loginForm.html
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ ├── basic
│ ├── SecurityBasicApplicationTests.java
│ └── UserAccessTest.java
│ └── user
│ └── UserAuthorityApplicationTests.java
├── sp-board-web
├── .mvn
│ └── wrapper
│ │ ├── MavenWrapperDownloader.java
│ │ ├── maven-wrapper.jar
│ │ └── maven-wrapper.properties
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── sp
│ │ │ └── sec
│ │ │ └── web
│ │ │ ├── SpBoardWebApplication.java
│ │ │ └── config
│ │ │ └── SecurityConfig.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── sp
│ └── sec
│ └── web
│ ├── SpBoardWebApplicationTests.java
│ └── board
│ ├── BoardControllerCommentIntegrationTest.java
│ └── BoardControllerIntegrationTest.java
└── user-authority-test-web
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── sp
│ │ └── sec
│ │ └── web
│ │ ├── UserAuthorityTestWebApplication.java
│ │ ├── config
│ │ ├── DBInit.java
│ │ ├── MongoConfig.java
│ │ └── SecurityConfig.java
│ │ └── controller
│ │ ├── HomeController.java
│ │ ├── SecurityMessage.java
│ │ └── TestController.java
└── resources
│ ├── application.yml
│ ├── static
│ ├── login.css
│ └── style.css
│ └── templates
│ ├── index.html
│ └── loginForm.html
└── test
└── java
└── com
└── sp
└── sec
└── web
├── UserAccessTest.java
├── UserAuthorityTestWebApplicationTests.java
└── UserControllerTest.java
/README.md:
--------------------------------------------------------------------------------
1 | # 스프링 Security를 유닛테스트 하라! (Do Unit Test Spring Security!)
2 |
3 |
4 | 이 프로젝트는 스프링 Security 의 주요 기능을 유닛테스트 하기 위해 만든 샘플 프로젝트 입니다.
5 | 이 프로젝트의 코드를 만드는 과정은 Youtube 에서 시리즈로 제공하고 있습니다.
6 |
7 | 이 과정을 만든 목적은 다음과 같습니다.
8 | * 유닛테스트를 통해 스프링 부트 시큐리티를 이해한다.
9 | * 모듈 중심 설계
10 | * MSA 시스템 구성 하기 좋은 설계
11 | * OAuth2 시스템 구성과 설계
12 |
13 | ## Episode1 : DB 없이 테스트 하기
14 |
15 | 영상 : https://www.youtube.com/watch?v=MNEgiFeUy_U
16 |
17 |
18 | ## Episode2 : User 모듈 만들고 테스트 하기
19 |
20 | * 도메인, 서비스 구현/테스트 : https://www.youtube.com/watch?v=WcF95nNbh7o
21 | * 컨트롤러 구현/테스트 : https://www.youtube.com/watch?v=2ljhDwMdTP8
22 |
23 | ## Episode3 : JWT 토큰 테스트
24 |
25 | * JWT 토큰 구현/테스트 : https://www.youtube.com/watch?v=w8wY2x5ezyU
26 | * 소스 정리 : https://www.youtube.com/watch?v=ctEUUScmH1I
27 |
28 | ## Episode4 : 게시판 모듈 만들고 테스트 하기
29 |
30 | * 도메인, 서비스 구현/테스트 : https://www.youtube.com/watch?v=0cNzhs405Ug
31 | * 컨트롤러 구현/테스트 : https://www.youtube.com/watch?v=grrR-wdNVgU
32 |
33 |
34 | ## Episode5 : Refresh Token 방식 테스트
35 |
36 | * Refresh Token 구현/테스트 : https://www.youtube.com/watch?v=4an8SrfvXSo
37 | * 번외편 : https://www.youtube.com/watch?v=NCglUWoXqm4
38 |
39 | ## Episode6 : OAuth2 인증
40 |
41 | * 인증서버 토큰으로 MSA 구현 :
42 |
--------------------------------------------------------------------------------
/sp-board/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | spring-boot-dependencies
8 | org.springframework.boot
9 | 2.4.0
10 |
11 | com.sp.sec
12 | sp-board
13 | 1.0.0
14 |
15 |
16 | 11
17 | ${java.version}
18 | ${java.version}
19 | UTF-8
20 | UTF-8
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-data-mongodb
27 |
28 |
29 | org.springframework.boot
30 | spring-boot-starter-security
31 |
32 |
33 |
34 | org.projectlombok
35 | lombok
36 | true
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-test
41 | test
42 |
43 |
44 | org.junit.vintage
45 | junit-vintage-engine
46 |
47 |
48 |
49 |
50 | de.flapdoodle.embed
51 | de.flapdoodle.embed.mongo
52 | test
53 |
54 |
55 |
56 | org.springframework.security
57 | spring-security-test
58 | test
59 |
60 |
61 | com.fasterxml.jackson.core
62 | jackson-annotations
63 |
64 |
65 |
66 |
67 | com.sp.sec
68 | user-authority
69 | 1.0.0
70 |
71 |
72 |
73 | com.sp.sec
74 | user-authority
75 | 1.0.0
76 | test-jar
77 | test
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | org.apache.maven.plugins
86 | maven-jar-plugin
87 |
88 |
89 |
90 | test-jar
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/sp-board/src/main/java/com/sp/sec/board/controller/BoardController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board.controller;
2 |
3 | import com.sp.sec.board.domain.Comment;
4 | import com.sp.sec.board.domain.SpBoard;
5 | import com.sp.sec.board.domain.SpBoardSummary;
6 | import com.sp.sec.board.service.SpBoardService;
7 | import com.sp.sec.user.domain.User;
8 | import com.sp.sec.web.util.RestResponsePage;
9 | import lombok.RequiredArgsConstructor;
10 | import org.springframework.security.access.AccessDeniedException;
11 | import org.springframework.security.access.prepost.PreAuthorize;
12 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
13 | import org.springframework.util.StringUtils;
14 | import org.springframework.web.bind.annotation.*;
15 |
16 | import java.util.Optional;
17 |
18 | @RestController
19 | @RequestMapping("/board")
20 | @RequiredArgsConstructor
21 | public class BoardController {
22 |
23 | private final SpBoardService boardService;
24 |
25 | @PreAuthorize("isAuthenticated() and (#board.boardId == null or #board.writerId == #user.userId)")
26 | @PostMapping("/save")
27 | public SpBoard save(
28 | @RequestBody SpBoard board,
29 | @AuthenticationPrincipal User user
30 | ){
31 | if(StringUtils.isEmpty(board.getBoardId())){
32 | board.setWriterId(user.getUserId());
33 | }
34 | return boardService.save(board);
35 | }
36 |
37 | @PreAuthorize("isAuthenticated()")
38 | @GetMapping("/list")
39 | public RestResponsePage list(
40 | @RequestParam(defaultValue = "1") Integer pageNum,
41 | @RequestParam(defaultValue = "10") Integer size
42 | ){
43 | return RestResponsePage.of(boardService.list(pageNum, size));
44 | }
45 |
46 | @PreAuthorize("isAuthenticated()")
47 | @GetMapping("/{boardId}")
48 | public Optional getBoard(@PathVariable String boardId){
49 | return boardService.findBoard(boardId);
50 | }
51 |
52 | @PreAuthorize("isAuthenticated()")
53 | @DeleteMapping("/{boardId}")
54 | public Optional remove(
55 | @PathVariable String boardId,
56 | @AuthenticationPrincipal User user
57 | ){
58 | return boardService.findBoard(boardId).map(board->{
59 | if(board.getWriterId().equals(user.getUserId())){
60 | boardService.removeBoard(boardId);
61 | }else{
62 | throw new AccessDeniedException("게시자만 삭제할 수 있습니다.");
63 | }
64 | return board;
65 | });
66 | }
67 |
68 | @PreAuthorize("isAuthenticated()")
69 | @PutMapping("/{boardId}/comment")
70 | public Comment addComment(
71 | @PathVariable String boardId,
72 | @RequestBody String comment,
73 | @AuthenticationPrincipal User user
74 | ){
75 | return boardService.addComment(boardId, Comment.builder()
76 | .userId(user.getUserId())
77 | .userName(user.getName())
78 | .comment(comment)
79 | .build());
80 | }
81 |
82 | @PreAuthorize("isAuthenticated()")
83 | @DeleteMapping("/{boardId}/comment/{commentId}")
84 | public Optional removeComment(
85 | @PathVariable String boardId,
86 | @PathVariable String commentId,
87 | @AuthenticationPrincipal User user
88 | ){
89 | return boardService.findBoard(boardId).map(board->{
90 | if(board.getCommentList() == null) return false;
91 | Optional comment = board.getCommentList().stream().filter(c->c.getCommentId().equals(commentId)).findFirst();
92 | if(comment.isPresent()){
93 | if(comment.get().getUserId().equals(user.getUserId())){
94 | boardService.removeComment(boardId, commentId);
95 | return true;
96 | }else{
97 | throw new AccessDeniedException("댓글을 생성한 사람만 삭제할 수 있습니다.");
98 | }
99 | }
100 | return false;
101 | });
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/sp-board/src/main/java/com/sp/sec/board/domain/Comment.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board.domain;
2 |
3 |
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 |
9 | import java.time.LocalDateTime;
10 |
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | @Builder
15 | public class Comment {
16 |
17 | private String commentId;
18 |
19 | private String comment;
20 | private String userId;
21 | private String userName;
22 |
23 | private LocalDateTime created;
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/sp-board/src/main/java/com/sp/sec/board/domain/SpBoard.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board.domain;
2 |
3 |
4 | import com.sp.sec.user.domain.User;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import org.springframework.data.annotation.Id;
10 | import org.springframework.data.annotation.Transient;
11 | import org.springframework.data.mongodb.core.mapping.Document;
12 |
13 | import java.time.LocalDateTime;
14 | import java.util.List;
15 |
16 | @Document(collection = "sp_board")
17 | @Data
18 | @AllArgsConstructor
19 | @NoArgsConstructor
20 | @Builder
21 | public class SpBoard {
22 |
23 | @Id
24 | private String boardId;
25 |
26 | private String title;
27 | private String content;
28 |
29 | private String writerId;
30 |
31 | @Transient
32 | private User writer;
33 |
34 | private List commentList;
35 |
36 | private LocalDateTime created;
37 | private LocalDateTime updated;
38 |
39 | private boolean open; // ready, open
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/sp-board/src/main/java/com/sp/sec/board/domain/SpBoardSummary.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board.domain;
2 |
3 |
4 | import com.sp.sec.user.domain.User;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import org.springframework.data.annotation.Transient;
10 |
11 | import java.time.LocalDateTime;
12 | import java.util.List;
13 |
14 | @Data
15 | @AllArgsConstructor
16 | @NoArgsConstructor
17 | @Builder
18 | public class SpBoardSummary {
19 |
20 | private String boardId;
21 | private String title;
22 | private String writerId;
23 |
24 | @Transient
25 | private User writer;
26 |
27 | private int commentCount;
28 |
29 | private LocalDateTime created;
30 | private LocalDateTime updated;
31 |
32 | public static SpBoardSummary of(SpBoard board, User writer){
33 | return SpBoardSummary.builder()
34 | .boardId(board.getBoardId())
35 | .title(board.getTitle())
36 | .writerId(board.getWriterId())
37 | .writer(writer)
38 | .commentCount(board.getCommentList() == null ? 0 : board.getCommentList().size())
39 | .created(board.getCreated())
40 | .updated(board.getUpdated())
41 | .build();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/sp-board/src/main/java/com/sp/sec/board/repository/SpBoardRepository.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board.repository;
2 |
3 | import com.sp.sec.board.domain.SpBoard;
4 | import org.springframework.data.domain.Page;
5 | import org.springframework.data.domain.Pageable;
6 | import org.springframework.data.mongodb.repository.MongoRepository;
7 | import org.springframework.stereotype.Repository;
8 |
9 | @Repository
10 | public interface SpBoardRepository extends MongoRepository {
11 |
12 | Page findAllByOpenOrderByCreatedDesc(boolean open, Pageable pageable);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/sp-board/src/main/java/com/sp/sec/config/SpBoardModule.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.config;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
6 |
7 | @Configuration
8 | @ComponentScan("com.sp.sec.board")
9 | @EnableMongoRepositories(basePackages = {
10 | "com.sp.sec.board.repository"
11 | })
12 | public class SpBoardModule {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/sp-board/src/test/java/com/sp/sec/board/SpBoardTestApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.context.annotation.Profile;
7 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
8 |
9 | @SpringBootApplication
10 | public class SpBoardTestApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(SpBoardTestApplication.class, args);
14 | }
15 |
16 | @Profile("board-test")
17 | @Configuration
18 | @EnableMongoRepositories(basePackages = {
19 | "com.sp.sec.user.repository",
20 | "com.sp.sec.board.repository"
21 | })
22 | class MongoConfig{
23 |
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/sp-board/src/test/java/com/sp/sec/board/service/SpBoardTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.board.service;
2 |
3 | import com.sp.sec.board.domain.Comment;
4 | import com.sp.sec.board.domain.SpBoard;
5 | import com.sp.sec.board.domain.SpBoardSummary;
6 | import com.sp.sec.user.domain.User;
7 | import lombok.AllArgsConstructor;
8 |
9 | import static org.junit.jupiter.api.Assertions.assertEquals;
10 | import static org.junit.jupiter.api.Assertions.assertNotNull;
11 |
12 | @AllArgsConstructor
13 | public class SpBoardTestHelper {
14 |
15 | private final SpBoardService boardService;
16 |
17 | public static SpBoard makeBoard(User user, String title, String content){
18 | return SpBoard.builder()
19 | .writerId(user.getUserId())
20 | .open(true)
21 | .title(title)
22 | .content(content)
23 | .build();
24 | }
25 |
26 | public SpBoard createBoard(User user, String title, String content){
27 | return boardService.save(makeBoard(user, title, content));
28 | }
29 |
30 | public static void assertBoard(SpBoard board, User user, String title, String content){
31 | assertNotNull(board.getBoardId());
32 | assertNotNull(board.getCreated());
33 | assertNotNull(board.getUpdated());
34 | assertEquals(user.getUserId(), board.getWriterId());
35 | assertEquals(title, board.getTitle());
36 | assertEquals(content, board.getContent());
37 | }
38 |
39 | public static Comment makeComment(User user, String commentStr){
40 | return Comment.builder()
41 | .userId(user.getUserId())
42 | .userName(user.getName())
43 | .comment(commentStr)
44 | .build();
45 | }
46 |
47 | public Comment createComment(String boardId, User user, String commentStr){
48 | return boardService.addComment(boardId, makeComment(user, commentStr));
49 | }
50 |
51 | public static void assertComment(Comment comment, User user, String commentStr){
52 | assertNotNull(comment.getCreated());
53 | assertNotNull(comment.getCommentId());
54 | assertEquals(user.getUserId(), comment.getUserId());
55 | assertEquals(user.getName(), comment.getUserName());
56 | assertEquals(commentStr, comment.getComment());
57 | }
58 |
59 |
60 | public static void assertBoardSummary(SpBoardSummary summary,
61 | SpBoard board, int count){
62 | assertEquals(board.getBoardId(), summary.getBoardId());
63 | assertEquals(board.getTitle(), summary.getTitle());
64 | assertEquals(board.getWriterId(), summary.getWriterId());
65 | assertEquals(board.getWriter().getName(), summary.getWriter().getName());
66 | assertEquals(count, summary.getCommentCount());
67 | assertNotNull(summary.getCreated());
68 | assertNotNull(summary.getUpdated());
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/sp-jwt-security/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | spring-boot-dependencies
8 | org.springframework.boot
9 | 2.4.0
10 |
11 | com.sp.sec
12 | sp-jwt-security
13 | 1.0.0
14 |
15 |
16 | 11
17 | ${java.version}
18 | ${java.version}
19 | UTF-8
20 | UTF-8
21 |
22 |
23 |
24 |
25 |
26 | com.sp.sec
27 | user-authority
28 | 1.0.0
29 |
30 |
31 |
32 | com.sp.sec
33 | user-authority
34 | 1.0.0
35 | test-jar
36 | test
37 |
38 |
39 |
40 | javax.servlet
41 | javax.servlet-api
42 |
43 |
44 | com.fasterxml.jackson.core
45 | jackson-databind
46 |
47 |
48 | org.projectlombok
49 | lombok
50 |
51 |
52 | com.auth0
53 | java-jwt
54 | 3.11.0
55 | compile
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | org.apache.maven.plugins
64 | maven-jar-plugin
65 |
66 |
67 |
68 | test-jar
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/config/SpJwtSecurityModule.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.config;
2 |
3 | import com.sp.sec.web.config.SpJwtProperties;
4 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | @Configuration
8 | @EnableConfigurationProperties({SpJwtProperties.class})
9 | public class SpJwtSecurityModule {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/JWTCheckFilter.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.sp.sec.user.domain.User;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.security.authentication.AuthenticationManager;
6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
7 | import org.springframework.security.core.context.SecurityContextHolder;
8 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
9 |
10 | import javax.servlet.FilterChain;
11 | import javax.servlet.ServletException;
12 | import javax.servlet.http.HttpServletRequest;
13 | import javax.servlet.http.HttpServletResponse;
14 | import java.io.IOException;
15 |
16 | public class JWTCheckFilter extends BasicAuthenticationFilter {
17 |
18 | private final UserService userService;
19 | private final JWTUtil jwtUtil;
20 |
21 | public JWTCheckFilter(AuthenticationManager authenticationManager, UserService userService, JWTUtil jwtUtil) {
22 | super(authenticationManager);
23 | this.userService = userService;
24 | this.jwtUtil = jwtUtil;
25 | }
26 |
27 | @Override
28 | protected void doFilterInternal(
29 | HttpServletRequest request,
30 | HttpServletResponse response,
31 | FilterChain chain) throws IOException, ServletException
32 | {
33 | String token = request.getHeader(JWTUtil.AUTH_HEADER);
34 | if(token == null || !token.startsWith(JWTUtil.BEARER)){
35 | chain.doFilter(request, response);
36 | return;
37 | }
38 | VerifyResult result = jwtUtil.verify(token.substring(JWTUtil.BEARER.length()));
39 | if(result.isResult()){
40 | // TODO : user cacher ....
41 | User user = userService.findUser(result.getUserId()).get();
42 | SecurityContextHolder.getContext().setAuthentication(
43 | new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities())
44 | );
45 | }
46 | chain.doFilter(request, response);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/JWTLoginFilter.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.user.domain.User;
5 | import lombok.SneakyThrows;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
8 | import org.springframework.security.core.Authentication;
9 | import org.springframework.security.core.AuthenticationException;
10 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
11 |
12 | import javax.servlet.FilterChain;
13 | import javax.servlet.ServletException;
14 | import javax.servlet.http.HttpServletRequest;
15 | import javax.servlet.http.HttpServletResponse;
16 | import java.io.IOException;
17 |
18 | public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {
19 |
20 | private final AuthenticationManager authenticationManager;
21 | private final JWTUtil jwtUtil;
22 | private final ObjectMapper objectMapper;
23 |
24 | public JWTLoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil, ObjectMapper objectMapper){
25 | this.authenticationManager = authenticationManager;
26 | this.jwtUtil = jwtUtil;
27 | this.objectMapper = objectMapper;
28 | setFilterProcessesUrl("/login");
29 | }
30 |
31 | @SneakyThrows
32 | @Override
33 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
34 | UserLogin userLogin = objectMapper.readValue(request.getInputStream(), UserLogin.class);
35 | UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
36 | userLogin.getUsername(), userLogin.getPassword(), null
37 | );
38 | return authenticationManager.authenticate(authToken);
39 | }
40 |
41 | @Override
42 | protected void successfulAuthentication(
43 | HttpServletRequest request,
44 | HttpServletResponse response,
45 | FilterChain chain,
46 | Authentication authResult) throws IOException, ServletException
47 | {
48 | User user = (User)authResult.getPrincipal();
49 | response.addHeader(JWTUtil.AUTH_HEADER, JWTUtil.BEARER+jwtUtil.generate(user.getUserId()));
50 | // super.successfulAuthentication(request, response, chain, authResult);
51 | }
52 |
53 | @Override
54 | protected void unsuccessfulAuthentication(
55 | HttpServletRequest request,
56 | HttpServletResponse response,
57 | AuthenticationException failed) throws IOException, ServletException
58 | {
59 | System.out.println(failed.getMessage());
60 | super.unsuccessfulAuthentication(request, response, failed);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/JWTUtil.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.algorithms.Algorithm;
5 | import com.auth0.jwt.exceptions.JWTVerificationException;
6 | import com.auth0.jwt.interfaces.DecodedJWT;
7 | import org.springframework.stereotype.Component;
8 |
9 | import java.time.Instant;
10 |
11 | @Component
12 | public class JWTUtil {
13 |
14 | public static final String AUTH_HEADER = "Authentication";
15 | public static final String REFRESH_HEADER = "refresh-token";
16 | public static final String BEARER = "Bearer ";
17 |
18 | private Algorithm AL;
19 | public static enum TokenType {
20 | access,
21 | refresh
22 | }
23 |
24 | SpJwtProperties properties;
25 |
26 | public SpJwtProperties getProperties() {
27 | return properties;
28 | }
29 |
30 | public JWTUtil(SpJwtProperties properties){
31 | this.properties = properties;
32 | this.AL = Algorithm.HMAC512(properties.getSecret());
33 | }
34 |
35 | public String generate(String userId){
36 | return generate(userId, TokenType.access);
37 | }
38 |
39 | public String generate(String userId, TokenType type){
40 | return JWT.create().withSubject(userId)
41 | .withClaim("exp", Instant.now().getEpochSecond()+ getLifeTime(type))
42 | .sign(AL);
43 | }
44 |
45 | private long getLifeTime(TokenType type) {
46 | switch(type){
47 | case refresh:
48 | return this.properties.getTokenRefreshTime();
49 | case access:
50 | default:
51 | return this.properties.getTokenLifeTime();
52 | }
53 | }
54 |
55 | public VerifyResult verify(String token){
56 | try{
57 | DecodedJWT decode = JWT.require(AL).build().verify(token);
58 | return VerifyResult.builder().userId(decode.getSubject()).result(true).build();
59 | }catch(JWTVerificationException ex){
60 | DecodedJWT decode = JWT.decode(token);
61 | return VerifyResult.builder().userId(decode.getSubject()).result(false).build();
62 | }
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/RefreshableJWTLoginFilter.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.auth0.jwt.exceptions.TokenExpiredException;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.service.UserService;
7 | import lombok.SneakyThrows;
8 | import org.springframework.security.authentication.AuthenticationManager;
9 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
10 | import org.springframework.security.core.Authentication;
11 | import org.springframework.security.core.AuthenticationException;
12 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
13 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
14 | import org.springframework.util.StringUtils;
15 |
16 | import javax.servlet.FilterChain;
17 | import javax.servlet.ServletException;
18 | import javax.servlet.http.HttpServletRequest;
19 | import javax.servlet.http.HttpServletResponse;
20 | import java.io.IOException;
21 |
22 | public class RefreshableJWTLoginFilter extends UsernamePasswordAuthenticationFilter {
23 |
24 | private final AuthenticationManager authenticationManager;
25 | private final UserService userService;
26 | private final JWTUtil jwtUtil;
27 | private final ObjectMapper objectMapper;
28 |
29 | public RefreshableJWTLoginFilter(AuthenticationManager authenticationManager, UserService userService, JWTUtil jwtUtil, ObjectMapper objectMapper){
30 | this.authenticationManager = authenticationManager;
31 | this.userService = userService;
32 | this.jwtUtil = jwtUtil;
33 | this.objectMapper = objectMapper;
34 | setFilterProcessesUrl("/login");
35 | }
36 |
37 | @SneakyThrows
38 | @Override
39 | public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
40 | UserLogin userLogin = objectMapper.readValue(request.getInputStream(), UserLogin.class);
41 |
42 | // id password login
43 | if(userLogin.getType().equals(UserLogin.Type.login)){
44 | UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
45 | userLogin.getUsername(), userLogin.getPassword(), null
46 | );
47 | return authenticationManager.authenticate(authToken);
48 | }else if(userLogin.getType().equals(UserLogin.Type.refresh)){
49 | // refresh token
50 | if(StringUtils.isEmpty(userLogin.getRefreshToken()))
51 | throw new IllegalArgumentException("리프레쉬 토큰이 필요함. : "+userLogin.getRefreshToken());
52 |
53 | VerifyResult result = jwtUtil.verify(userLogin.getRefreshToken());
54 | if(result.isResult()){
55 | User user = userService.findUser(result.getUserId()).orElseThrow(() -> new UsernameNotFoundException("알 수 없는 사용자 : " + result.getUserId()));
56 | return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
57 | }else{
58 | throw new TokenExpiredException("리프레쉬 토큰 만료");
59 | }
60 | }else{
61 | throw new IllegalArgumentException("알 수 없는 타입 : "+userLogin.getType());
62 | }
63 | }
64 |
65 | @Override
66 | protected void successfulAuthentication(
67 | HttpServletRequest request,
68 | HttpServletResponse response,
69 | FilterChain chain,
70 | Authentication authResult) throws IOException, ServletException
71 | {
72 | User user = (User)authResult.getPrincipal();
73 | response.addHeader(JWTUtil.AUTH_HEADER, JWTUtil.BEARER+jwtUtil.generate(user.getUserId(), JWTUtil.TokenType.access));
74 | response.addHeader(JWTUtil.REFRESH_HEADER, jwtUtil.generate(user.getUserId(), JWTUtil.TokenType.refresh));
75 | }
76 |
77 | @Override
78 | protected void unsuccessfulAuthentication(
79 | HttpServletRequest request,
80 | HttpServletResponse response,
81 | AuthenticationException failed) throws IOException, ServletException
82 | {
83 | System.out.println(failed.getMessage());
84 | super.unsuccessfulAuthentication(request, response, failed);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/SpJwtProperties.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 |
4 | import lombok.Data;
5 | import org.springframework.boot.context.properties.ConfigurationProperties;
6 |
7 | @Data
8 | @ConfigurationProperties(prefix = "sp.jwt")
9 | public class SpJwtProperties {
10 |
11 | private String secret = "default-secret-value";
12 | private long tokenLifeTime = 600;
13 | private long tokenRefreshTime = 24*60*60; // 86400
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/UserLogin.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 |
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 |
9 | @Data
10 | @AllArgsConstructor
11 | @NoArgsConstructor
12 | @Builder
13 | public class UserLogin {
14 |
15 | public enum Type {
16 | login,
17 | refresh
18 | }
19 | private Type type;
20 | private String username;
21 | private String password;
22 | private String refreshToken;
23 | }
24 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/java/com/sp/sec/web/config/VerifyResult.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | @Builder
12 | public class VerifyResult {
13 |
14 | private String userId;
15 | private boolean result;
16 | }
17 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/main/resources/META-INF/spring-configuration-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "groups": [{
3 | "name": "sp.jwt",
4 | "type": "com.sp.sec.web.config.SpJwtProperties"
5 | }],
6 | "properties": [{
7 | "name": "sp.jwt.secret",
8 | "type": "java.lang.String"
9 | }, {
10 | "name": "sp.jwt.token-life-time",
11 | "type": "java.lang.Long"
12 | }, {
13 | "name": "sp.jwt.token-refresh-time",
14 | "type": "java.lang.Long"
15 | }],
16 | "hints": []
17 | }
--------------------------------------------------------------------------------
/sp-jwt-security/src/test/java/com/sp/sec/web/SpIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.web.config.JWTUtil;
5 | import com.sp.sec.web.config.UserLogin;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.web.server.LocalServerPort;
8 | import org.springframework.http.HttpEntity;
9 | import org.springframework.http.HttpHeaders;
10 | import org.springframework.http.HttpMethod;
11 | import org.springframework.http.ResponseEntity;
12 | import org.springframework.web.client.RestTemplate;
13 |
14 | import java.net.URI;
15 | import java.net.URISyntaxException;
16 |
17 | import static java.lang.String.format;
18 |
19 | public class SpIntegrationTest {
20 |
21 | @LocalServerPort
22 | protected int port;
23 |
24 | @Autowired
25 | protected ObjectMapper objectMapper;
26 |
27 | protected RestTemplate restTemplate = new RestTemplate();
28 |
29 |
30 | protected URI uri(String path) throws URISyntaxException {
31 | return new URI(format("http://localhost:%d%s", port, path));
32 | }
33 |
34 | protected URI uri(String path, String... args) throws URISyntaxException {
35 | return uri(format(path, args));
36 | }
37 |
38 | protected Tokens getToken(String username, String password) throws URISyntaxException {
39 | UserLogin login = UserLogin.builder().type(UserLogin.Type.login).username(username).password(password).build();
40 | HttpEntity body = new HttpEntity<>(login);
41 | ResponseEntity response = restTemplate.exchange(uri("/login"),
42 | HttpMethod.POST, body, String.class);
43 | return Tokens.builder().accessToken(getAccessToken(response)).build();
44 | }
45 |
46 | protected String getAccessToken(ResponseEntity response) {
47 | return response.getHeaders().get(JWTUtil.AUTH_HEADER).get(0)
48 | .substring(JWTUtil.BEARER.length());
49 | }
50 |
51 | protected HttpEntity getAuthHeaderEntity(String accessToken) {
52 | return getPostAuthHeaderEntity(accessToken, null);
53 | }
54 |
55 | protected HttpEntity getPostAuthHeaderEntity(String accessToken, Object object) {
56 | HttpHeaders headers = new HttpHeaders();
57 | headers.add(JWTUtil.AUTH_HEADER, JWTUtil.BEARER+ accessToken);
58 | HttpEntity entity = new HttpEntity(object, headers);
59 | return entity;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/test/java/com/sp/sec/web/SpJwtRefreshableTwoUserIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.sp.sec.user.UserTestHelper;
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.service.UserService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 |
10 | public class SpJwtRefreshableTwoUserIntegrationTest extends SpRefreshableIntegrationTest {
11 |
12 | @Autowired
13 | protected UserService userService;
14 |
15 | @Autowired
16 | private PasswordEncoder passwordEncoder;
17 | protected UserTestHelper userTestHelper;
18 |
19 | protected User USER1;
20 | protected User USER2;
21 |
22 | protected void prepareTwoUsers(){
23 | userService.clearUsers();
24 | this.userTestHelper = new UserTestHelper(userService, passwordEncoder);
25 | this.USER1 = this.userTestHelper.createUser("user1", Authority.ROLE_USER);
26 | this.USER2 = this.userTestHelper.createUser("user2", Authority.ROLE_USER);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/test/java/com/sp/sec/web/SpJwtTwoUserIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.sp.sec.user.UserTestHelper;
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.service.UserService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 |
10 | public class SpJwtTwoUserIntegrationTest extends SpIntegrationTest{
11 |
12 | @Autowired
13 | protected UserService userService;
14 |
15 | @Autowired
16 | private PasswordEncoder passwordEncoder;
17 | protected UserTestHelper userTestHelper;
18 |
19 | protected User USER1;
20 | protected User USER2;
21 |
22 | protected void prepareTwoUsers(){
23 | userService.clearUsers();
24 | this.userTestHelper = new UserTestHelper(userService, passwordEncoder);
25 | this.USER1 = this.userTestHelper.createUser("user1", Authority.ROLE_USER);
26 | this.USER2 = this.userTestHelper.createUser("user2", Authority.ROLE_USER);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/test/java/com/sp/sec/web/SpJwtUserAdminIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.sp.sec.user.UserTestHelper;
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.service.UserService;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 |
10 | public class SpJwtUserAdminIntegrationTest extends SpIntegrationTest{
11 |
12 | @Autowired
13 | protected UserService userService;
14 |
15 | @Autowired
16 | private PasswordEncoder passwordEncoder;
17 | protected UserTestHelper userTestHelper;
18 |
19 | protected User USER1;
20 | protected User ADMIN;
21 |
22 | protected void prepareUserAdmin(){
23 | userService.clearUsers();
24 | this.userTestHelper = new UserTestHelper(userService, passwordEncoder);
25 | this.USER1 = this.userTestHelper.createUser("user1", Authority.ROLE_USER);
26 | this.ADMIN = this.userTestHelper.createUser("admin", Authority.ROLE_ADMIN);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/test/java/com/sp/sec/web/SpRefreshableIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.sp.sec.web.config.JWTUtil;
4 | import com.sp.sec.web.config.UserLogin;
5 | import org.springframework.http.HttpEntity;
6 | import org.springframework.http.HttpMethod;
7 | import org.springframework.http.ResponseEntity;
8 |
9 | import java.net.URISyntaxException;
10 |
11 | public class SpRefreshableIntegrationTest extends SpIntegrationTest {
12 |
13 |
14 | protected Tokens getToken(String username, String password) throws URISyntaxException {
15 | UserLogin login = UserLogin.builder().type(UserLogin.Type.login).username(username).password(password).build();
16 | HttpEntity body = new HttpEntity<>(login);
17 | ResponseEntity response = restTemplate.exchange(uri("/login"),
18 | HttpMethod.POST, body, String.class);
19 | return Tokens.builder()
20 | .accessToken(getAccessToken(response))
21 | .refreshToken(getRefreshToken(response))
22 | .build();
23 | }
24 |
25 | protected Tokens getRefreshToken(String refreshToken) throws URISyntaxException {
26 | UserLogin login = UserLogin.builder().type(UserLogin.Type.refresh)
27 | .refreshToken(refreshToken).build();
28 | HttpEntity body = new HttpEntity<>(login);
29 | ResponseEntity response = restTemplate.exchange(uri("/login"),
30 | HttpMethod.POST, body, String.class);
31 | return Tokens.builder()
32 | .accessToken(getAccessToken(response))
33 | .refreshToken(getRefreshToken(response))
34 | .build();
35 | }
36 |
37 | protected String getRefreshToken(ResponseEntity response) {
38 | return response.getHeaders().get(JWTUtil.REFRESH_HEADER).get(0);
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/sp-jwt-security/src/test/java/com/sp/sec/web/Tokens.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 |
8 | @Data
9 | @AllArgsConstructor
10 | @NoArgsConstructor
11 | @Builder
12 | public class Tokens {
13 |
14 | private String accessToken;
15 | private String refreshToken;
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/sp-web-util/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | spring-boot-dependencies
8 | org.springframework.boot
9 | 2.4.0
10 |
11 | com.sp.sec
12 | sp-web-util
13 | 1.0.0
14 |
15 |
16 | 11
17 | ${java.version}
18 | ${java.version}
19 | UTF-8
20 | UTF-8
21 |
22 |
23 |
24 |
25 |
26 | com.fasterxml.jackson.core
27 | jackson-annotations
28 |
29 |
30 | org.springframework.data
31 | spring-data-commons
32 |
33 |
34 | com.fasterxml.jackson.core
35 | jackson-databind
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/sp-web-util/src/main/java/com/sp/sec/web/util/RestResponsePage.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.util;
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator;
4 | import com.fasterxml.jackson.annotation.JsonProperty;
5 | import com.fasterxml.jackson.databind.JsonNode;
6 | import org.springframework.data.domain.Page;
7 | import org.springframework.data.domain.PageImpl;
8 | import org.springframework.data.domain.PageRequest;
9 | import org.springframework.data.domain.Pageable;
10 |
11 | import java.util.ArrayList;
12 | import java.util.List;
13 | import java.util.stream.Collectors;
14 | import java.util.stream.Stream;
15 |
16 | public class RestResponsePage extends PageImpl {
17 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
18 | public RestResponsePage(@JsonProperty("content") List content,
19 | @JsonProperty("number") int number,
20 | @JsonProperty("size") int size,
21 | @JsonProperty("totalElements") Long totalElements,
22 | @JsonProperty("pageable") JsonNode pageable,
23 | @JsonProperty("last") boolean last,
24 | @JsonProperty("totalPages") int totalPages,
25 | @JsonProperty("sort") JsonNode sort,
26 | @JsonProperty("first") boolean first,
27 | @JsonProperty("numberOfElements") int numberOfElements) {
28 |
29 | super(content, PageRequest.of(number, size), totalElements);
30 | }
31 |
32 | public static RestResponsePage of(Page page){
33 | return new RestResponsePage(page);
34 | }
35 |
36 | public RestResponsePage(Page page) {
37 | super(page.getContent(), page.getPageable(), page.getTotalElements());
38 | }
39 |
40 | public RestResponsePage(List content, Pageable pageable, long total) {
41 | super(content, pageable, total);
42 | }
43 |
44 | public RestResponsePage(List content) {
45 | super(content);
46 | }
47 |
48 | public RestResponsePage(T ... array) {
49 | super(Stream.of(array).collect(Collectors.toList()));
50 | }
51 |
52 | public RestResponsePage() {
53 | super(new ArrayList<>());
54 | }
55 | }
--------------------------------------------------------------------------------
/user-authority/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/user-authority/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/user-authority/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/user-authority/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-dependencies
8 | 2.4.0
9 |
10 | com.sp.sec
11 | user-authority
12 | 1.0.0
13 | user-authority
14 | 사용자 모듈
15 |
16 |
17 | 11
18 | ${java.version}
19 | ${java.version}
20 | UTF-8
21 | UTF-8
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-data-mongodb
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-security
32 |
33 |
34 |
35 | org.projectlombok
36 | lombok
37 | true
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-test
42 | test
43 |
44 |
45 | org.junit.vintage
46 | junit-vintage-engine
47 |
48 |
49 |
50 |
51 | de.flapdoodle.embed
52 | de.flapdoodle.embed.mongo
53 | test
54 |
55 |
56 | org.springframework.security
57 | spring-security-test
58 | test
59 |
60 |
61 | com.fasterxml.jackson.core
62 | jackson-annotations
63 |
64 |
65 | com.sp.sec
66 | sp-web-util
67 | 1.0.0
68 | compile
69 |
70 |
71 |
72 |
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-jar-plugin
77 |
78 |
79 |
80 | test-jar
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/user-authority/src/main/java/com/sp/sec/config/UserAuthorityModule.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.config;
2 |
3 |
4 | import org.springframework.context.annotation.ComponentScan;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
7 |
8 | @Configuration
9 | @ComponentScan("com.sp.sec.user")
10 | @EnableMongoRepositories(basePackages = {
11 | "com.sp.sec.user.repository"
12 | })
13 | public class UserAuthorityModule {
14 |
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/user-authority/src/main/java/com/sp/sec/user/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.controller;
2 |
3 |
4 | import com.sp.sec.user.domain.User;
5 | import com.sp.sec.user.service.UserService;
6 | import com.sp.sec.web.util.RestResponsePage;
7 | import lombok.AllArgsConstructor;
8 | import org.springframework.security.access.prepost.PreAuthorize;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | import java.util.Optional;
12 |
13 | @RestController
14 | @RequestMapping("/user")
15 | @AllArgsConstructor
16 | public class UserController {
17 |
18 |
19 | private final UserService userService;
20 |
21 | // save
22 | @PostMapping("/save")
23 | public User saveUser(
24 | @RequestBody User user
25 | ){
26 | return userService.save(user);
27 | }
28 |
29 | @GetMapping("/{userId}")
30 | public Optional getUser(@PathVariable String userId){
31 | return userService.findUser(userId);
32 | }
33 |
34 | // list : page
35 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
36 | @GetMapping("/list")
37 | public RestResponsePage list(
38 | @RequestParam(defaultValue = "1") Integer page,
39 | @RequestParam(defaultValue = "10") Integer size
40 | ){
41 | return RestResponsePage.of(userService.listUsers(page, size));
42 | }
43 |
44 | // add role
45 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
46 | @PutMapping("/authority/add")
47 | public Optional addAuthority(
48 | @RequestParam String userId,
49 | @RequestParam String authority
50 | ){
51 | userService.findUser(userId).ifPresent(user->{
52 | userService.addAuthority(userId, authority);
53 | });
54 | return userService.findUser(userId);
55 | }
56 |
57 | // remove role
58 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
59 | @PutMapping("/authority/remove")
60 | public Optional removeAuthority(
61 | @RequestParam String userId,
62 | @RequestParam String authority
63 | ){
64 | userService.findUser(userId).ifPresent(user->{
65 | userService.removeAuthority(userId, authority);
66 | });
67 | return userService.findUser(userId);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/user-authority/src/main/java/com/sp/sec/user/domain/Authority.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.domain;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Builder;
5 | import lombok.Data;
6 | import lombok.NoArgsConstructor;
7 | import org.springframework.security.core.GrantedAuthority;
8 |
9 | import java.util.Objects;
10 |
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | @Builder
15 | public class Authority implements GrantedAuthority {
16 |
17 | public static final String ROLE_USER = "ROLE_USER";
18 | public static final String ROLE_ADMIN = "ROLE_ADMIN";
19 | public static final Authority USER = new Authority(ROLE_USER);
20 | public static final Authority ADMIN = new Authority(ROLE_ADMIN);
21 |
22 | private String authority;
23 |
24 | @Override
25 | public boolean equals(Object o) {
26 | if (this == o) return true;
27 | if (o == null || getClass() != o.getClass()) return false;
28 | Authority authority1 = (Authority) o;
29 | return Objects.equals(authority, authority1.authority);
30 | }
31 |
32 | @Override
33 | public int hashCode() {
34 | return Objects.hash(authority);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/user-authority/src/main/java/com/sp/sec/user/domain/User.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.domain;
2 |
3 | import com.fasterxml.jackson.annotation.JsonIgnore;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import org.springframework.data.annotation.Id;
9 | import org.springframework.data.mongodb.core.index.Indexed;
10 | import org.springframework.data.mongodb.core.mapping.Document;
11 | import org.springframework.security.core.userdetails.UserDetails;
12 |
13 | import java.time.LocalDateTime;
14 | import java.util.Set;
15 |
16 | @Document(collection = "sp_user")
17 | @Data
18 | @AllArgsConstructor
19 | @NoArgsConstructor
20 | @Builder
21 | public class User implements UserDetails {
22 |
23 | @Id
24 | private String userId;
25 |
26 | @Indexed(unique = true)
27 | private String email;
28 | private String name;
29 | private String picUrl;
30 |
31 | @JsonIgnore
32 | private String password;
33 |
34 | private boolean enabled;
35 |
36 | private Set authorities;
37 |
38 | private LocalDateTime created;
39 | private LocalDateTime updated;
40 |
41 | @Override
42 | public String getUsername() {
43 | return email;
44 | }
45 |
46 | @Override
47 | public boolean isAccountNonExpired() {
48 | return enabled;
49 | }
50 |
51 | @Override
52 | public boolean isAccountNonLocked() {
53 | return enabled;
54 | }
55 |
56 | @Override
57 | public boolean isCredentialsNonExpired() {
58 | return enabled;
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/user-authority/src/main/java/com/sp/sec/user/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.repository;
2 |
3 | import com.sp.sec.user.domain.User;
4 | import org.springframework.data.mongodb.repository.MongoRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | import java.util.Optional;
8 |
9 | @Repository
10 | public interface UserRepository extends MongoRepository {
11 |
12 | Optional findByEmail(String email);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/user-authority/src/main/java/com/sp/sec/user/service/UserService.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.service;
2 |
3 |
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.repository.UserRepository;
7 | import org.springframework.dao.DuplicateKeyException;
8 | import org.springframework.data.domain.Page;
9 | import org.springframework.data.domain.PageRequest;
10 | import org.springframework.data.domain.Sort;
11 | import org.springframework.data.mongodb.core.MongoTemplate;
12 | import org.springframework.data.mongodb.core.index.Index;
13 | import org.springframework.data.mongodb.core.query.Criteria;
14 | import org.springframework.data.mongodb.core.query.Query;
15 | import org.springframework.data.mongodb.core.query.Update;
16 | import org.springframework.security.core.userdetails.UserDetails;
17 | import org.springframework.security.core.userdetails.UserDetailsService;
18 | import org.springframework.security.core.userdetails.UsernameNotFoundException;
19 | import org.springframework.stereotype.Service;
20 | import org.springframework.util.StringUtils;
21 |
22 | import java.time.LocalDateTime;
23 | import java.util.Collection;
24 | import java.util.HashMap;
25 | import java.util.Map;
26 | import java.util.Optional;
27 | import java.util.function.Function;
28 | import java.util.stream.Collectors;
29 | import java.util.stream.StreamSupport;
30 |
31 | @Service
32 | public class UserService implements UserDetailsService {
33 |
34 | protected final MongoTemplate mongoTemplate;
35 | protected final UserRepository userRepository;
36 |
37 | public UserService(MongoTemplate mongoTemplate, UserRepository userRepository) {
38 | this.mongoTemplate = mongoTemplate;
39 | this.userRepository = userRepository;
40 | ensureIndex();
41 | }
42 |
43 | private void ensureIndex() {
44 | mongoTemplate.indexOps(User.class).
45 | ensureIndex(new Index().on("email", Sort.Direction.ASC).unique());
46 | }
47 |
48 | public User save(User user) throws DuplicateKeyException {
49 | if(StringUtils.isEmpty(user.getUserId())){
50 | user.setCreated(LocalDateTime.now());
51 | }
52 | user.setUpdated(LocalDateTime.now());
53 | // user.setEnabled(true);
54 | return userRepository.save(user);
55 | }
56 |
57 | public Optional findUser(String userId){
58 | return userRepository.findById(userId);
59 | }
60 |
61 | public boolean updateUserName(String userId, String userName){
62 | Update update = new Update();
63 | update.set("name", userName);
64 | update.set("updated", LocalDateTime.now());
65 | return mongoTemplate.updateFirst(Query.query(Criteria.where("userId").is(userId)),
66 | update, User.class).wasAcknowledged();
67 | }
68 |
69 | @Override
70 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
71 | return userRepository.findByEmail(username).orElseThrow(()->new UsernameNotFoundException(username+"이 존재하지 않음"));
72 | }
73 |
74 | public boolean addAuthority(String userId, String authority){
75 | Update update = new Update();
76 | update.push("authorities", new Authority((authority)));
77 | update.set("updated", LocalDateTime.now());
78 | return mongoTemplate.updateFirst(Query.query(Criteria.where("userId").is(userId)),
79 | update, User.class).wasAcknowledged();
80 | }
81 |
82 | public boolean removeAuthority(String userId, String authority){
83 | Update update = new Update();
84 | update.pull("authorities", new Authority((authority)));
85 | update.set("updated", LocalDateTime.now());
86 | return mongoTemplate.updateFirst(Query.query(Criteria.where("userId").is(userId)),
87 | update, User.class).wasAcknowledged();
88 | }
89 |
90 | public void clearUsers() {
91 | userRepository.deleteAll();
92 | }
93 |
94 | public Page listUsers(Integer page, Integer size) {
95 | return userRepository.findAll(PageRequest.of(page-1, size));
96 | }
97 |
98 | public Map getUserMap(Collection userIds){
99 | if(userIds == null || userIds.isEmpty()) return new HashMap<>();
100 | return StreamSupport.stream(userRepository.findAllById(userIds).spliterator(), false)
101 | .collect(Collectors.toMap(User::getUserId, Function.identity()));
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/user-authority/src/test/java/com/sp/sec/user/UserAuthorityJpaTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user;
2 |
3 |
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.repository.UserRepository;
7 | import com.sp.sec.user.service.UserService;
8 | import org.junit.jupiter.api.BeforeEach;
9 | import org.junit.jupiter.api.DisplayName;
10 | import org.junit.jupiter.api.Test;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
13 | import org.springframework.dao.DuplicateKeyException;
14 | import org.springframework.data.mongodb.core.MongoTemplate;
15 | import org.springframework.security.crypto.password.NoOpPasswordEncoder;
16 | import org.springframework.test.context.ActiveProfiles;
17 |
18 | import java.util.List;
19 |
20 | import static org.junit.jupiter.api.Assertions.assertEquals;
21 | import static org.junit.jupiter.api.Assertions.assertThrows;
22 |
23 | @ActiveProfiles("test")
24 | @DataMongoTest
25 | public class UserAuthorityJpaTest extends WithUserTest {
26 |
27 | @BeforeEach
28 | void before(){
29 | prepareUserService();
30 | }
31 |
32 | @DisplayName("1. 사용자를 생성한다.")
33 | @Test
34 | void test_1() {
35 | userTestHelper.createUser("user1");
36 | List userList = this.userRepository.findAll();
37 |
38 | assertEquals(1, userList.size());
39 | userTestHelper.assertUser(userList.get(0), "user1");
40 | }
41 |
42 | @DisplayName("2. 사용자의 이름을 수정한다.")
43 | @Test
44 | void test_2() {
45 | User user1 = userTestHelper.createUser("user1");
46 | userService.updateUserName(user1.getUserId(), "user2");
47 |
48 | User savedUser = userService.findUser(user1.getUserId()).get();
49 | assertEquals("user2", savedUser.getName());
50 | }
51 |
52 | @DisplayName("3. authority를 부여한다.")
53 | @Test
54 | void test_3() {
55 | User user1 = userTestHelper.createUser("user1", Authority.ROLE_USER);
56 | userService.addAuthority(user1.getUserId(), Authority.ROLE_ADMIN);
57 | User savedUser = userService.findUser(user1.getUserId()).get();
58 | userTestHelper.assertUser(savedUser, "user1", Authority.ROLE_USER, Authority.ROLE_ADMIN);
59 | }
60 |
61 |
62 | @DisplayName("4. authority를 뺏는다.")
63 | @Test
64 | void test_4() {
65 | User user1 = userTestHelper.createUser("admin", Authority.ROLE_USER, Authority.ROLE_ADMIN);
66 | userService.removeAuthority(user1.getUserId(), Authority.ROLE_USER);
67 | User savedUser = userService.findUser(user1.getUserId()).get();
68 | userTestHelper.assertUser(savedUser, "admin", Authority.ROLE_ADMIN);
69 | }
70 |
71 | @DisplayName("5. email 로 검색이 된다.")
72 | @Test
73 | void test_5() {
74 | User user1 = userTestHelper.createUser("user1");
75 | User saved = (User) userService.loadUserByUsername("user1@test.com");
76 | userTestHelper.assertUser(saved, "user1");
77 | }
78 |
79 | @DisplayName("6. role이 중복되서 추가되지 않는다.")
80 | @Test
81 | void test_6() {
82 | User user1 = userTestHelper.createUser("user1", Authority.ROLE_USER);
83 | userService.addAuthority(user1.getUserId(), Authority.ROLE_USER);
84 | userService.addAuthority(user1.getUserId(), Authority.ROLE_USER);
85 | User savedUser = userService.findUser(user1.getUserId()).get();
86 | userTestHelper.assertUser(savedUser, "user1", Authority.ROLE_USER);
87 | }
88 |
89 | @DisplayName("7. email이 중복되어서 들어가는가?")
90 | @Test
91 | void test_() {
92 | userTestHelper.createUser("user1");
93 | assertThrows(DuplicateKeyException.class, ()->{
94 | userTestHelper.createUser("user1");
95 | });
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/user-authority/src/test/java/com/sp/sec/user/UserAuthorityTestApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.boot.test.context.SpringBootTest;
7 |
8 | @SpringBootApplication
9 | public class UserAuthorityTestApplication {
10 |
11 | public static void main(String[] args){
12 | SpringApplication.run(UserAuthorityTestApplication.class, args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/user-authority/src/test/java/com/sp/sec/user/UserTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user;
2 |
3 |
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.service.UserService;
7 | import lombok.AllArgsConstructor;
8 | import org.springframework.dao.DuplicateKeyException;
9 | import org.springframework.security.crypto.password.PasswordEncoder;
10 |
11 | import java.util.stream.Collectors;
12 | import java.util.stream.Stream;
13 |
14 | import static org.junit.jupiter.api.Assertions.*;
15 |
16 | @AllArgsConstructor
17 | public class UserTestHelper {
18 |
19 | private final UserService userService;
20 |
21 | private final PasswordEncoder passwordEncoder;
22 |
23 | public User createUser(String name) throws DuplicateKeyException {
24 | User user = User.builder()
25 | .name(name)
26 | .email(name+"@test.com")
27 | .password(passwordEncoder.encode(name+"123"))
28 | .enabled(true)
29 | .build();
30 | return userService.save(user);
31 | }
32 |
33 | public User createUser(String name, String... authorities){
34 | User user = createUser(name);
35 | Stream.of(authorities).forEach(auth->userService.addAuthority(user.getUserId(), auth));
36 | return user;
37 | }
38 |
39 | public void assertUser(User user, String name){
40 | assertNotNull(user.getUserId());
41 | assertNotNull(user.getCreated());
42 | assertNotNull(user.getUpdated());
43 | assertTrue(user.isEnabled());
44 | assertEquals(name, user.getName());
45 | assertEquals(name+"@test.com", user.getEmail());
46 | // assertEquals(name+"123", user.getPassword());
47 | }
48 |
49 | public void assertUser(User user, String name, String... authorities){
50 | assertUser(user, name);
51 | assertTrue(user.getAuthorities().containsAll(
52 | Stream.of(authorities).map(auth->new Authority(auth)).collect(Collectors.toList())
53 | ));
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/user-authority/src/test/java/com/sp/sec/user/WithUserTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user;
2 |
3 | import com.sp.sec.user.repository.UserRepository;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.data.mongodb.core.MongoTemplate;
7 | import org.springframework.security.crypto.password.NoOpPasswordEncoder;
8 |
9 | public class WithUserTest {
10 |
11 | @Autowired
12 | protected MongoTemplate mongoTemplate;
13 |
14 | @Autowired
15 | protected UserRepository userRepository;
16 |
17 | protected UserService userService;
18 |
19 | protected UserTestHelper userTestHelper;
20 |
21 | protected void prepareUserService(){
22 | this.userRepository.deleteAll();
23 | this.userService = new UserService(mongoTemplate, userRepository);
24 | this.userTestHelper = new UserTestHelper(userService, NoOpPasswordEncoder.getInstance());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/user-authority/src/test/resources/applicaiton-test.yml:
--------------------------------------------------------------------------------
1 |
2 | spring:
3 | data:
4 | mongodb:
5 | database: user-authority
6 | host: localhost
7 | port: 1111
8 |
9 |
--------------------------------------------------------------------------------
/user-oauth2-support/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.springframework.boot
8 | spring-boot-dependencies
9 | 2.4.0
10 |
11 | com.sp.sec
12 | user-oauth2-support
13 | 1.0.0
14 |
15 |
16 | 11
17 | ${java.version}
18 | ${java.version}
19 | UTF-8
20 | UTF-8
21 |
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-data-mongodb
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-oauth2-client
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-test
44 | test
45 |
46 |
47 | org.junit.vintage
48 | junit-vintage-engine
49 |
50 |
51 |
52 |
53 |
54 | de.flapdoodle.embed
55 | de.flapdoodle.embed.mongo
56 | test
57 |
58 |
59 |
60 | org.springframework.security
61 | spring-security-test
62 | test
63 |
64 |
65 |
66 | com.fasterxml.jackson.core
67 | jackson-annotations
68 |
69 |
70 |
71 | com.sp.sec
72 | user-authority
73 | 1.0.0
74 |
75 |
76 |
77 | com.sp.sec
78 | user-authority
79 | 1.0.0
80 | test-jar
81 | test
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | org.apache.maven.plugins
90 | maven-jar-plugin
91 |
92 |
93 |
94 | test-jar
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/config/UserOAuth2SupportModule.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.config;
2 |
3 |
4 | import org.springframework.context.annotation.ComponentScan;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
7 |
8 | @Configuration
9 | @ComponentScan("com.sp.sec.user.oauth2")
10 | @EnableMongoRepositories(basePackages = {
11 | "com.sp.sec.user.oauth2.repository"
12 | })
13 | public class UserOAuth2SupportModule {
14 | }
15 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/user/oauth2/domain/ExtendedUser.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2.domain;
2 |
3 | import com.sp.sec.user.domain.User;
4 | import lombok.Data;
5 | import lombok.NoArgsConstructor;
6 | import org.springframework.security.oauth2.core.user.OAuth2User;
7 |
8 | import java.util.Map;
9 |
10 | @Data
11 | @NoArgsConstructor
12 | public class ExtendedUser extends User implements OAuth2User {
13 |
14 | private Map attributes;
15 |
16 | public ExtendedUser(User user){
17 | super(user.getUserId(), user.getEmail(), user.getName(), user.getPicUrl(), user.getPassword()
18 | , user.isEnabled(), user.getAuthorities(), user.getCreated(), user.getUpdated());
19 | }
20 |
21 | public static ExtendedUser of(User user){
22 | return new ExtendedUser(user);
23 | }
24 |
25 | @Override
26 | public A getAttribute(String name) {
27 | return attributes == null ? null : (A) attributes.get(name);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/user/oauth2/domain/ProvidedOAuth2User.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2.domain;
2 |
3 |
4 | import lombok.AllArgsConstructor;
5 | import lombok.Builder;
6 | import lombok.Data;
7 | import lombok.NoArgsConstructor;
8 | import org.springframework.data.annotation.Id;
9 | import org.springframework.data.mongodb.core.mapping.Document;
10 | import org.springframework.security.oauth2.core.user.OAuth2User;
11 |
12 | import java.time.LocalDateTime;
13 | import java.util.Map;
14 |
15 | import static java.lang.String.format;
16 |
17 | @Data
18 | @AllArgsConstructor
19 | @NoArgsConstructor
20 | @Builder
21 | @Document(collection = "sp_provided_oauth2_user")
22 | public class ProvidedOAuth2User {
23 |
24 | @Id
25 | private String oauth2UserId;
26 |
27 | private String userId; // refer
28 |
29 | private Provider provider;
30 |
31 | private String name;
32 | private String email;
33 | private String picUrl;
34 |
35 | private LocalDateTime registered;
36 | private LocalDateTime lastLoggedIn;
37 |
38 | public static enum Provider {
39 | google {
40 | public ProvidedOAuth2User convert(OAuth2User user){
41 | return ProvidedOAuth2User.builder()
42 | .oauth2UserId(format("%s_%s", name(), user.getAttribute("sub")))
43 | .name(user.getAttribute("name"))
44 | .email(user.getAttribute("email"))
45 | .picUrl(user.getAttribute("picture"))
46 | .provider(google)
47 | .build();
48 | }
49 | },
50 | facebook{
51 | public ProvidedOAuth2User convert(OAuth2User user){
52 | return ProvidedOAuth2User.builder()
53 | .oauth2UserId(format("%s_%s", name(), user.getAttribute("id")))
54 | .name(user.getAttribute("name"))
55 | .email(user.getAttribute("email"))
56 | // .picUrl()
57 | .provider(facebook)
58 | .build();
59 | }
60 | },
61 | naver{
62 | public ProvidedOAuth2User convert(OAuth2User user){
63 | Map response = user.getAttribute("response");
64 | return ProvidedOAuth2User.builder()
65 | .oauth2UserId(format("%s_%s", name(), response.get("id")))
66 | .name(""+response.get("name"))
67 | .email(""+response.get("email"))
68 | .picUrl(""+response.get("profile_image"))
69 | .provider(naver)
70 | .build();
71 | }
72 | },
73 | kakao{
74 | public ProvidedOAuth2User convert(OAuth2User user){
75 | Map kakaoAccount = user.getAttribute("kakao_account");
76 | Map profile = (Map) kakaoAccount.get("profile");
77 | return ProvidedOAuth2User.builder()
78 | .oauth2UserId(format("%s_%s", name(), user.getAttribute("id")))
79 | .name(""+profile.get("nickname"))
80 | .email(""+kakaoAccount.get("email"))
81 | .picUrl(""+profile.get("thumbnail_image_url"))
82 | .provider(kakao)
83 | .build();
84 | }
85 | }
86 | ;
87 |
88 | public abstract ProvidedOAuth2User convert(OAuth2User user);
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/user/oauth2/repository/ExtendedUserRepository.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2.repository;
2 |
3 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
4 | import org.springframework.data.mongodb.repository.MongoRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | @Repository
8 | public interface ExtendedUserRepository extends MongoRepository {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/user/oauth2/repository/ProvidedOAuth2UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2.repository;
2 |
3 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
4 | import org.springframework.data.mongodb.repository.MongoRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | @Repository
8 | public interface ProvidedOAuth2UserRepository extends MongoRepository {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/user/oauth2/service/ExtendedUserService.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2.service;
2 |
3 | import com.sp.sec.user.domain.Authority;
4 | import com.sp.sec.user.domain.User;
5 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
6 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
7 | import com.sp.sec.user.oauth2.repository.ExtendedUserRepository;
8 | import com.sp.sec.user.oauth2.repository.ProvidedOAuth2UserRepository;
9 | import com.sp.sec.user.repository.UserRepository;
10 | import com.sp.sec.user.service.UserService;
11 | import org.springframework.data.mongodb.core.MongoTemplate;
12 | import org.springframework.data.mongodb.core.query.Criteria;
13 | import org.springframework.data.mongodb.core.query.Query;
14 | import org.springframework.security.oauth2.core.user.OAuth2User;
15 | import org.springframework.stereotype.Service;
16 |
17 | import java.time.LocalDateTime;
18 | import java.util.List;
19 | import java.util.Optional;
20 | import java.util.Set;
21 |
22 | @Service
23 | public class ExtendedUserService extends UserService {
24 |
25 | private final ExtendedUserRepository extendedUserRepository;
26 | private final ProvidedOAuth2UserService providedOAuth2UserService;
27 |
28 | public ExtendedUserService(MongoTemplate mongoTemplate,
29 | UserRepository userRepository,
30 | ExtendedUserRepository extendedUserRepository,
31 | ProvidedOAuth2UserService providedOAuth2UserService) {
32 | super(mongoTemplate, userRepository);
33 | this.extendedUserRepository = extendedUserRepository;
34 | this.providedOAuth2UserService = providedOAuth2UserService;
35 | }
36 |
37 | public Optional findExtendedUser(String userId){
38 | return extendedUserRepository.findById(userId);
39 | }
40 |
41 |
42 | public ExtendedUser registerOAuth2User(final ExtendedUser user, OAuth2User oAuth2User, ProvidedOAuth2User.Provider provider) {
43 |
44 | // 1. ProvidedOAuth2User 를 가져오고 없으면 등록한다.
45 | ProvidedOAuth2User providedOAuth2User = provider.convert(oAuth2User);
46 | ProvidedOAuth2User saved = providedOAuth2UserService.find(providedOAuth2User.getOauth2UserId()).orElseGet(()->{
47 | if(user == null){
48 | // 2. user 정보가 없으면 user를 새로 등록하고 리턴한다.
49 | ExtendedUser siteUser = ExtendedUser.of(User.builder()
50 | .name(providedOAuth2User.getName())
51 | .picUrl(providedOAuth2User.getPicUrl())
52 | .email(providedOAuth2User.getEmail())
53 | .authorities(Set.of(Authority.USER))
54 | .enabled(true)
55 | .build());
56 | siteUser = (ExtendedUser) save(siteUser);
57 | providedOAuth2User.setUserId(siteUser.getUserId());
58 | }else{
59 | providedOAuth2User.setUserId(user.getUserId());
60 | }
61 | providedOAuth2User.setLastLoggedIn(LocalDateTime.now());
62 | return providedOAuth2UserService.save(providedOAuth2User);
63 | });
64 |
65 | if(user == null) return findExtendedUser(saved.getUserId()).get();
66 |
67 | // 3. user 정보가 있고 user와 같으면 lastLogin 값을 업데이트 하고 user 를 리턴한다.
68 | if(user.getUserId().equals(saved.getUserId())){
69 | providedOAuth2UserService.updateLastLoggedInTime(saved.getOauth2UserId());
70 | return user;
71 | }
72 |
73 | // 4. user 정보가 있고 user와 같지 않으면 ... ??? user 로 userId를 치환하고 lastLogin 을 업데이트 하고 user를 리턴한다.
74 | // * 필요하다면 변경 정보를 별도 DB로 관리한다.
75 | // * 해당 provider로 등록된 계정이 없다면 등록한다.
76 | if(providedOAuth2UserService.findProvidedOAuth2User(user.getUserId(), provider).isPresent()){
77 | throw new IllegalArgumentException("이미 등록된 사용자 정보가 있습니다.");
78 | }
79 |
80 | providedOAuth2UserService.changeUserId(saved.getOauth2UserId(), user.getUserId());
81 | return user;
82 | }
83 |
84 |
85 | public List getProvidedOAuth2UserList(String userId){
86 | return mongoTemplate.find(Query.query(Criteria.where("userId").is(userId)), ProvidedOAuth2User.class);
87 | }
88 |
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/main/java/com/sp/sec/user/oauth2/service/ProvidedOAuth2UserService.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2.service;
2 |
3 |
4 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
5 | import com.sp.sec.user.oauth2.repository.ProvidedOAuth2UserRepository;
6 | import org.springframework.data.mongodb.core.MongoTemplate;
7 | import org.springframework.data.mongodb.core.query.Criteria;
8 | import org.springframework.data.mongodb.core.query.Query;
9 | import org.springframework.data.mongodb.core.query.Update;
10 | import org.springframework.stereotype.Service;
11 |
12 | import java.time.LocalDateTime;
13 | import java.util.Optional;
14 |
15 | @Service
16 | public class ProvidedOAuth2UserService {
17 |
18 | private final MongoTemplate mongoTemplate;
19 | private final ProvidedOAuth2UserRepository repository;
20 |
21 | public ProvidedOAuth2UserService(MongoTemplate mongoTemplate, ProvidedOAuth2UserRepository repository) {
22 | this.mongoTemplate = mongoTemplate;
23 | this.repository = repository;
24 | }
25 |
26 | public ProvidedOAuth2User save(ProvidedOAuth2User providedOAuth2User){
27 | providedOAuth2User.setRegistered(LocalDateTime.now());
28 | return repository.save(providedOAuth2User);
29 | }
30 |
31 | public boolean updateLastLoggedInTime(String oauth2UserId){
32 | return mongoTemplate.updateFirst(Query.query(Criteria.where("oauth2UserId").is(oauth2UserId)),
33 | Update.update("lastLoggedIn", LocalDateTime.now()), ProvidedOAuth2User.class).wasAcknowledged();
34 | }
35 |
36 |
37 | public Optional find(String oauth2UserId) {
38 | return repository.findById(oauth2UserId);
39 | }
40 |
41 | public boolean changeUserId(String oauth2UserId, String userId) {
42 | Update update = Update.update("lastLoggedIn", LocalDateTime.now());
43 | update.set("userId", userId);
44 | return mongoTemplate.updateFirst(Query.query(Criteria.where("oauth2UserId").is(oauth2UserId)),
45 | update , ProvidedOAuth2User.class).wasAcknowledged();
46 | }
47 |
48 | public Optional findProvidedOAuth2User(String userId, ProvidedOAuth2User.Provider provider) {
49 | return Optional.of(mongoTemplate.findOne(
50 | Query.query(Criteria.where("userId").is(userId).and("provider").is(provider)),
51 | ProvidedOAuth2User.class));
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/ExtendedUserServiceTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 |
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
6 | import com.sp.sec.user.oauth2.repository.ExtendedUserRepository;
7 | import org.junit.jupiter.api.BeforeEach;
8 | import org.junit.jupiter.api.DisplayName;
9 | import org.junit.jupiter.api.Test;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
12 | import org.springframework.dao.DuplicateKeyException;
13 |
14 | import java.util.List;
15 |
16 | import static org.junit.jupiter.api.Assertions.assertEquals;
17 | import static org.junit.jupiter.api.Assertions.assertThrows;
18 |
19 | @DataMongoTest
20 | public class ExtendedUserServiceTest extends WithExtendedUserTest {
21 |
22 | @Autowired
23 | private ExtendedUserRepository extendedUserRepository;
24 |
25 | @BeforeEach
26 | void before(){
27 | prepareUserService();
28 | }
29 |
30 | @DisplayName("1. 확장 사용자를 생성한다.")
31 | @Test
32 | void test_1() {
33 | userTestHelper.createUser("user1");
34 | List userList = this.extendedUserRepository.findAll();
35 |
36 | assertEquals(1, userList.size());
37 | userTestHelper.assertUser(userList.get(0), "user1");
38 | }
39 |
40 |
41 | @DisplayName("2. 확장 사용자의 이름을 수정한다.")
42 | @Test
43 | void test_2() {
44 | ExtendedUser user1 = userTestHelper.createUser("user1");
45 | userService.updateUserName(user1.getUserId(), "user2");
46 |
47 | ExtendedUser savedUser = userService.findExtendedUser(user1.getUserId()).get();
48 | assertEquals("user2", savedUser.getName());
49 | }
50 |
51 | @DisplayName("3. authority를 부여한다.")
52 | @Test
53 | void test_3() {
54 | ExtendedUser user1 = userTestHelper.createUser("user1", Authority.ROLE_USER);
55 | userService.addAuthority(user1.getUserId(), Authority.ROLE_ADMIN);
56 | ExtendedUser savedUser = userService.findExtendedUser(user1.getUserId()).get();
57 | userTestHelper.assertUser(savedUser, "user1", Authority.ROLE_USER, Authority.ROLE_ADMIN);
58 | }
59 |
60 |
61 | @DisplayName("4. authority를 뺏는다.")
62 | @Test
63 | void test_4() {
64 | ExtendedUser user1 = userTestHelper.createUser("admin", Authority.ROLE_USER, Authority.ROLE_ADMIN);
65 | userService.removeAuthority(user1.getUserId(), Authority.ROLE_USER);
66 | ExtendedUser savedUser = userService.findExtendedUser(user1.getUserId()).get();
67 | userTestHelper.assertUser(savedUser, "admin", Authority.ROLE_ADMIN);
68 | }
69 |
70 | @DisplayName("5. email 로 검색이 된다.")
71 | @Test
72 | void test_5() {
73 | ExtendedUser user1 = userTestHelper.createUser("user1");
74 | ExtendedUser saved = (ExtendedUser) userService.loadUserByUsername("user1@test.com");
75 | userTestHelper.assertUser(saved, "user1");
76 | }
77 |
78 | @DisplayName("6. role이 중복되서 추가되지 않는다.")
79 | @Test
80 | void test_6() {
81 | ExtendedUser user1 = userTestHelper.createUser("user1", Authority.ROLE_USER);
82 | userService.addAuthority(user1.getUserId(), Authority.ROLE_USER);
83 | userService.addAuthority(user1.getUserId(), Authority.ROLE_USER);
84 | ExtendedUser savedUser = userService.findExtendedUser(user1.getUserId()).get();
85 | userTestHelper.assertUser(savedUser, "user1", Authority.ROLE_USER);
86 | }
87 |
88 | @DisplayName("7. email이 중복되어서 들어가는가?")
89 | @Test
90 | void test_() {
91 | userTestHelper.createUser("user1");
92 | assertThrows(DuplicateKeyException.class, ()->{
93 | userTestHelper.createUser("user1");
94 | });
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/ExtendedUserTestHelper.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 |
4 | import com.sp.sec.user.domain.Authority;
5 | import com.sp.sec.user.domain.User;
6 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
7 | import com.sp.sec.user.oauth2.service.ExtendedUserService;
8 | import com.sp.sec.user.service.UserService;
9 | import lombok.AllArgsConstructor;
10 | import org.springframework.dao.DuplicateKeyException;
11 | import org.springframework.security.crypto.password.PasswordEncoder;
12 |
13 | import java.util.Map;
14 | import java.util.stream.Collectors;
15 | import java.util.stream.Stream;
16 |
17 | import static org.junit.jupiter.api.Assertions.*;
18 |
19 | @AllArgsConstructor
20 | public class ExtendedUserTestHelper {
21 |
22 | private final ExtendedUserService userService;
23 |
24 | private final PasswordEncoder passwordEncoder;
25 |
26 | public ExtendedUser createUser(String name) throws DuplicateKeyException {
27 | ExtendedUser user = ExtendedUser.of(User.builder()
28 | .name(name)
29 | .email(name+"@test.com")
30 | .password(passwordEncoder.encode(name+"123"))
31 | .enabled(true)
32 | .build());
33 | user.setAttributes(Map.of("from", "oauth2_support"));
34 | return (ExtendedUser) userService.save(user);
35 | }
36 |
37 | public ExtendedUser createUser(String name, String... authorities){
38 | ExtendedUser user = createUser(name);
39 | Stream.of(authorities).forEach(auth->userService.addAuthority(user.getUserId(), auth));
40 | return user;
41 | }
42 |
43 | public static void assertUser(ExtendedUser user, String name){
44 | assertNotNull(user.getUserId());
45 | assertNotNull(user.getCreated());
46 | assertNotNull(user.getUpdated());
47 | assertTrue(user.isEnabled());
48 | assertEquals(name, user.getName());
49 | assertEquals(name+"@test.com", user.getEmail());
50 | // assertEquals(name+"123", user.getPassword());
51 | assertEquals("oauth2_support", user.getAttribute("from"));
52 | }
53 |
54 | public static void assertUser(ExtendedUser user, String name, String... authorities){
55 | assertUser(user, name);
56 | assertTrue(user.getAuthorities().containsAll(
57 | Stream.of(authorities).map(auth->new Authority(auth)).collect(Collectors.toList())
58 | ));
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/FacebookUserLoginTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 |
4 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
5 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
6 | import org.junit.jupiter.api.BeforeEach;
7 | import org.junit.jupiter.api.DisplayName;
8 | import org.junit.jupiter.api.Test;
9 | import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
10 |
11 | import java.util.List;
12 |
13 | import static org.junit.jupiter.api.Assertions.*;
14 |
15 | @DataMongoTest
16 | public class FacebookUserLoginTest extends WithExtendedUserTest{
17 |
18 | ExtendedUser user;
19 |
20 | @BeforeEach
21 | void before(){
22 | prepareUserService();
23 | this.user = userService.registerOAuth2User(null, OAuth2UserSample.facebookUser,
24 | ProvidedOAuth2User.Provider.facebook);
25 | }
26 |
27 | @DisplayName("1. 사이트에 가입하지 않은 사용자가 페이스북 사용자로 로그인 하면 사용자가로 등록 된다.")
28 | @Test
29 | void test_1() {
30 | assertNotNull(user.getUserId());
31 | assertEquals("Jongwon Choi", user.getName());
32 | assertNull( user.getPicUrl());
33 | assertEquals("jongwons.choi@gmail.com", user.getEmail());
34 |
35 | List list = providedOAuth2UserRepository.findAll();
36 | assertEquals(1, list.size());
37 | ProvidedOAuth2User facebookUser = list.get(0);
38 | assertEquals("facebook_4000026893357972", facebookUser.getOauth2UserId());
39 | assertEquals("Jongwon Choi", facebookUser.getName());
40 | assertNull(facebookUser.getPicUrl());
41 | assertEquals("jongwons.choi@gmail.com", facebookUser.getEmail());
42 |
43 | assertNotNull(facebookUser.getRegistered());
44 | assertNotNull(facebookUser.getLastLoggedIn());
45 | assertEquals(user.getUserId(), facebookUser.getUserId());
46 | assertEquals(ProvidedOAuth2User.Provider.facebook, facebookUser.getProvider());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/GoogleUserLoginTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 |
4 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
5 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
6 | import org.junit.jupiter.api.BeforeEach;
7 | import org.junit.jupiter.api.DisplayName;
8 | import org.junit.jupiter.api.Test;
9 | import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
10 |
11 | import java.util.List;
12 |
13 | import static org.junit.jupiter.api.Assertions.assertEquals;
14 | import static org.junit.jupiter.api.Assertions.assertNotNull;
15 |
16 | @DataMongoTest
17 | public class GoogleUserLoginTest extends WithExtendedUserTest{
18 |
19 | ExtendedUser user;
20 |
21 | @BeforeEach
22 | void before(){
23 | prepareUserService();
24 | this.user = userService.registerOAuth2User(null, OAuth2UserSample.googleUser,
25 | ProvidedOAuth2User.Provider.google);
26 | }
27 |
28 | @DisplayName("1. 사이트에 가입하지 않은 사용자가 구글 사용자로 로그인 하면 사용자가로 등록 된다.")
29 | @Test
30 | void test_1() {
31 | assertNotNull(user.getUserId());
32 | assertEquals("옥탑방개발자", user.getName());
33 | assertEquals("https://lh3.googleusercontent.com/a-/AOh14GgFLv4rMtdDUyBFDgsJggHdCK5IuKSLuOq9OwwLDyc=s96-c", user.getPicUrl());
34 | assertEquals("jongwons.choi@gmail.com", user.getEmail());
35 |
36 | List list = providedOAuth2UserRepository.findAll();
37 | assertEquals(1, list.size());
38 | ProvidedOAuth2User googleInfo = list.get(0);
39 | assertEquals("google_113976141374150070219", googleInfo.getOauth2UserId());
40 | assertEquals("옥탑방개발자", googleInfo.getName());
41 | assertEquals("https://lh3.googleusercontent.com/a-/AOh14GgFLv4rMtdDUyBFDgsJggHdCK5IuKSLuOq9OwwLDyc=s96-c", googleInfo.getPicUrl());
42 | assertEquals("jongwons.choi@gmail.com", googleInfo.getEmail());
43 |
44 | assertNotNull(googleInfo.getRegistered());
45 | assertNotNull(googleInfo.getLastLoggedIn());
46 | assertEquals(user.getUserId(), googleInfo.getUserId());
47 | assertEquals(ProvidedOAuth2User.Provider.google, googleInfo.getProvider());
48 | }
49 |
50 | @DisplayName("2. ProvidedOAuth2User 와 ExtendedUser 가 잘 링크된다.")
51 | @Test
52 | void test_2() {
53 | List providedOAuth2UserList = userService.getProvidedOAuth2UserList(user.getUserId());
54 | assertEquals(1, providedOAuth2UserList.size());
55 | assertEquals(ProvidedOAuth2User.Provider.google, providedOAuth2UserList.get(0).getProvider());
56 | }
57 |
58 | @DisplayName("3. 다시 로그인을 하더라도 새로운 사용자가 등록되지 않는다.")
59 | @Test
60 | void test_3() {
61 | ExtendedUser loginAgain = userService.registerOAuth2User(null, OAuth2UserSample.googleUser,
62 | ProvidedOAuth2User.Provider.google);
63 | assertEquals(user.getUserId(), loginAgain.getUserId());
64 | }
65 |
66 |
67 | @DisplayName("4. 구글로 로그인한 다음, 해당 유저로 네이버, 카카오, 페이스북을 차례로 로그인해 계정을 연결할 수 있다.")
68 | @Test
69 | void test_4() {
70 | userService.registerOAuth2User(user, OAuth2UserSample.facebookUser,
71 | ProvidedOAuth2User.Provider.facebook);
72 | assertEquals(1, userRepository.findAll().size());
73 | assertEquals(2, userService.getProvidedOAuth2UserList(user.getUserId()).size());
74 |
75 | userService.registerOAuth2User(user, OAuth2UserSample.naverUser,
76 | ProvidedOAuth2User.Provider.naver);
77 | assertEquals(1, userRepository.findAll().size());
78 | assertEquals(3, userService.getProvidedOAuth2UserList(user.getUserId()).size());
79 |
80 | userService.registerOAuth2User(user, OAuth2UserSample.kakaoUser,
81 | ProvidedOAuth2User.Provider.kakao);
82 | assertEquals(1, userRepository.findAll().size());
83 | assertEquals(4, userService.getProvidedOAuth2UserList(user.getUserId()).size());
84 |
85 | }
86 |
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/KakaoUserLoginTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
4 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.DisplayName;
7 | import org.junit.jupiter.api.Test;
8 | import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
9 |
10 | import java.util.List;
11 |
12 | import static org.junit.jupiter.api.Assertions.assertEquals;
13 | import static org.junit.jupiter.api.Assertions.assertNotNull;
14 |
15 | @DataMongoTest
16 | public class KakaoUserLoginTest extends WithExtendedUserTest{
17 |
18 | ExtendedUser user;
19 |
20 | @BeforeEach
21 | void before(){
22 | prepareUserService();
23 | this.user = userService.registerOAuth2User(null, OAuth2UserSample.kakaoUser,
24 | ProvidedOAuth2User.Provider.kakao);
25 | }
26 |
27 | @DisplayName("1. 사이트에 가입하지 않은 사용자가 구글 사용자로 로그인 하면 사용자가로 등록 된다.")
28 | @Test
29 | void test_1() {
30 | assertNotNull(user.getUserId());
31 | assertEquals("jongwon", user.getName());
32 | assertEquals("http://k.kakaocdn.net/dn/XQHgC/btqyj3C5jCQ/KjiijMK462WPrRrnkoOtY0/img_110x110.jpg", user.getPicUrl());
33 | assertEquals("jongwons.choi@kakao.com", user.getEmail());
34 |
35 | List list = providedOAuth2UserRepository.findAll();
36 | assertEquals(1, list.size());
37 | ProvidedOAuth2User kakaoUser = list.get(0);
38 | assertEquals("kakao_1534230750", kakaoUser.getOauth2UserId());
39 | assertEquals("jongwon", kakaoUser.getName());
40 | assertEquals("http://k.kakaocdn.net/dn/XQHgC/btqyj3C5jCQ/KjiijMK462WPrRrnkoOtY0/img_110x110.jpg", kakaoUser.getPicUrl());
41 | assertEquals("jongwons.choi@kakao.com", kakaoUser.getEmail());
42 |
43 | assertNotNull(kakaoUser.getRegistered());
44 | assertNotNull(kakaoUser.getLastLoggedIn());
45 | assertEquals(user.getUserId(), kakaoUser.getUserId());
46 | assertEquals(ProvidedOAuth2User.Provider.kakao, kakaoUser.getProvider());
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/NaverUserLoginTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 | import com.sp.sec.user.oauth2.domain.ExtendedUser;
4 | import com.sp.sec.user.oauth2.domain.ProvidedOAuth2User;
5 | import org.junit.jupiter.api.BeforeEach;
6 | import org.junit.jupiter.api.DisplayName;
7 | import org.junit.jupiter.api.Test;
8 | import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest;
9 |
10 | import java.util.List;
11 |
12 | import static org.junit.jupiter.api.Assertions.assertEquals;
13 | import static org.junit.jupiter.api.Assertions.assertNotNull;
14 |
15 |
16 | @DataMongoTest
17 | public class NaverUserLoginTest extends WithExtendedUserTest {
18 |
19 | ExtendedUser user;
20 |
21 | @BeforeEach
22 | void before(){
23 | prepareUserService();
24 | this.user = userService.registerOAuth2User(null, OAuth2UserSample.naverUser,
25 | ProvidedOAuth2User.Provider.naver);
26 | }
27 |
28 | @DisplayName("1. 사이트에 가입하지 않은 사용자가 구글 사용자로 로그인 하면 사용자가로 등록 된다.")
29 | @Test
30 | void test_1() {
31 | assertNotNull(user.getUserId());
32 | assertEquals("최종원", user.getName());
33 | assertEquals("https://phinf.pstatic.net/contact/20180308_276/1520490317846up6kA_PNG/avatar_profile.png", user.getPicUrl());
34 | assertEquals("jongwons.choi@gmail.com", user.getEmail());
35 |
36 | List list = providedOAuth2UserRepository.findAll();
37 | assertEquals(1, list.size());
38 | ProvidedOAuth2User naverUser = list.get(0);
39 | assertEquals("naver_18997705", naverUser.getOauth2UserId());
40 | assertEquals("최종원", naverUser.getName());
41 | assertEquals("https://phinf.pstatic.net/contact/20180308_276/1520490317846up6kA_PNG/avatar_profile.png", naverUser.getPicUrl());
42 | assertEquals("jongwons.choi@gmail.com", naverUser.getEmail());
43 |
44 | assertNotNull(naverUser.getRegistered());
45 | assertNotNull(naverUser.getLastLoggedIn());
46 | assertEquals(user.getUserId(), naverUser.getUserId());
47 | assertEquals(ProvidedOAuth2User.Provider.naver, naverUser.getProvider());
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/OAuth2UserSample.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 | import com.sp.sec.user.domain.Authority;
4 | import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
5 | import org.springframework.security.oauth2.core.user.OAuth2User;
6 |
7 | import java.util.Map;
8 | import java.util.Set;
9 |
10 | public class OAuth2UserSample {
11 |
12 | public static OAuth2User googleUser = new DefaultOAuth2User(Set.of(Authority.USER),
13 | Map.of(
14 | "name", "옥탑방개발자",
15 | "sub", "113976141374150070219",
16 | "picture", "https://lh3.googleusercontent.com/a-/AOh14GgFLv4rMtdDUyBFDgsJggHdCK5IuKSLuOq9OwwLDyc=s96-c",
17 | "email", "jongwons.choi@gmail.com"
18 | ), "sub");
19 |
20 |
21 | public static OAuth2User facebookUser = new DefaultOAuth2User(Set.of(Authority.USER),
22 | Map.of("id", "4000026893357972",
23 | "name", "Jongwon Choi",
24 | "email", "jongwons.choi@gmail.com"), "id");
25 |
26 | public static OAuth2User naverUser = new DefaultOAuth2User(Set.of(Authority.USER),
27 | Map.of(
28 | "response", Map.of(
29 | "id", "18997705",
30 | "nickname", "슈타인",
31 | "profile_image", "https://phinf.pstatic.net/contact/20180308_276/1520490317846up6kA_PNG/avatar_profile.png",
32 | "email", "jongwons.choi@gmail.com",
33 | "name", "최종원"
34 | )
35 | ), "response");
36 |
37 | public static OAuth2User kakaoUser = new DefaultOAuth2User(Set.of(Authority.USER),
38 | Map.of(
39 | "id", 1534230750,
40 | "kakao_account", Map.of(
41 | "profile", Map.of(
42 | "nickname", "jongwon",
43 | "thumbnail_image_url", "http://k.kakaocdn.net/dn/XQHgC/btqyj3C5jCQ/KjiijMK462WPrRrnkoOtY0/img_110x110.jpg",
44 | "profile_image_url", "http://k.kakaocdn.net/dn/XQHgC/btqyj3C5jCQ/KjiijMK462WPrRrnkoOtY0/img_640x640.jpg"
45 |
46 | ),
47 | "email", "jongwons.choi@kakao.com"
48 | )
49 | ), "id");
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/UserOAuth2SupportApp.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
7 |
8 | @SpringBootApplication
9 | public class UserOAuth2SupportApp {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(UserOAuth2SupportApp.class, args);
13 | }
14 |
15 | @Configuration
16 | @EnableMongoRepositories(basePackages = {
17 | "com.sp.sec.user.repository",
18 | "com.sp.sec.user.oauth2.repository"
19 | })
20 | class MongoConfig {}
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/user-oauth2-support/src/test/java/com/sp/sec/user/oauth2/WithExtendedUserTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user.oauth2;
2 |
3 | import com.sp.sec.user.oauth2.repository.ExtendedUserRepository;
4 | import com.sp.sec.user.oauth2.repository.ProvidedOAuth2UserRepository;
5 | import com.sp.sec.user.oauth2.service.ExtendedUserService;
6 | import com.sp.sec.user.oauth2.service.ProvidedOAuth2UserService;
7 | import com.sp.sec.user.repository.UserRepository;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.data.mongodb.core.MongoTemplate;
10 | import org.springframework.security.crypto.password.NoOpPasswordEncoder;
11 |
12 | public class WithExtendedUserTest {
13 |
14 | @Autowired
15 | protected MongoTemplate mongoTemplate;
16 |
17 | @Autowired
18 | protected UserRepository userRepository;
19 |
20 | @Autowired
21 | protected ExtendedUserRepository extendedUserRepository;
22 |
23 | @Autowired
24 | protected ProvidedOAuth2UserRepository providedOAuth2UserRepository;
25 |
26 | protected ProvidedOAuth2UserService providedOAuth2UserService;
27 |
28 | protected ExtendedUserService userService;
29 |
30 | protected ExtendedUserTestHelper userTestHelper;
31 |
32 | protected void prepareUserService(){
33 | this.userRepository.deleteAll();
34 | this.providedOAuth2UserRepository.deleteAll();
35 | this.providedOAuth2UserService = new ProvidedOAuth2UserService(mongoTemplate, providedOAuth2UserRepository);
36 | this.userService = new ExtendedUserService(mongoTemplate, userRepository,
37 | extendedUserRepository,
38 | providedOAuth2UserService);
39 | this.userTestHelper = new ExtendedUserTestHelper(userService, NoOpPasswordEncoder.getInstance());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/web/auth-server-1/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/web/auth-server-1/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/web/auth-server-1/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/web/auth-server-1/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | auth-server-1
13 | 1.0.0
14 | auth-server-1
15 | 테스트 인증서버 1
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 | org.junit.vintage
47 | junit-vintage-engine
48 |
49 |
50 |
51 |
52 | de.flapdoodle.embed
53 | de.flapdoodle.embed.mongo
54 | test
55 |
56 |
57 | org.springframework.security
58 | spring-security-test
59 | test
60 |
61 |
62 |
63 | com.sp.sec
64 | user-authority
65 | 1.0.0
66 |
67 |
68 |
69 | com.sp.sec
70 | user-authority
71 | 1.0.0
72 | test-jar
73 | test
74 |
75 |
76 |
77 | com.sp.sec
78 | sp-jwt-security
79 | 1.0.0
80 |
81 |
82 |
83 | com.sp.sec
84 | sp-jwt-security
85 | 1.0.0
86 | test-jar
87 | test
88 |
89 |
90 |
91 |
92 | com.sp.sec
93 | sp-web-util
94 | 1.0.0
95 |
96 |
97 |
98 |
99 |
100 | org.springframework.boot
101 | spring-boot-maven-plugin
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/web/auth-server-1/src/main/java/com/sp/sec/web/AuthServer1Application.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication(scanBasePackages = {
7 | "com.sp.sec.config",
8 | "com.sp.sec.web"
9 | })
10 | public class AuthServer1Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(AuthServer1Application.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/web/auth-server-1/src/main/java/com/sp/sec/web/config/DBInit.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.sp.sec.user.domain.Authority;
4 | import com.sp.sec.user.domain.User;
5 | import com.sp.sec.user.service.UserService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.CommandLineRunner;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.Set;
12 |
13 | @Component
14 | public class DBInit implements CommandLineRunner {
15 |
16 | @Autowired
17 | private UserService userService;
18 |
19 | @Autowired
20 | private PasswordEncoder passwordEncoder;
21 |
22 | @Override
23 | public void run(String... args) throws Exception {
24 | userService.clearUsers();
25 | User user1 = User.builder().name("user1")
26 | .email("user1@test.com")
27 | .password(passwordEncoder.encode("1234"))
28 | .enabled(true)
29 | .authorities(Set.of(Authority.USER))
30 | .build();
31 | User admin = User.builder().name("admin")
32 | .email("admin@test.com")
33 | .password(passwordEncoder.encode("admin"))
34 | .enabled(true)
35 | .authorities(Set.of(Authority.ADMIN))
36 | .build();
37 |
38 | userService.save(user1);
39 | userService.save(admin);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/web/auth-server-1/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
12 | import org.springframework.security.config.http.SessionCreationPolicy;
13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14 |
15 | @EnableWebSecurity
16 | @EnableGlobalMethodSecurity(prePostEnabled = true)
17 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
18 |
19 | private final UserService userService;
20 | private final ObjectMapper objectMapper;
21 | private final JWTUtil jwtUtil;
22 |
23 | public SecurityConfig(UserService userService, ObjectMapper objectMapper, JWTUtil jwtUtil) {
24 | this.userService = userService;
25 | this.objectMapper = objectMapper;
26 | this.jwtUtil = jwtUtil;
27 | }
28 |
29 |
30 | @Override
31 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
32 | auth.userDetailsService(userService)
33 | .passwordEncoder(passwordEncoder());
34 | }
35 |
36 | @Bean
37 | BCryptPasswordEncoder passwordEncoder(){
38 | return new BCryptPasswordEncoder();
39 | }
40 |
41 | @Override
42 | protected AuthenticationManager authenticationManager() throws Exception {
43 | return super.authenticationManager();
44 | }
45 |
46 | @Override
47 | protected void configure(HttpSecurity http) throws Exception {
48 | final RefreshableJWTLoginFilter loginFilter = new RefreshableJWTLoginFilter(
49 | authenticationManager(), userService, jwtUtil, objectMapper);
50 | http
51 | .csrf().disable()
52 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
53 | .and()
54 | .addFilter(loginFilter)
55 | ;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/web/auth-server-1/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9001
3 |
4 |
5 | sp:
6 | jwt:
7 | secret: auth-server-1
8 | token-life-time: 100
9 | token-refresh-time: 10000
10 |
11 |
12 | spring:
13 | data:
14 | mongodb:
15 | database: auth-server-1
16 | host: localhost
17 | port: 27018
18 |
--------------------------------------------------------------------------------
/web/auth-server-1/src/test/java/com/sp/sec/web/AuthServer1ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class AuthServer1ApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/google-client-4/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/web/google-client-4/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/web/google-client-4/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/web/google-client-4/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | google-client-4
13 | 1.0.0
14 | google-client-4
15 | 구글 클라이언트 4
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-oauth2-client
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-security
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-web
37 |
38 |
39 |
40 | org.projectlombok
41 | lombok
42 | true
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-test
47 | test
48 |
49 |
50 | de.flapdoodle.embed
51 | de.flapdoodle.embed.mongo
52 | test
53 |
54 |
55 | org.springframework.security
56 | spring-security-test
57 | test
58 |
59 |
60 |
61 | com.sp.sec
62 | user-authority
63 | 1.0.0
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | org.springframework.boot
72 | spring-boot-maven-plugin
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/java/com/sp/sec/web/GoogleClient4Application.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication(scanBasePackages = {
7 | "com.sp.sec.config",
8 | "com.sp.sec.web"
9 | })
10 | public class GoogleClient4Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(GoogleClient4Application.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
7 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
8 | import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
9 | import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;
10 |
11 | @EnableWebSecurity
12 | @EnableGlobalMethodSecurity(prePostEnabled = true)
13 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
14 |
15 | // CommonOAuth2Provider provider;
16 |
17 | @Autowired
18 | private SpGoogleUser spGoogleUser;
19 |
20 | @Autowired
21 | private SpGoogleUserToMyUserFilter googleUserToMyUserFilter;
22 |
23 | @Override
24 | protected void configure(HttpSecurity http) throws Exception {
25 | http
26 | .oauth2Login(oauth->{
27 | oauth.userInfoEndpoint(userinfo->{
28 | userinfo.oidcUserService(spGoogleUser);
29 | });
30 | })
31 | .addFilterAfter(googleUserToMyUserFilter, OAuth2LoginAuthenticationFilter.class)
32 | ;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/java/com/sp/sec/web/config/SpGoogleUser.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
4 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
5 | import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
6 | import org.springframework.security.oauth2.core.oidc.user.OidcUser;
7 | import org.springframework.stereotype.Service;
8 |
9 | @Service
10 | public class SpGoogleUser extends OidcUserService {
11 |
12 | @Override
13 | public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
14 | OidcUser googleUser = super.loadUser(userRequest);
15 | return googleUser;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/java/com/sp/sec/web/config/SpGoogleUserToMyUserFilter.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.sp.sec.user.domain.Authority;
4 | import com.sp.sec.user.domain.User;
5 | import com.sp.sec.user.service.UserService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
8 | import org.springframework.security.core.Authentication;
9 | import org.springframework.security.core.context.SecurityContextHolder;
10 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
11 | import org.springframework.security.oauth2.core.oidc.user.OidcUser;
12 | import org.springframework.stereotype.Component;
13 |
14 | import javax.servlet.*;
15 | import java.io.IOException;
16 | import java.util.Set;
17 |
18 | @Component
19 | public class SpGoogleUserToMyUserFilter implements Filter {
20 |
21 | @Autowired
22 | private UserService userService;
23 |
24 | @Override
25 | public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
26 | Authentication auth = SecurityContextHolder.getContext().getAuthentication();
27 | if(auth instanceof OAuth2AuthenticationToken){
28 | OidcUser googleUser = (OidcUser)((OAuth2AuthenticationToken) auth).getPrincipal();
29 | User user = userService.findUser("google_"+googleUser.getSubject())
30 | .orElseGet(()->
31 | userService.save(User.builder()
32 | .userId("google_"+googleUser.getSubject())
33 | .email(googleUser.getEmail())
34 | .authorities(Set.of(Authority.USER, new Authority("FROM_GOOGLE")))
35 | .enabled(true)
36 | .build())
37 | );
38 | SecurityContextHolder.getContext().setAuthentication(
39 | new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities())
40 | );
41 | }
42 | filterChain.doFilter(servletRequest, servletResponse);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/java/com/sp/sec/web/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.controller;
2 |
3 |
4 | import com.sp.sec.user.domain.User;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.security.access.prepost.PreAuthorize;
7 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
8 | import org.springframework.security.oauth2.core.user.OAuth2User;
9 | import org.springframework.web.bind.annotation.GetMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | @RestController
13 | public class HomeController {
14 |
15 | @Autowired
16 | private SecuredService securedService;
17 |
18 | @PreAuthorize("isAuthenticated()")
19 | @GetMapping("/")
20 | public String home(@AuthenticationPrincipal User user){
21 | return securedService.secured();
22 | }
23 |
24 |
25 | @PreAuthorize("isAuthenticated()")
26 | @GetMapping("/user")
27 | public OAuth2User user(@AuthenticationPrincipal OAuth2User user){
28 |
29 | return user;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/java/com/sp/sec/web/controller/SecuredService.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.controller;
2 |
3 |
4 | import org.springframework.security.access.prepost.PreAuthorize;
5 | import org.springframework.stereotype.Service;
6 |
7 | @Service
8 | public class SecuredService {
9 |
10 | @PreAuthorize("hasAnyAuthority('FROM_GOOGLE')")
11 | public String secured(){
12 | return "secured info : 옥수수";
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/web/google-client-4/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 9006
3 | spring:
4 | security:
5 | oauth2:
6 | client:
7 | registration:
8 | google:
9 | client-id: 377825014181-2tpha7niq0hjar86e4j8h6dl0h8j1s3i.apps.googleusercontent.com
10 | client-secret: yyHXhXWBRrx3jyDWrhiSwzu8
11 | data:
12 | mongodb:
13 | database: google-client-4
14 | host: localhost
15 | port: 27018
16 |
--------------------------------------------------------------------------------
/web/google-client-4/src/test/java/com/sp/sec/web/GoogleClient4ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class GoogleClient4ApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/web/jwt-refresh-token-test/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | jwt-refresh-token-test
13 | 1.0.0
14 | jwt-refresh-token-test
15 | jwt-refresh-token-test
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 | org.junit.vintage
47 | junit-vintage-engine
48 |
49 |
50 |
51 |
52 | de.flapdoodle.embed
53 | de.flapdoodle.embed.mongo
54 | test
55 |
56 |
57 | org.springframework.security
58 | spring-security-test
59 | test
60 |
61 |
62 |
63 | com.sp.sec
64 | user-authority
65 | 1.0.0
66 |
67 |
68 |
69 | com.sp.sec
70 | user-authority
71 | 1.0.0
72 | test-jar
73 | test
74 |
75 |
76 |
77 | com.sp.sec
78 | sp-board
79 | 1.0.0
80 |
81 |
82 |
83 | com.sp.sec
84 | sp-board
85 | 1.0.0
86 | test-jar
87 | test
88 |
89 |
90 |
91 |
92 | com.sp.sec
93 | sp-jwt-security
94 | 1.0.0
95 |
96 |
97 |
98 | com.sp.sec
99 | sp-jwt-security
100 | 1.0.0
101 | test-jar
102 | test
103 |
104 |
105 |
106 |
107 | com.sp.sec
108 | sp-web-util
109 | 1.0.0
110 |
111 |
112 |
113 |
114 |
115 |
116 | org.springframework.boot
117 | spring-boot-maven-plugin
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/src/main/java/com/sp/sec/web/JwtRefreshTokenTestApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication(scanBasePackages = {
7 | "com.sp.sec.config",
8 | "com.sp.sec.web"
9 | })
10 | public class JwtRefreshTokenTestApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(JwtRefreshTokenTestApplication.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
12 | import org.springframework.security.config.http.SessionCreationPolicy;
13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14 |
15 | @EnableWebSecurity
16 | @EnableGlobalMethodSecurity(prePostEnabled = true)
17 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
18 |
19 | private final UserService userService;
20 | private final ObjectMapper objectMapper;
21 | private final JWTUtil jwtUtil;
22 |
23 | public SecurityConfig(UserService userService, ObjectMapper objectMapper, JWTUtil jwtUtil) {
24 | this.userService = userService;
25 | this.objectMapper = objectMapper;
26 | this.jwtUtil = jwtUtil;
27 | }
28 |
29 |
30 | @Override
31 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
32 | auth.userDetailsService(userService)
33 | .passwordEncoder(passwordEncoder());
34 | }
35 |
36 | @Bean
37 | BCryptPasswordEncoder passwordEncoder(){
38 | return new BCryptPasswordEncoder();
39 | }
40 |
41 | @Override
42 | protected AuthenticationManager authenticationManager() throws Exception {
43 | return super.authenticationManager();
44 | }
45 |
46 | @Override
47 | protected void configure(HttpSecurity http) throws Exception {
48 | final RefreshableJWTLoginFilter loginFilter = new RefreshableJWTLoginFilter(
49 | authenticationManager(), userService, jwtUtil, objectMapper);
50 | final JWTCheckFilter checkFilter = new JWTCheckFilter(authenticationManager(), userService, jwtUtil);
51 |
52 | http
53 | .csrf().disable()
54 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
55 | .and()
56 | .addFilter(loginFilter)
57 | .addFilter(checkFilter)
58 | ;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web/jwt-refresh-token-test/src/test/java/com/sp/sec/web/RefreshTokenTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.core.type.TypeReference;
5 | import com.sp.sec.board.domain.SpBoard;
6 | import com.sp.sec.board.domain.SpBoardSummary;
7 | import com.sp.sec.board.service.SpBoardService;
8 | import com.sp.sec.board.service.SpBoardTestHelper;
9 | import com.sp.sec.web.config.JWTUtil;
10 | import com.sp.sec.web.util.RestResponsePage;
11 | import org.junit.jupiter.api.BeforeEach;
12 | import org.junit.jupiter.api.DisplayName;
13 | import org.junit.jupiter.api.Test;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.boot.test.context.SpringBootTest;
16 | import org.springframework.http.HttpMethod;
17 | import org.springframework.http.ResponseEntity;
18 | import org.springframework.web.client.HttpClientErrorException;
19 |
20 | import java.net.URISyntaxException;
21 |
22 | import static org.junit.jupiter.api.Assertions.assertEquals;
23 | import static org.junit.jupiter.api.Assertions.assertThrows;
24 |
25 | /**
26 | * user1 이 두개의 게시물을 올린다.
27 | *
28 | */
29 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
30 | public class RefreshTokenTest extends SpJwtRefreshableTwoUserIntegrationTest{
31 |
32 | @Autowired
33 | private SpBoardService boardService;
34 |
35 | @Autowired
36 | private JWTUtil jwtUtil;
37 |
38 | @BeforeEach
39 | void before() throws URISyntaxException {
40 | prepareTwoUsers();
41 | boardService.clearBoards();
42 | Tokens tokens = 유저로그인("user1");
43 | 게시글을_작성한다(tokens, SpBoardTestHelper.makeBoard(USER1, "title1", "content1"));
44 | 게시글을_작성한다(tokens, SpBoardTestHelper.makeBoard(USER1, "title2", "content2"));
45 | }
46 |
47 | private void 게시글을_작성한다(Tokens tokens, SpBoard board1) throws URISyntaxException {
48 | ResponseEntity response = restTemplate.exchange(uri("/board/save"),
49 | HttpMethod.POST, getPostAuthHeaderEntity(tokens.getAccessToken(), board1),
50 | SpBoard.class);
51 | assertEquals(200, response.getStatusCodeValue());
52 | }
53 |
54 | @DisplayName("1. user2 가 게시물을 조회하고, 일정 시간이 지나 토큰이 만료된 후 다시 조회한다.")
55 | @Test
56 | void test_1() throws URISyntaxException, JsonProcessingException, InterruptedException {
57 | 토큰타임을_1초로_맞춘다();
58 |
59 | final Tokens 첫번째토큰 = 유저로그인("user2");
60 | 게시판의_게시글이_2개인걸_확인한다(첫번째토큰);
61 |
62 | Thread.sleep(2000);
63 |
64 | assertThrows(HttpClientErrorException.class, ()->{
65 | 게시판의_게시글이_2개인걸_확인한다(첫번째토큰);
66 | });
67 |
68 | Tokens 다시얻은토큰 = getRefreshToken(첫번째토큰.getRefreshToken());
69 | 게시판의_게시글이_2개인걸_확인한다(다시얻은토큰);
70 | }
71 |
72 | private void 게시판의_게시글이_2개인걸_확인한다(Tokens tokens) throws URISyntaxException, JsonProcessingException {
73 | ResponseEntity response = restTemplate.exchange(uri("/board/list"),
74 | HttpMethod.GET, getAuthHeaderEntity(tokens.getAccessToken()), String.class);
75 | assertEquals(200, response.getStatusCodeValue());
76 | RestResponsePage page = objectMapper.readValue(response.getBody(),
77 | new TypeReference>() {
78 | });
79 | assertEquals(2, page.getTotalElements());
80 | }
81 |
82 | private Tokens 유저로그인(String name) throws URISyntaxException {
83 | return getToken(name+"@test.com", name+"123");
84 | }
85 |
86 | private void 토큰타임을_1초로_맞춘다() {
87 | jwtUtil.getProperties().setTokenLifeTime(1);
88 | }
89 |
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/web/jwt-user-web/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | jwt-user-web
13 | 1.0.0
14 | jwt-user-web
15 | JWT 유저 웹
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 | org.junit.vintage
47 | junit-vintage-engine
48 |
49 |
50 |
51 |
52 | de.flapdoodle.embed
53 | de.flapdoodle.embed.mongo
54 | test
55 |
56 |
57 | org.springframework.security
58 | spring-security-test
59 | test
60 |
61 |
62 |
63 | com.auth0
64 | java-jwt
65 | 3.11.0
66 |
67 |
68 |
69 | com.sp.sec
70 | user-authority
71 | 1.0.0
72 |
73 |
74 |
75 | com.sp.sec
76 | user-authority
77 | 1.0.0
78 | test-jar
79 | test
80 |
81 |
82 | com.sp.sec
83 | sp-web-util
84 | 1.0.0
85 | compile
86 |
87 |
88 | com.sp.sec
89 | sp-jwt-security
90 | 1.0.0
91 | compile
92 |
93 |
94 | com.sp.sec
95 | sp-jwt-security
96 | 1.0.0
97 | test-jar
98 | test
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | org.springframework.boot
107 | spring-boot-maven-plugin
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/web/jwt-user-web/src/main/java/com/sp/sec/web/JwtUserWebApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.sp.sec.web.config.SpJwtProperties;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
7 |
8 | @SpringBootApplication(scanBasePackages = {
9 | "com.sp.sec.config",
10 | "com.sp.sec.web"
11 | })
12 | public class JwtUserWebApplication {
13 |
14 | public static void main(String[] args) {
15 | SpringApplication.run(JwtUserWebApplication.class, args);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/web/jwt-user-web/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
13 | import org.springframework.security.crypto.password.PasswordEncoder;
14 |
15 |
16 | @EnableWebSecurity
17 | @EnableGlobalMethodSecurity(prePostEnabled = true)
18 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
19 |
20 | private final UserService userService;
21 | private final ObjectMapper objectMapper;
22 | private final JWTUtil jwtUtil ;
23 |
24 | public SecurityConfig(UserService userService, ObjectMapper objectMapper, JWTUtil jwtUtil) {
25 | this.userService = userService;
26 | this.objectMapper = objectMapper;
27 | this.jwtUtil = jwtUtil;
28 | }
29 |
30 |
31 | @Bean
32 | PasswordEncoder passwordEncoder(){
33 | return new BCryptPasswordEncoder();
34 | }
35 |
36 | @Override
37 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
38 | auth.userDetailsService(userService)
39 | .passwordEncoder(passwordEncoder());
40 | }
41 |
42 | @Override
43 | protected AuthenticationManager authenticationManager() throws Exception {
44 | return super.authenticationManager();
45 | }
46 |
47 | @Override
48 | protected void configure(HttpSecurity http) throws Exception {
49 | JWTLoginFilter jwtLoginFilter = new JWTLoginFilter(authenticationManager(), jwtUtil, objectMapper);
50 | JWTCheckFilter checkFilter = new JWTCheckFilter(authenticationManager(),
51 | userService, jwtUtil);
52 | http
53 | .csrf().disable()
54 | .addFilter(jwtLoginFilter)
55 | .addFilter(checkFilter)
56 | ;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/web/jwt-user-web/src/main/resources/application-test.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8098
3 |
4 | spring:
5 | data:
6 | mongodb:
7 | database: jwt-user-web
8 | host: localhost
9 | port: 0
10 |
11 | sp:
12 | jwt:
13 | secret: test-secret
14 | token-life-time: 3
--------------------------------------------------------------------------------
/web/jwt-user-web/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8098
3 |
4 | spring:
5 | data:
6 | mongodb:
7 | database: jwt-user-web
8 | host: localhost
9 | port: 27018
10 |
11 | sp:
12 | jwt:
13 | secret: product-secret
14 | token-life-time: 600
--------------------------------------------------------------------------------
/web/jwt-user-web/src/test/java/com/sp/sec/web/JWTLoginFilterTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 |
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.sp.sec.user.UserTestHelper;
6 | import com.sp.sec.user.domain.Authority;
7 | import com.sp.sec.user.domain.User;
8 | import com.sp.sec.user.service.UserService;
9 | import com.sp.sec.web.config.JWTUtil;
10 | import com.sp.sec.web.config.UserLogin;
11 | import org.junit.jupiter.api.BeforeEach;
12 | import org.junit.jupiter.api.DisplayName;
13 | import org.junit.jupiter.api.Test;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.boot.test.context.SpringBootTest;
16 | import org.springframework.boot.web.server.LocalServerPort;
17 | import org.springframework.http.HttpEntity;
18 | import org.springframework.http.HttpMethod;
19 | import org.springframework.http.ResponseEntity;
20 | import org.springframework.security.core.parameters.P;
21 | import org.springframework.security.crypto.password.PasswordEncoder;
22 | import org.springframework.test.context.ActiveProfiles;
23 | import org.springframework.web.client.HttpClientErrorException;
24 | import org.springframework.web.client.RestTemplate;
25 |
26 | import java.net.URI;
27 | import java.net.URISyntaxException;
28 | import java.util.Set;
29 |
30 | import static java.lang.String.format;
31 | import static org.junit.jupiter.api.Assertions.assertEquals;
32 | import static org.junit.jupiter.api.Assertions.assertThrows;
33 |
34 | @ActiveProfiles("test")
35 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
36 | public class JWTLoginFilterTest {
37 |
38 | @LocalServerPort
39 | private int port;
40 |
41 | @Autowired
42 | private ObjectMapper objectMapper;
43 |
44 | @Autowired
45 | private UserService userService;
46 | @Autowired
47 | private PasswordEncoder passwordEncoder;
48 |
49 | private UserTestHelper userTestHelper;
50 |
51 | private RestTemplate restTemplate = new RestTemplate();
52 |
53 | private URI uri(String path) throws URISyntaxException {
54 | return new URI(format("http://localhost:%d%s", port, path));
55 | }
56 |
57 | @BeforeEach
58 | void before(){
59 | userService.clearUsers();
60 | this.userTestHelper = new UserTestHelper(userService, passwordEncoder);
61 | this.userTestHelper.createUser("user1", Authority.ROLE_USER);
62 | }
63 |
64 | @DisplayName("1. jwt 로 로그인을 시도한다.")
65 | @Test
66 | void test_1() throws URISyntaxException {
67 | UserLogin login = UserLogin.builder().username("user1@test.com")
68 | .password("user1123").build();
69 | HttpEntity body = new HttpEntity<>(login);
70 | ResponseEntity response = restTemplate.exchange(uri("/login"), HttpMethod.POST, body, String.class);
71 | assertEquals(200, response.getStatusCodeValue());
72 | }
73 |
74 | @DisplayName("2. 비번이 틀리면 로그인을 하지 못한다.")
75 | @Test
76 | void test_2() throws URISyntaxException {
77 | UserLogin login = UserLogin.builder().username("user1@test.com")
78 | .password("1234").build();
79 | HttpEntity body = new HttpEntity<>(login);
80 |
81 | assertThrows(HttpClientErrorException.class, ()->{
82 | restTemplate.exchange(uri("/login"), HttpMethod.POST, body, String.class);
83 | // expected 401 에러
84 | });
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/web/jwt-user-web/src/test/java/com/sp/sec/web/JWTTokenTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.auth0.jwt.JWT;
4 | import com.auth0.jwt.algorithms.Algorithm;
5 | import com.auth0.jwt.exceptions.TokenExpiredException;
6 | import com.auth0.jwt.interfaces.Claim;
7 | import com.auth0.jwt.interfaces.DecodedJWT;
8 | import org.junit.jupiter.api.Disabled;
9 | import org.junit.jupiter.api.DisplayName;
10 | import org.junit.jupiter.api.Test;
11 |
12 | import java.time.Instant;
13 | import java.util.Date;
14 | import java.util.Map;
15 | import java.util.stream.Collectors;
16 | import java.util.stream.Stream;
17 |
18 | import static org.junit.jupiter.api.Assertions.assertThrows;
19 |
20 | public class JWTTokenTest {
21 |
22 | static void printClaim(String key, Claim value){
23 | if(value.isNull()){
24 | System.out.printf("%s:%s\n", key, "none");
25 | return;
26 | }
27 | if(value.asString() != null){
28 | System.out.printf("%s:{str}%s\n", key, value.asString());
29 | return;
30 | }
31 | if(value.asLong() != null){
32 | System.out.printf("%s:{lng}%d\n", key, value.asLong());
33 | return;
34 | }
35 | if(value.asInt() != null ){
36 | System.out.printf("%s:{int}%d\n", key, value.asInt());
37 | return;
38 | }
39 | if(value.asBoolean() != null){
40 | System.out.printf("%s:{bol}%b\n", key, value.asBoolean());
41 | return;
42 | }
43 | if(value.asDate() != null){
44 | System.out.printf("%s:{dte}%s\n", key, value.asDate().toString());
45 | return;
46 | }
47 | if(value.asDouble() != null){
48 | System.out.printf("%s:{dbl}%f\n", key, value.asDouble());
49 | return;
50 | }
51 | String[] values = value.asArray(String.class);
52 | if(values != null){
53 | System.out.printf("%s:{arr}%s\n", key, Stream.of(values).collect(Collectors.joining(",")));
54 | return;
55 | }
56 | Map valueMap = value.asMap();
57 | if(valueMap != null) {
58 | System.out.printf("%s:{map}%s\n", key, valueMap);
59 | return;
60 | }
61 | System.out.println("====>> unknown type for :"+key);
62 | }
63 |
64 | @DisplayName("1. JWT 토큰이 잘 만들어 진다.")
65 | @Test
66 | @Disabled
67 | void test_() throws InterruptedException {
68 |
69 | Algorithm AL = Algorithm.HMAC256("hello");
70 | String token = JWT.create()
71 | .withSubject("jongwon")
72 | .withClaim("exp", Instant.now().getEpochSecond()+2)
73 | .withArrayClaim("role", new String[]{"ROLE_ADMIN", "ROLE_USER"})
74 | .sign(AL);
75 | System.out.println(token);
76 | // DecodedJWT decode = JWT.decode(token);
77 |
78 | Thread.sleep(1000);
79 |
80 | DecodedJWT decode = JWT.require(AL).build().verify(token);
81 |
82 | printClaim("typ", decode.getHeaderClaim("typ"));
83 | printClaim("alg", decode.getHeaderClaim("alg"));
84 | System.out.println("=======");
85 | decode.getClaims().forEach(JWTTokenTest::printClaim);
86 |
87 | Thread.sleep(2000);
88 |
89 | assertThrows(TokenExpiredException.class, ()->{
90 | JWT.require(AL).build().verify(token);
91 | });
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/web/jwt-user-web/src/test/java/com/sp/sec/web/JwtUserWebApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 | import org.springframework.test.context.ActiveProfiles;
6 |
7 | @ActiveProfiles("test")
8 | @SpringBootTest
9 | class JwtUserWebApplicationTests {
10 |
11 | @Test
12 | void contextLoads() {
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/web/oauth2-client-test/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/web/oauth2-client-test/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | oauth2-client-test
13 | 1.0.0
14 | oauth2-client-test
15 | oauth2-client-test
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-oauth2-client
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 | org.springframework.security
47 | spring-security-test
48 | test
49 |
50 |
51 |
52 |
53 |
54 |
55 | org.springframework.boot
56 | spring-boot-maven-plugin
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/java/com/sp/sec/web/Oauth2ClientTestApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Oauth2ClientTestApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Oauth2ClientTestApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.sp.sec.web.service.SpOAuth2UserService;
4 | import com.sp.sec.web.service.SpOidcUserService;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 | import org.springframework.security.core.userdetails.User;
12 | import org.springframework.security.core.userdetails.UserDetails;
13 | import org.springframework.security.core.userdetails.UserDetailsService;
14 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
15 | import org.springframework.security.crypto.password.PasswordEncoder;
16 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
17 |
18 | @EnableWebSecurity
19 | @EnableGlobalMethodSecurity(prePostEnabled = true)
20 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
21 |
22 | @Autowired
23 | private SpOAuth2UserService oAuth2UserService;
24 |
25 | @Autowired
26 | private SpOidcUserService oidcUserService;
27 |
28 |
29 | @Bean
30 | UserDetailsService users() {
31 | UserDetails user1 = User.builder()
32 | .username("user1")
33 | .password(passwordEncoder().encode("1234"))
34 | .roles("USER")
35 | .build();
36 |
37 | UserDetails admin = User.builder()
38 | .username("admin")
39 | .password(passwordEncoder().encode("1234"))
40 | .roles("ADMIN")
41 | .build();
42 |
43 | return new InMemoryUserDetailsManager(user1, admin);
44 | }
45 |
46 | @Bean
47 | PasswordEncoder passwordEncoder() {
48 | return new BCryptPasswordEncoder();
49 | }
50 |
51 |
52 |
53 | @Override
54 | protected void configure(HttpSecurity http) throws Exception {
55 | http
56 | .formLogin()
57 | .and()
58 | .oauth2Login(oauth2->{
59 | oauth2.userInfoEndpoint(userinfo->{
60 | userinfo.userService(oAuth2UserService) // OAuth2 방식
61 | .oidcUserService(oidcUserService) // OIDC 방식
62 | ;
63 | });
64 | })
65 | ;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/java/com/sp/sec/web/config/SpOidcUserToSiteUserFilter.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 |
4 | public class SpOidcUserToSiteUserFilter {
5 | }
6 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/java/com/sp/sec/web/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.controller;
2 |
3 | import org.springframework.security.access.prepost.PreAuthorize;
4 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
5 | import org.springframework.security.core.userdetails.UserDetails;
6 | import org.springframework.security.oauth2.core.user.OAuth2User;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @RestController
11 | public class HomeController {
12 |
13 |
14 | @RequestMapping("/")
15 | @PreAuthorize("isAuthenticated()")
16 | public OAuth2User home(@AuthenticationPrincipal OAuth2User user){
17 | return user;
18 | }
19 |
20 | @RequestMapping("/site")
21 | @PreAuthorize("isAuthenticated()")
22 | public UserDetails siteUser(@AuthenticationPrincipal UserDetails user){
23 | return user;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/java/com/sp/sec/web/service/SpOAuth2UserService.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.service;
2 |
3 | import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
4 | import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
5 | import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
6 | import org.springframework.security.oauth2.core.user.OAuth2User;
7 | import org.springframework.stereotype.Service;
8 |
9 | @Service
10 | public class SpOAuth2UserService extends DefaultOAuth2UserService {
11 |
12 | @Override
13 | public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
14 | OAuth2User user = super.loadUser(userRequest);
15 |
16 | // TODO : ExtendedUser 로 바꾼뒤 리턴한다.
17 |
18 | return user;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/java/com/sp/sec/web/service/SpOidcUserService.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.service;
2 |
3 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
4 | import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
5 | import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
6 | import org.springframework.security.oauth2.core.oidc.user.OidcUser;
7 | import org.springframework.stereotype.Service;
8 |
9 | @Service
10 | public class SpOidcUserService extends OidcUserService {
11 | @Override
12 | public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
13 | OidcUser user = super.loadUser(userRequest);
14 |
15 | // TODO : ExtendedUser 를 생성을 보장한다.
16 |
17 | return user;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 |
2 | server:
3 | port: 9007
4 |
5 | spring:
6 | security:
7 | oauth2:
8 | client:
9 | registration:
10 | google:
11 | client-id: 377825014181-2tpha7niq0hjar86e4j8h6dl0h8j1s3i.apps.googleusercontent.com
12 | client-secret: yyHXhXWBRrx3jyDWrhiSwzu8
13 | facebook:
14 | client-id: 488695445381590
15 | client-secret: bcbbc8a3a1d98bedeb258b318878e745
16 | kakao:
17 | client-id: fa84d469d8038b6a422d1f9b2b6f9067
18 | client-secert: RuubX7J4HuhrekWplEeu2FnlSMiCftHH
19 | redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
20 | authorization-grant-type: authorization_code
21 | client-name: kakao
22 | naver:
23 | client-id: bkdZjYp4EfmqIkZaEtqi
24 | client-secret: YLHoiLkdSy
25 | redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
26 | authorization-grant-type: authorization_code
27 | client-name: naver
28 | provider:
29 | kakao:
30 | authorization-uri: https://kauth.kakao.com/oauth/authorize
31 | token-uri: https://kauth.kakao.com/oauth/token
32 | user-info-uri: https://kapi.kakao.com/v2/user/me
33 | user-name-attribute: id
34 | naver:
35 | authorization-uri: https://nid.naver.com/oauth2.0/authorize
36 | token-uri: https://nid.naver.com/oauth2.0/token
37 | user-info-uri: https://openapi.naver.com/v1/nid/me
38 | user-name-attribute: response
--------------------------------------------------------------------------------
/web/oauth2-client-test/src/test/java/com/sp/sec/web/Oauth2ClientTestApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class Oauth2ClientTestApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/resource-server-1/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/web/resource-server-1/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/web/resource-server-1/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/web/resource-server-1/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | resource-server-1
13 | 1.0.0
14 | resource-server-1
15 | 테스트 리소스 서버1
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 | org.junit.vintage
47 | junit-vintage-engine
48 |
49 |
50 |
51 |
52 | de.flapdoodle.embed
53 | de.flapdoodle.embed.mongo
54 | test
55 |
56 |
57 | org.springframework.security
58 | spring-security-test
59 | test
60 |
61 |
62 |
63 | com.sp.sec
64 | user-authority
65 | 1.0.0
66 |
67 |
68 |
69 | com.sp.sec
70 | user-authority
71 | 1.0.0
72 | test-jar
73 | test
74 |
75 |
76 |
77 | com.sp.sec
78 | sp-board
79 | 1.0.0
80 |
81 |
82 |
83 | com.sp.sec
84 | sp-board
85 | 1.0.0
86 | test-jar
87 | test
88 |
89 |
90 |
91 |
92 | com.sp.sec
93 | sp-jwt-security
94 | 1.0.0
95 |
96 |
97 |
98 | com.sp.sec
99 | sp-jwt-security
100 | 1.0.0
101 | test-jar
102 | test
103 |
104 |
105 |
106 |
107 | com.sp.sec
108 | sp-web-util
109 | 1.0.0
110 |
111 |
112 |
113 |
114 |
115 |
116 | org.springframework.boot
117 | spring-boot-maven-plugin
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/web/resource-server-1/src/main/java/com/sp/sec/web/ResourceServer1Application.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication(scanBasePackages = {
7 | "com.sp.sec.config",
8 | "com.sp.sec.web"
9 | })
10 | public class ResourceServer1Application {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(ResourceServer1Application.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/web/resource-server-1/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
12 | import org.springframework.security.config.http.SessionCreationPolicy;
13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14 |
15 | @EnableWebSecurity
16 | @EnableGlobalMethodSecurity(prePostEnabled = true)
17 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
18 |
19 | private final UserService userService;
20 | private final ObjectMapper objectMapper;
21 | private final JWTUtil jwtUtil;
22 |
23 | public SecurityConfig(UserService userService, ObjectMapper objectMapper, JWTUtil jwtUtil) {
24 | this.userService = userService;
25 | this.objectMapper = objectMapper;
26 | this.jwtUtil = jwtUtil;
27 | }
28 |
29 |
30 | @Override
31 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
32 | auth.userDetailsService(userService)
33 | .passwordEncoder(passwordEncoder());
34 | }
35 |
36 | @Bean
37 | BCryptPasswordEncoder passwordEncoder(){
38 | return new BCryptPasswordEncoder();
39 | }
40 |
41 | @Override
42 | protected AuthenticationManager authenticationManager() throws Exception {
43 | return super.authenticationManager();
44 | }
45 |
46 | @Override
47 | protected void configure(HttpSecurity http) throws Exception {
48 | final JWTCheckFilter checkFilter = new JWTCheckFilter(authenticationManager(), userService, jwtUtil);
49 | http
50 | .csrf().disable()
51 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
52 | .and()
53 | .addFilter(checkFilter)
54 | ;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/web/resource-server-1/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 |
2 |
3 | sp:
4 | jwt:
5 | secret: auth-server-1
6 | token-life-time: 100
7 | token-refresh-time: 10000
8 |
--------------------------------------------------------------------------------
/web/resource-server-1/src/test/java/com/sp/sec/web/AuthResourceTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import com.fasterxml.jackson.core.JsonProcessingException;
4 | import com.fasterxml.jackson.core.type.TypeReference;
5 | import com.sp.sec.board.domain.SpBoardSummary;
6 | import com.sp.sec.board.service.SpBoardService;
7 | import com.sp.sec.board.service.SpBoardTestHelper;
8 | import com.sp.sec.user.domain.Authority;
9 | import com.sp.sec.user.domain.User;
10 | import com.sp.sec.web.config.UserLogin;
11 | import com.sp.sec.web.util.RestResponsePage;
12 | import org.junit.jupiter.api.BeforeEach;
13 | import org.junit.jupiter.api.DisplayName;
14 | import org.junit.jupiter.api.Test;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.boot.test.context.SpringBootTest;
17 | import org.springframework.http.HttpEntity;
18 | import org.springframework.http.HttpMethod;
19 | import org.springframework.http.ResponseEntity;
20 |
21 | import java.net.URISyntaxException;
22 | import java.util.Set;
23 |
24 | import static org.junit.jupiter.api.Assertions.assertEquals;
25 |
26 | /**
27 | * Auth 서버로 부터
28 | *
29 | */
30 | @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
31 | public class AuthResourceTest extends SpJwtRefreshableTwoUserIntegrationTest {
32 |
33 | @Autowired
34 | private SpBoardService boardService;
35 |
36 | private SpBoardTestHelper boardTestHelper;
37 |
38 | @BeforeEach
39 | void before(){
40 | prepareTwoUsers();
41 | User 인증서버의사용자 = User.builder()
42 | .userId("5fab670555c23260d5f5fdbf")
43 | .email("user3@test.com")
44 | .name("user1")
45 | .authorities(Set.of(Authority.USER))
46 | .enabled(true)
47 | .build();
48 | userService.save(인증서버의사용자);
49 |
50 | boardTestHelper = new SpBoardTestHelper(boardService);
51 | boardTestHelper.createBoard(USER1, "title1", "content1");
52 | boardTestHelper.createBoard(USER1, "title2", "content2");
53 | }
54 |
55 | @DisplayName("Auth 서버에서 토큰을 받아와서 리소스 서버로 부터 게시판의 게시글을 조회한다.")
56 | @Test
57 | void test_1() throws URISyntaxException, JsonProcessingException {
58 | Tokens 인증서버토큰 = getAuthServerToken("user1@test.com", "1234");
59 | ResponseEntity response = restTemplate.exchange(uri("/board/list"),
60 | HttpMethod.GET, getAuthHeaderEntity(인증서버토큰.getAccessToken()), String.class);
61 | assertEquals(200, response.getStatusCodeValue());
62 |
63 | RestResponsePage page = objectMapper.readValue(response.getBody(), new TypeReference>() {
64 | });
65 | assertEquals(2, page.getTotalElements());
66 | }
67 |
68 | private Tokens getAuthServerToken(String username, String password) {
69 | UserLogin login = UserLogin.builder().type(UserLogin.Type.login)
70 | .username(username).password(password).build();
71 | HttpEntity body = new HttpEntity<>(login);
72 | ResponseEntity response = restTemplate.exchange(("http://localhost:9001/login"),
73 | HttpMethod.POST, body, String.class);
74 | return Tokens.builder()
75 | .accessToken(getAccessToken(response))
76 | .refreshToken(getRefreshToken(response))
77 | .build();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/web/resource-server-1/src/test/java/com/sp/sec/web/ResourceServer1ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class ResourceServer1ApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/security-basic/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 | com.sp.sec
11 | security-basic
12 | 1.0.0
13 | security-basic
14 | 사용자 모듈
15 |
16 |
17 | 11
18 |
19 |
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-web
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-data-mongodb
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-security
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-test
37 | test
38 |
39 |
40 | org.junit.vintage
41 | junit-vintage-engine
42 |
43 |
44 |
45 |
46 | de.flapdoodle.embed
47 | de.flapdoodle.embed.mongo
48 | test
49 |
50 |
51 | org.springframework.security
52 | spring-security-test
53 | test
54 |
55 |
56 | org.projectlombok
57 | lombok
58 |
59 |
60 | com.fasterxml.jackson.core
61 | jackson-annotations
62 |
63 |
64 |
65 |
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-maven-plugin
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/java/com/sp/sec/basic/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic;
2 |
3 |
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.ui.Model;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | @Controller
12 | public class HomeController {
13 |
14 | @RequestMapping("/")
15 | public String home() {
16 | return "index";
17 | }
18 |
19 | @RequestMapping("/login")
20 | public String login(
21 | @RequestParam(defaultValue = "false") Boolean error,
22 | Model model
23 | ) {
24 | if (error) {
25 | model.addAttribute("errorMessage", "아이디나 패스워드가 올바르지 않습니다.");
26 | }
27 | return "loginForm";
28 | }
29 |
30 |
31 | // @GetMapping(value="/hello")
32 | // public String hello(){
33 | // return "hello jongwon";
34 | // }
35 | }
36 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/java/com/sp/sec/basic/SecurityBasicApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
7 | import org.springframework.security.core.userdetails.User;
8 | import org.springframework.security.core.userdetails.UserDetails;
9 | import org.springframework.security.core.userdetails.UserDetailsService;
10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
11 | import org.springframework.security.crypto.password.PasswordEncoder;
12 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
13 |
14 | @SpringBootApplication
15 | @EnableGlobalMethodSecurity(securedEnabled = true)
16 | public class SecurityBasicApplication {
17 |
18 | public static void main(String[] args) {
19 | SpringApplication.run(SecurityBasicApplication.class, args);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/java/com/sp/sec/basic/SecurityMessage.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnore;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import org.springframework.security.core.Authentication;
10 |
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | @Builder
15 | public class SecurityMessage {
16 |
17 | private String message;
18 |
19 | @JsonIgnore
20 | private Authentication auth;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/java/com/sp/sec/basic/TestController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic;
2 |
3 |
4 | import org.springframework.security.access.annotation.Secured;
5 | import org.springframework.security.core.context.SecurityContextHolder;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | @RestController
10 | public class TestController {
11 |
12 | @Secured({"ROLE_USER", "ROLE_ADMIN"})
13 | @GetMapping(value = "/user")
14 | public SecurityMessage user() {
15 | return SecurityMessage.builder()
16 | .message("user page")
17 | .auth(SecurityContextHolder.getContext().getAuthentication()).build();
18 | }
19 |
20 | @Secured({"ROLE_ADMIN"})
21 | @GetMapping(value = "/admin")
22 | public SecurityMessage admin() {
23 | return SecurityMessage.builder()
24 | .message("admin page")
25 | .auth(SecurityContextHolder.getContext().getAuthentication()).build();
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/java/com/sp/sec/basic/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
5 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
7 | import org.springframework.security.core.userdetails.User;
8 | import org.springframework.security.core.userdetails.UserDetails;
9 | import org.springframework.security.core.userdetails.UserDetailsService;
10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
11 | import org.springframework.security.crypto.password.PasswordEncoder;
12 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
13 |
14 |
15 | @EnableWebSecurity
16 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
17 |
18 | @Bean
19 | UserDetailsService users() {
20 | UserDetails user1 = User.builder()
21 | .username("user1")
22 | .password(passwordEncoder().encode("1234"))
23 | .roles("USER")
24 | .build();
25 |
26 | UserDetails admin = User.builder()
27 | .username("admin")
28 | .password(passwordEncoder().encode("1234"))
29 | .roles("ADMIN")
30 | .build();
31 |
32 | return new InMemoryUserDetailsManager(user1, admin);
33 | }
34 |
35 | @Bean
36 | PasswordEncoder passwordEncoder() {
37 | return new BCryptPasswordEncoder();
38 | }
39 |
40 |
41 | @Override
42 | protected void configure(HttpSecurity http) throws Exception {
43 | http
44 | .csrf().disable()
45 | .formLogin(config -> {
46 | config.loginPage("/login")
47 | // .successForwardUrl("/") // requestCache
48 | .failureForwardUrl("/login?error=true");
49 | })
50 | .authorizeRequests(config -> {
51 | config.antMatchers("/login")
52 | .permitAll()
53 | .antMatchers("/")
54 | .authenticated()
55 | ;
56 | })
57 | ;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/java/com/sp/sec/user/UserAuthorityApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class UserAuthorityApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(UserAuthorityApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8095
3 |
4 | spring:
5 | security:
6 | user:
7 | name: user1
8 | password: 1234
9 | roles: USER
--------------------------------------------------------------------------------
/web/security-basic/src/main/resources/static/login.css:
--------------------------------------------------------------------------------
1 |
2 | .error-message {
3 | color: red;
4 | }
--------------------------------------------------------------------------------
/web/security-basic/src/main/resources/static/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | ul {
6 | list-style: none;
7 | margin: 0;
8 | padding: 0;
9 | display: flex;
10 | flex-direction: row;
11 | width: 100vw;
12 | }
13 |
14 | ul li {
15 | justify-content: space-between;
16 | align-items: center;
17 | background-color: #196ba2;
18 | color: white;
19 | border-radius: 0.5em;
20 | cursor: pointer;
21 | margin: 1em;
22 | }
23 |
24 | ul li:hover {
25 | background-color: #4991c3;
26 | }
27 |
28 | ul li a {
29 | display: inline-block;
30 | padding: 1em;
31 | text-decoration: none;
32 | color: white;
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | 테스트 홈 화면
7 |
8 |
9 |
10 |
11 | Security Test
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/web/security-basic/src/main/resources/templates/loginForm.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | 로그인
7 |
8 |
9 |
10 |
11 |
29 |
30 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/web/security-basic/src/test/java/com/sp/sec/basic/SecurityBasicApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class SecurityBasicApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/security-basic/src/test/java/com/sp/sec/basic/UserAccessTest.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.basic;
2 |
3 |
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import org.junit.jupiter.api.DisplayName;
6 | import org.junit.jupiter.api.Test;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
9 | import org.springframework.security.core.userdetails.User;
10 | import org.springframework.security.core.userdetails.UserDetails;
11 | import org.springframework.security.crypto.password.PasswordEncoder;
12 | import org.springframework.security.test.context.support.WithAnonymousUser;
13 | import org.springframework.test.web.servlet.MockMvc;
14 |
15 | import static org.junit.jupiter.api.Assertions.assertEquals;
16 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
17 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
19 |
20 | @WebMvcTest
21 | public class UserAccessTest {
22 |
23 | @Autowired
24 | private MockMvc mockMvc;
25 |
26 | @Autowired
27 | private ObjectMapper mapper;
28 |
29 | @Autowired
30 | private PasswordEncoder passwordEncoder;
31 |
32 | UserDetails user1() {
33 | return User.builder()
34 | .username("user1")
35 | .password(passwordEncoder.encode("1234"))
36 | .roles("USER")
37 | .build();
38 | }
39 |
40 | UserDetails admin() {
41 | return User.builder()
42 | .username("admin")
43 | .password(passwordEncoder.encode("1234"))
44 | .roles("ADMIN")
45 | .build();
46 | }
47 |
48 |
49 | @DisplayName("1. user 로 user 페이지를 접근할 수 있다.")
50 | @Test
51 | // @WithMockUser(username = "user1", roles = {"USER"})
52 | void test_user_access_userpage() throws Exception {
53 | String resp = mockMvc.perform(get("/user").with(user(user1())))
54 | .andExpect(status().isOk())
55 | .andReturn().getResponse().getContentAsString();
56 | SecurityMessage message = mapper.readValue(resp, SecurityMessage.class);
57 | assertEquals("user page", message.getMessage());
58 | }
59 |
60 | @DisplayName("2. user로 admin 페이지를 접근할 수 없다.")
61 | @Test
62 | // @WithMockUser(username = "user1", roles = {"USER"})
63 | void test_user_cannot_access_adminpage() throws Exception {
64 | mockMvc.perform(get("/admin").with(user(user1())))
65 | .andExpect(status().is4xxClientError());
66 | }
67 |
68 | @DisplayName("3. admin 이 user 페이지와 admin 페이지를 접근할 수 있다.")
69 | @Test
70 | // @WithMockUser(username="admin", roles={"ADMIN"})
71 | void test_admin_can_access_user_and_admin_page() throws Exception {
72 | SecurityMessage message = mapper.readValue(mockMvc.perform(get("/user").with(user(admin())))
73 | .andExpect(status().isOk())
74 | .andReturn().getResponse().getContentAsString(), SecurityMessage.class);
75 | assertEquals("user page", message.getMessage());
76 |
77 | message = mapper.readValue(mockMvc.perform(get("/admin").with(user(admin())))
78 | .andExpect(status().isOk())
79 | .andReturn().getResponse().getContentAsString(), SecurityMessage.class);
80 | assertEquals("admin page", message.getMessage());
81 |
82 | }
83 |
84 | @DisplayName("4. login 페이지는 아무나 접근할 수 있어야 한다.")
85 | @Test
86 | @WithAnonymousUser
87 | void test_login_page_can_accessed_anonymous() throws Exception {
88 | mockMvc.perform(get("/login"))
89 | .andExpect(status().isOk());
90 | }
91 |
92 | @DisplayName("5. / 홈페이지는 로그인 하지 않은 사람은 접근할 수 없다.")
93 | @Test
94 | void test_need_login() throws Exception {
95 | mockMvc.perform(get("/"))
96 | .andExpect(status().is3xxRedirection()); // 302 redirect to /login
97 | mockMvc.perform(get("/user")).andExpect(status().is3xxRedirection());
98 | mockMvc.perform(get("/admin")).andExpect(status().is3xxRedirection());
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/web/security-basic/src/test/java/com/sp/sec/user/UserAuthorityApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.user;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class UserAuthorityApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/sp-board-web/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jongwon/spring-security-junit5-test/8398bcfcc938cf1709496b099cd8e50270ec3dcf/web/sp-board-web/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/web/sp-board-web/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
3 |
--------------------------------------------------------------------------------
/web/sp-board-web/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | sp-board-web
13 | 1.0.0
14 | sp-board-web
15 | 게시판 테스트 웹
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 |
36 | org.projectlombok
37 | lombok
38 | true
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-test
43 | test
44 |
45 |
46 | org.junit.vintage
47 | junit-vintage-engine
48 |
49 |
50 |
51 |
52 | de.flapdoodle.embed
53 | de.flapdoodle.embed.mongo
54 | test
55 |
56 |
57 | org.springframework.security
58 | spring-security-test
59 | test
60 |
61 |
62 |
63 | com.sp.sec
64 | user-authority
65 | 1.0.0
66 |
67 |
68 |
69 | com.sp.sec
70 | user-authority
71 | 1.0.0
72 | test-jar
73 | test
74 |
75 |
76 |
77 | com.sp.sec
78 | sp-board
79 | 1.0.0
80 |
81 |
82 |
83 | com.sp.sec
84 | sp-board
85 | 1.0.0
86 | test-jar
87 | test
88 |
89 |
90 |
91 |
92 | com.sp.sec
93 | sp-jwt-security
94 | 1.0.0
95 |
96 |
97 |
98 | com.sp.sec
99 | sp-jwt-security
100 | 1.0.0
101 | test-jar
102 | test
103 |
104 |
105 |
106 |
107 | com.sp.sec
108 | sp-web-util
109 | 1.0.0
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | org.springframework.boot
118 | spring-boot-maven-plugin
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/web/sp-board-web/src/main/java/com/sp/sec/web/SpBoardWebApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication(scanBasePackages = {
7 | "com.sp.sec.config",
8 | "com.sp.sec.web"
9 | })
10 | public class SpBoardWebApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(SpBoardWebApplication.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/web/sp-board-web/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.sp.sec.user.service.UserService;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.authentication.AuthenticationManager;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
12 | import org.springframework.security.config.http.SessionCreationPolicy;
13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
14 |
15 | @EnableWebSecurity
16 | @EnableGlobalMethodSecurity(prePostEnabled = true)
17 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
18 |
19 | private final UserService userService;
20 | private final ObjectMapper objectMapper;
21 | private final JWTUtil jwtUtil;
22 |
23 | public SecurityConfig(UserService userService, ObjectMapper objectMapper, JWTUtil jwtUtil) {
24 | this.userService = userService;
25 | this.objectMapper = objectMapper;
26 | this.jwtUtil = jwtUtil;
27 | }
28 |
29 |
30 | @Override
31 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
32 | auth.userDetailsService(userService)
33 | .passwordEncoder(passwordEncoder());
34 | }
35 |
36 | @Bean
37 | BCryptPasswordEncoder passwordEncoder(){
38 | return new BCryptPasswordEncoder();
39 | }
40 |
41 | @Override
42 | protected AuthenticationManager authenticationManager() throws Exception {
43 | return super.authenticationManager();
44 | }
45 |
46 | @Override
47 | protected void configure(HttpSecurity http) throws Exception {
48 | final JWTLoginFilter loginFilter = new JWTLoginFilter(authenticationManager(), jwtUtil, objectMapper);
49 | final JWTCheckFilter checkFilter = new JWTCheckFilter(authenticationManager(), userService, jwtUtil);
50 |
51 | http
52 | .csrf().disable()
53 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
54 | .and()
55 | .addFilter(loginFilter)
56 | .addFilter(checkFilter)
57 | ;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/web/sp-board-web/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web/sp-board-web/src/test/java/com/sp/sec/web/SpBoardWebApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class SpBoardWebApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.springframework.boot
7 | spring-boot-starter-parent
8 | 2.4.0
9 |
10 |
11 | com.sp.sec
12 | user-authority-test-web
13 | 0.0.1-SNAPSHOT
14 | user-authority-test-web
15 | user authority 모듈 테스트 웹
16 |
17 |
18 | 11
19 |
20 |
21 |
22 |
23 | org.springframework.boot
24 | spring-boot-starter-data-mongodb
25 |
26 |
27 | org.springframework.boot
28 | spring-boot-starter-security
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-thymeleaf
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-web
37 |
38 |
39 | org.thymeleaf.extras
40 | thymeleaf-extras-springsecurity5
41 |
42 |
43 |
44 | org.projectlombok
45 | lombok
46 | true
47 |
48 |
49 | org.springframework.boot
50 | spring-boot-starter-test
51 | test
52 |
53 |
54 | org.junit.vintage
55 | junit-vintage-engine
56 |
57 |
58 |
59 |
60 | de.flapdoodle.embed
61 | de.flapdoodle.embed.mongo
62 | test
63 |
64 |
65 | org.springframework.security
66 | spring-security-test
67 | test
68 |
69 |
70 |
71 | com.sp.sec
72 | user-authority
73 | 1.0.0
74 |
75 |
76 |
77 | com.sp.sec
78 | user-authority
79 | 1.0.0
80 | test-jar
81 | test
82 |
83 |
84 | com.sp.sec
85 | sp-web-util
86 | 1.0.0
87 | compile
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | org.springframework.boot
96 | spring-boot-maven-plugin
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/UserAuthorityTestWebApplication.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication(scanBasePackages = {
7 | "com.sp.sec.user",
8 | "com.sp.sec.web"
9 | })
10 | public class UserAuthorityTestWebApplication {
11 |
12 | public static void main(String[] args) {
13 | SpringApplication.run(UserAuthorityTestWebApplication.class, args);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/config/DBInit.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.sp.sec.user.domain.Authority;
4 | import com.sp.sec.user.domain.User;
5 | import com.sp.sec.user.service.UserService;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.CommandLineRunner;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 | import org.springframework.stereotype.Component;
10 |
11 | import java.util.Set;
12 |
13 | @Component
14 | public class DBInit implements CommandLineRunner {
15 |
16 | @Autowired
17 | private UserService userService;
18 |
19 | @Autowired
20 | private PasswordEncoder passwordEncoder;
21 |
22 | @Override
23 | public void run(String... args) throws Exception {
24 | userService.clearUsers();
25 | User user1 = User.builder().name("user2")
26 | .email("user2@test.com")
27 | .password(passwordEncoder.encode("1234"))
28 | .enabled(true)
29 | .authorities(Set.of(Authority.USER))
30 | .build();
31 | User admin = User.builder().name("admin")
32 | .email("admin@test.com")
33 | .password(passwordEncoder.encode("admin"))
34 | .enabled(true)
35 | .authorities(Set.of(Authority.ADMIN))
36 | .build();
37 |
38 | userService.save(user1);
39 | userService.save(admin);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/config/MongoConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
5 |
6 | @Configuration
7 | @EnableMongoRepositories(basePackages = {
8 | "com.sp.sec.user.repository"
9 | })
10 | public class MongoConfig {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.config;
2 |
3 | import com.sp.sec.user.service.UserService;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
7 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
10 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
11 | import org.springframework.security.core.userdetails.User;
12 | import org.springframework.security.core.userdetails.UserDetails;
13 | import org.springframework.security.core.userdetails.UserDetailsService;
14 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
15 | import org.springframework.security.crypto.password.PasswordEncoder;
16 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
17 |
18 |
19 | @EnableWebSecurity
20 | @EnableGlobalMethodSecurity(prePostEnabled = true)
21 | public class SecurityConfig extends WebSecurityConfigurerAdapter {
22 |
23 | @Autowired
24 | private UserService userService;
25 |
26 | @Override
27 | protected void configure(AuthenticationManagerBuilder auth) throws Exception {
28 | auth.userDetailsService(userService);
29 | }
30 |
31 | @Bean
32 | PasswordEncoder passwordEncoder() {
33 | return new BCryptPasswordEncoder();
34 | }
35 |
36 |
37 | @Override
38 | protected void configure(HttpSecurity http) throws Exception {
39 | http
40 | .csrf().disable()
41 | .formLogin(config -> {
42 | config.loginPage("/login")
43 | // .successForwardUrl("/") // requestCache
44 | .failureForwardUrl("/login?error=true");
45 | })
46 | .authorizeRequests(config -> {
47 | config.antMatchers("/login")
48 | .permitAll()
49 | .antMatchers("/")
50 | .authenticated()
51 | ;
52 | })
53 | ;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.controller;
2 |
3 |
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.ui.Model;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RequestParam;
8 |
9 | @Controller
10 | public class HomeController {
11 |
12 | @RequestMapping("/")
13 | public String home() {
14 | return "index";
15 | }
16 |
17 | @RequestMapping("/login")
18 | public String login(
19 | @RequestParam(defaultValue = "false") Boolean error,
20 | Model model
21 | ) {
22 | if (error) {
23 | model.addAttribute("errorMessage", "아이디나 패스워드가 올바르지 않습니다.");
24 | }
25 | return "loginForm";
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/controller/SecurityMessage.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.controller;
2 |
3 |
4 | import com.fasterxml.jackson.annotation.JsonIgnore;
5 | import lombok.AllArgsConstructor;
6 | import lombok.Builder;
7 | import lombok.Data;
8 | import lombok.NoArgsConstructor;
9 | import org.springframework.security.core.Authentication;
10 |
11 | @Data
12 | @AllArgsConstructor
13 | @NoArgsConstructor
14 | @Builder
15 | public class SecurityMessage {
16 |
17 | private String message;
18 |
19 | @JsonIgnore
20 | private Authentication auth;
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/java/com/sp/sec/web/controller/TestController.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web.controller;
2 |
3 |
4 | import org.springframework.security.access.annotation.Secured;
5 | import org.springframework.security.access.prepost.PreAuthorize;
6 | import org.springframework.security.core.context.SecurityContextHolder;
7 | import org.springframework.web.bind.annotation.GetMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | @RestController
11 | public class TestController {
12 |
13 | @PreAuthorize("hasAnyAuthority('ROLE_USER', 'ROLE_ADMIN')")
14 | @GetMapping(value = "/user")
15 | public SecurityMessage user() {
16 | return SecurityMessage.builder()
17 | .message("user page")
18 | .auth(SecurityContextHolder.getContext().getAuthentication()).build();
19 | }
20 |
21 | @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
22 | @GetMapping(value = "/admin")
23 | public SecurityMessage admin() {
24 | return SecurityMessage.builder()
25 | .message("admin page")
26 | .auth(SecurityContextHolder.getContext().getAuthentication()).build();
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | server:
2 | port: 8097
3 | spring:
4 | data:
5 | mongodb:
6 | database: user-auth-test-web
7 | host: localhost
8 | port: 27018
9 |
10 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/resources/static/login.css:
--------------------------------------------------------------------------------
1 |
2 | .error-message {
3 | color: red;
4 | }
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/resources/static/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | box-sizing: border-box;
3 | }
4 |
5 | ul {
6 | list-style: none;
7 | margin: 0;
8 | padding: 0;
9 | display: flex;
10 | flex-direction: row;
11 | width: 100vw;
12 | }
13 |
14 | ul li {
15 | justify-content: space-between;
16 | align-items: center;
17 | background-color: #196ba2;
18 | color: white;
19 | border-radius: 0.5em;
20 | cursor: pointer;
21 | margin: 1em;
22 | }
23 |
24 | ul li:hover {
25 | background-color: #4991c3;
26 | }
27 |
28 | ul li a {
29 | display: inline-block;
30 | padding: 1em;
31 | text-decoration: none;
32 | color: white;
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | 테스트 홈 화면
7 |
8 |
9 |
10 |
11 | Security Test
12 |
13 |
19 |
20 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/main/resources/templates/loginForm.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | 로그인
7 |
8 |
9 |
10 |
11 |
29 |
30 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/web/user-authority-test-web/src/test/java/com/sp/sec/web/UserAuthorityTestWebApplicationTests.java:
--------------------------------------------------------------------------------
1 | package com.sp.sec.web;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class UserAuthorityTestWebApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------