반응형

npm 업데이트

npm install -g npm


npm 으로 설치한 글로벌 패키지 목록

npm list -g --depth=0


npm 으로 설치한 글로벌 패키지 업데이트

npm update -g


npm 으로 설치한 글로벌 패키지 삭제

npm uninstall -g [패키지명]


npm 최신 버전으로 업데이트

npm install -g npm


추후에.. 또 필요한게 있으면 추가 해야겠다...

728x90
반응형
반응형

테스트 케이스를 작성하다가 좀 헷갈리는게 있었다. @Mock, @MockBean 차이가 뭐지??? 쓰긴 하고 있는데 알고 써야 하지 않을까라는 의문이 들었다. 그래서 찾아봤다.

 

먼저 Mock 객체를 선언할 때에는 2가지 방법이 있다.

 

1. 첫번째 : mock() 을 이용해서 선언

 

1
2
3
4
5
6
7
8
9
10
11
12
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
 
    @InjectMocks
    private UserService userService;
    UserRepository userRepository = mock(UserRepository.class);
 
    @Test
    public void findByEmail_test(){
        when(userRepository.findByEmail("test@test.com")).thenReturn(Optional.of(new User()));
        userService.findByEmail("test@test.com");
        verify(userRepository).findByEmail("test@test.com");
    }
}
cs

 

2. 두번째 : @Mock 을 이용해서 선언

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
 
    @InjectMocks
    private UserService userService;
 
    @Mock
    private UserRepository userRepository;
 
    @Test
    public void findByEmail_test(){
        when(userRepository.findByEmail("test@test.com")).thenReturn(Optional.of(new User()));
        userService.findByEmail("test@test.com");
 
        verify(userRepository).findByEmail("test@test.com");
    }
}
cs

 

이 2개의 테스트 케이스는 동일하게 동작한다. 

 

그리고 선언한 Mock 객체를 테스트를 실행하는 동안 사용할수 있게 하기 위한 작업이 있다. 바로 위에서 보이는 @RunWith(MockitoJUnitRunner.class) 가 바로 그 역할을 해준다.  또다른 방법으로는 아래와 같은 메소드를 테스트케이스 실행 전에 실행 시키면 된다. 

 

 

1
2
3
4
@Before 
public void initMocks() {
    MockitoAnnotations.initMocks(this);
}
cs

 

여태껏 아무생각없이 둘다 @RunWith랑 initMocks랑 둘다 썼는데 둘다 쓸 필요가 없었다. ㅡㅡ;

 

그럼 @MockBean 은 뭐지??

 

@MockBean 은 Mock 과는 달리 org.springframework.boot.test.mock.mockito 패키지 하위에 존재한다. 즉 spring-boot-test에서 제공하는 어노테이션이다. Mockito 의 Mock 객체들을 Spring의 ApplicationContext에 넣어준다. 그리고 동일한 타입의 Bean  이 존재할 경우 MockBean으로 교체해준다.

 

그럼 언제 @Mock을 쓰고 언제 @MockBean 을 써야 하나??

 

결론적으로 Spring Boot Container가 필요하고 Bean이 container 에 존재 해야 한다면 @MockBean 을 쓰면 되고 아니라면 @Mock 을 쓰면 된다. 그런데 개인적으로는 그걸 잘 판단을 못하겠다.

 

1. MockitoJUnitRunner 를 이용해서 Mock을 사용한 Testcase.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RunWith(MockitoJUnitRunner.class)
public class RegisterControllerMockTest {
 
    @InjectMocks
    private RegisterController registerController;
 
    private ModelAndView model;
 
    @Before
    public void setup(){
        model = new ModelAndView();
    }
 
    @Test
    public void showRegistrationPage_test() throws Exception {
 
        registerController.showRegistrationPage(model, new User());
        Assert.assertEquals("register", model.getViewName());
    }
}
 
cs

 

2. SpringRunner와 @WebMvcTest를 사용해서 @MockBean 을 사용한 Testcase

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RunWith(SpringRunner.class)
@WebMvcTest(value = RegisterController.class, secure = false)
public class RegisterControllerTest {
 
    @Autowired
    private MockMvc mockMvc;
 
    @MockBean
    private UserService userService;
 
    @MockBean
    private EmailService emailService;
 
    @Test
    public void showRegistrationPage_test() throws Exception {
 
        mockMvc.perform(MockMvcRequestBuilders.get("/register"))
                .andExpect(status().isOk())
                .andExpect(view().name("register"))
                .andDo(print());
    }
}
cs

분명 이 2개의 테스트 케이스 는 결과는 같지만 실제 내부에서 동작하는 부분은 다르다. 2번만 spring applicaiton context 가 올라간다. 그래서 부득이하게 @MockBean 으로 UserService 와 EmailService를 넣어줘야 했다. 1번은 Controller 객체에 대해서 Mock 을 만들어서 그거에 대해서만 테스트를 한다.(뭐라고 표현을 해야할지 잘 모르겠다.)

 

이렇게 같은 컨트롤러에 대한 테스트 이지만 작성하기에 따라서 여러가지 방법으로 테스트 케이스를 만들 수 있다. 아직은 미숙하지만 좀더 많이 작성하다 보면 방법도 많이 알게 되고 실제 구현 코드들의 모습도 많이 나아질거라는 생각이 든다.

 

참고 : https://stackoverflow.com/questions/44200720/difference-between-mock-mockbean-and-mockito-mock

728x90
반응형
반응형

Docker 도큐먼트를 따라하다 보니 갑자기 에러가 난다. 분명 난 똑같이 붙여넣기 하고 그대로 한것 같은데..


https://docs.docker.com/get-started/part2/#build-the-app


unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /home/ubuntu/study_docker/Dockerfile: no such file or directory


대체 왜나는 거지??? 분명 해석을 해보면 Dockerfile 이 저 위치에 없다는 이야기 인데...

있는데 분명히.... 뭐~~~가 잘못된거지???


헉!!! DockerFile........ 파일 이름이 Dockerfile 이 아닌 DockerFile 로 해놨다..

그래서 변경했더니 잘만 된다...


docker build

Estimated reading time: 21 minutes

Description

Build an image from a Dockerfile

(출처 : https://docs.docker.com/engine/reference/commandline/build/)


딱 저기에도 Dockerfile을 build한다고 써있다.



728x90
반응형
반응형

최근 백기선님이 하시는 방송을 보면서 Docker를 따라 해보려고 하니 내가 가지고 있는 Mac 에는 한계가 있었다. 설치를 하려고 하니 설치가 안 된다고 한다..

2010년 mid 맥북 프로인데.. 그래서 어쩔수 없이 Docker는 AWS에 올려놓은 Ubuntu 에 설치하고 따라하기 시작했다.

그런데 다시 문제가 발생했다. 내가 로컬에서 작성한 파일을 EC2 서버에 올려야 하는데 어떻게 해야 하는거지????

그래서 찾기 시작했다. 


일단 작동중인 Ubuntu 서버에 FTP 설정이 필요했다.


1. Ubuntu 서버에  vsftpd 를 설치한다. 


먼저 apt-get 을 업데이트 한다.

sudo apt-get update
cs

그리고 vsftpd를 설치한다. vsftpd(Very Secured FTPD) : 우분투에서 기본적으로 제공되는 FTP 서버이다.

sudo apt-get install vsftpd
cs

설치가 완료되면 /etc/vsftpd.conf 파일에서 몇가지 설정을 변경해줘야 한다.


아래 설정에 대한 주석 해제

chroot_list_enable=YES

chroot_list_file=/etc/vsftpd.chroot_list


위에 설정에서 chroot_list_file에 지정되어있는 파일을 만들어서 root를 추가해준다.

sudo vi /etc/vsftpd.chroot_list
cs

root 추가

sudo systemctl restart vsftpd
cs

그리고 vsftpd 를 재시작 해줘야 한다. 이렇게 하면 일단 Ubuntu 서버는 준비가 됐다.


2. 보안 그룹에 20 번 포트 등록

이제 FTP 서버에 접속할 수 있도록 보안그룹에 설정을 해줘야 한다.



이렇게 하면 끝


3. FileZilla로 접속해보자!!



이렇게 하면 접속이 완료된다.!!!

 Docker 한번 해보려다가 많은것을 배운다..


728x90
반응형
반응형

https://spring.io/guides/tutorials/spring-boot-oauth2/


위 사이트에 가면 Spring boot 를 이용해서 Oauth를 이용해서 Login 을 할 수 있는 샘플을 만들어볼 수 있다. 그래서 나도 해봤는데.. 그게 삽질의 시작이었다...

Tutorial 자체는 그렇게 어렵지 않게 따라 할 수 있다. 따라하기가 어렵다면 Git에서 소스를 내려 받아서 해볼 수도 있다.


이제 이 Tutorial 을 진행하기 위해서 Facebook Developer 사이트에서 앱을 등록을 해야 한다. 그래야 Client Id 하고 Client  Secret을 받을 수 있다.


https://developers.facebook.com


위 사이트에 들어 가면 본인의 Facebook 계정으로 앱을 등록할 수 있다.


위 화면에서와 같이 앱ID 와 앱 시크릿 코드 를 받을 수 있는데 이게 바로 Client Id 와 Client Secret으로 사용된다.

그리고 redirect URI를 등록하면 준비는 끝난다. (끝난건줄 알았다...)


이제 샘플을 실행 시켜봤다.


로그인 까지 멀쩡하게 됐는데 error가 딱 나온다.


아니 왜????? 대체 왜 에러가 나오는 거지??? 분명 할것 다 한것 같은데..



로그를 보니 분명 access_token 까지는 잘 가져 왔다. 그런데 https://graph.facebook.com/me 라는 url을 호출 할때 400 error 가 났다. Http request 400 Error 는 그냥 Bad Request(요청이 잘못됐다)라는 의미는 아니다.


400(잘못된 요청): 서버가 요청의 구문을 인식하지 못했다

(출처 : https://ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C#4xx_(%EC%9A%94%EC%B2%AD_%EC%98%A4%EB%A5%98)


한마디로 요청을 하는 syntax 가 뭔가 잘못됐다는 의미이다.


똑같은 요청을 Postman 으로 보내봤다.


리턴된 메세지에 appsecret_proof argument가 있어야 된다고 나온다.. 이게 뭐지??? client_secret 말고 뭐가 또있나???

appsecret_proof로 그래프 API 호출 인증

그래프 API는 클라이언트 또는 클라이언트를 대신하여 서버에서 호출할 수 있습니다. 서버의 호출은 appsecret_proof라고 하는 매개변수를 추가하여 앱의 보안을 개선할 수 있습니다.

액세스 토큰은 이동 가능합니다. 클라이언트에서 Facebook의 SDK에 의해 생성된 액세스 토큰을 취하여 서버에 보낸 다음, 해당 서버에서 클라이언트를 대신하여 호출할 수 있습니다. 사용자 컴퓨터의 악성 소프트웨어 또는 메시지 가로채기(man in the middle) 공격에서 액세스 토큰을 훔칠 수도 있습니다. 그런 다음 클라이언트나 서버가 아닌 완전히 다른 시스템에서 이 액세스 토큰을 사용하여 스팸을 생성하고 데이터를 훔칠 수 있습니다.

서버의 모든 API 호출에 appsecret_proof 매개변수를 추가하고 모든 호출에 대해 인증서를 요청하도록 설정하여 이를 방지할 수 있습니다. 이렇게 하면 악의적인 개발자가 자신의 서버에서 다른 개발자의 액세스 토큰으로 API를 호출할 수 없게 됩니다. Facebook PHP SDK를 사용하고 있다면 appsecret_proof 매개변수는 자동으로 추가되어 있습니다.

(출처 : https://developers.facebook.com/docs/graph-api/securing-requests?locale=ko_KR)


그런 이유로 access_token 이외에 appsecret_proof를 같이 보내야 한다는 거다.



그런데 이런 설명도 있다. 그래서 저기 보이는  Require App Secret(앱시크릿 코드요청) 에 대한 설정을 안하면 appsecret_proof를 추가하지 않아도 된다는 이야기이다. 그래서 저 설정을 No로 설정하면 된다.


이렇게 해서 Facebook 로그인에 대한 샘플이 제대로 작동하는 것을 확인 할 수 있었다. 

간단하게 끝날줄 알았느데 설정 하나 때문에 온갖 고생을 했다. 


728x90
반응형
반응형

ClassPathResource resource = new ClassPathResource(resourcePath);

File file = resource.getFile();

 

위와 같이 코드를 작성해서 로컬에서 실행했을 때에 아무 문제 없이 동작을 하던 것이 war File 배포한 후에 동작을 시켰을 때에 에러가 발생을 했다.

 

class path resource [img/header.jpg] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/xxxxadmin/lib/xxxxxxxxx.war!/WEB-INF/classes!/img/header.jpg

java.io.FileNotFoundException: class path resource [img/header.jpg] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/xxxxadmin/lib/xxxxxxxxx.war!/WEB-INF/classes!/img/header.jpg

at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:215)

at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)

 

분명히 존재하는데 찾지를 못하는 거지???

 

https://sonegy.wordpress.com/2015/07/23/spring-boot-executable-jar%EC%97%90%EC%84%9C-file-resource-%EC%B2%98%EB%A6%AC/

 

문제는 소스코드에 있는 resource.getFile() 메소드 때문이었다.

 

war 파일이나 IDE로 application run as로 실행하였다면 실제 resource 파일인 file:// 프로토콜을 쓰기 때문에 File객체를 생성해 줄 수 있지만, executable jar로 실행 했다면 FileNotFoundException이 발생 하게 됩니다.


그래서 아래와 같이 변경해주면 해결이 된다.


InputStream inputStream = classPathResource.getInputStream();

File tempImage = File.createTempFile("temp", ".jpg");

try {

    FileUtils.copyInputStreamToFile(inputStream, tempImage );

} finally {

    IOUtils.closeQuietly(inputStream);

}


728x90
반응형
반응형

테이블을 설계할때 항상 빠지지 않고 들어있는 컬럼이 있다. 바로 생성일자, 수정일자, 생성자, 수정자 컬럼이다.

거의 모든 테이블에 디폴트로 들어있는 컬럼이고 상당히 중요한(?) 정보이다. 그렇다 보니 도메인에도 항상 똑같은 컬럼이 존재하게 된다. 그런데 spring-data-jpa에 재미난 기능이 있다. 바로 생성일자, 수정일자, 생성자, 수정자 컬럼에 값을 자동으로 넣어주는 기능이다. 


Audit : 감시하다.


바로 spring-data-jpa 에서는 audit 기능을 제공하고 있다. 방법은 여러가지가 있다. 각 도메인에 @PrePersist, @PreUpdate 등을 붙여서 할수도 있다. 그런데 이게 단점이 각각의 도메인에 같은 컬럼이 정의되어있다는 것은 변하지 않는다는 것이다. 모든 도메인에 똑같이 createDate, modifyDate, createUser, modifyUser가 있어야 한다. 그래서 이런 부분을 피하기 위해 다른 방법을 택했다.


1
2
3
4
5
@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
public class AuditableDomain extends AbstractAuditable<Account, Long> {
    private static final long serialVersionUID = 1L;
}
cs


위 소스에 보면 @EntityListenersAuditingEntityListener를 넣어줬다. 그리고 지금 구현한 Class는  AbstractAuditable을 상속받고 있다. AbstractAuditable 클래스의 Account는 실제 User 정보를 담고 있는 Domain 이고 Long은 그 도메인의 Key 값을 의미한다. 

AbstractAuditable 클래스의 필드를 보면 다음과 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@MappedSuperclass
public abstract class AbstractAuditable<U, PK extends Serializable> extends AbstractPersistable<PK> implements
        Auditable<U, PK> {
 
    private static final long serialVersionUID = 141481953116476081L;
 
    @ManyToOne
    private U createdBy;
 
    @Temporal(TemporalType.TIMESTAMP)
    private Date createdDate;
 
    @ManyToOne
    private U lastModifiedBy;
 
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastModifiedDate;
 
    .. 중략
}
cs


이렇게 createBy, createDate, lastModifedBy, lastModifiedDate 필드를 가지고 있기 때문에 더이상 하위 Domain 에서는 이 필드들을 정의할 필요가 없어진다. 그리고 다시 AbstractAuditable  클래스는 AbstractPersistable을 상속받고 있다. 보면 PK 를 정의하고 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@MappedSuperclass
public abstract class AbstractPersistable<PK extends Serializable> implements Persistable<PK> {
 
    private static final long serialVersionUID = -5554308939380869754L;
 
    @Id @GeneratedValue private PK id;
 
    /*
     * (non-Javadoc)
     * @see org.springframework.data.domain.Persistable#getId()
     */
    public PK getId() {
        return id;
    }
 
    /**
     * Sets the id of the entity.
     * 
     * @param id the id to set
     */
    protected void setId(final PK id) {
        this.id = id;
    }
 
    .. 중략
}
cs


결론적으로 이 AbstractPersistable 클래스에서 PK 를 정의하고 있기 때문에 각각의 도메인들은 더이상 id를 정의할 필요가 없어진다. 

그런데 내가 이걸 테스트 하다보니 문제가 생겼다. 

사용자 정보를 담은 도메인인 Account 에는 id를 String 으로 loginId 라는 이름으로 넣어놨다. AbstractPersistable  클래스에서 정의하는 ID 랑 다르기 때문에 @AttributeOverride  를 써서 재정의를 해보았는데 이상한 현상이 발생했다. DB 테이블에 loginId 말고 id라는 컬럼이 따로 생기고 각각의 필드들도 id랑 loginId랑 2개씩 생기는 현상이 발견됐다. createById, createByLoginId 이런 형태로 2개씩 생겼다. 

그리고 두번째로 ID를 set을 할 수가 없다. 그렇다 보니 ID를 정의할 수 없어서 결국 domain의 Id는 @generateValue 가 가능한 Long 형태로 변경했다. 혹시라도 다시 방법을 찾게 된다면 다시 포스팅을 하겠다..


1
2
3
4
5
6
7
8
9
@EnableJpaAuditing
@Configuration
public class AuditingConfig {
 
    @Bean
    CustomAuditorAware auditorAware() {
        return new CustomAuditorAware();
    }
}
cs

두번째로 해야할 일은 Config 등록이다. @EnableJpaAuditing을 선언해주고 JavaConfig로 등록해준다. 그런데 이때에 createDate, modifyDate는 상관이 없는데 createBy, modifiedBy 같은 경우는 대상이 필요하기 때문에 재정의가 필요하다. 


1
2
3
4
5
6
7
8
9
10
public class CustomAuditorAware implements AuditorAware<Account>{
 
    @Override
    public Account getCurrentAuditor() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (null == authentication || !authentication.isAuthenticated()) {
            return null;
        }
        return (Account) authentication.getPrincipal(); }
}
cs


그래서 이렇게 getCurrentAuditor method를 재정의 해준다. 지금 작성하고 있는 프로젝트는 spring security 가 적용된 프로젝트이기 때문에 SecurityConextHolder에서 정보를 가져와서 보여준다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
(SpringRunner.class)
@SpringBootTest(classes = StudySpringApplication.class)
public class AccountServiceTest {
 
    private static Logger logger = LoggerFactory.getLogger(AccountServiceTest.class);
 
 
    @MockBean
    private CustomAuditorAware customAuditorAware;
 
    @Autowired
    private AccountService accountService;
 
    @Autowired
    private RoleService roleService;
 
    @Before
    public void setup(){
        Account sessionAccount = accountService.get("admin");
        Mockito.when(customAuditorAware.getCurrentAuditor()).thenReturn(sessionAccount);
    }
 
    @Test
    public void createAccount(){
        Account newAccount = new Account();
        newAccount.setLoginId("admin1");
        newAccount.setUsername("admin user");
        newAccount.setPassword("admin1!");
        newAccount.setEmail("admin@spring.com");
        newAccount.setEnable(true);
 
        accountService.create(newAccount, null);
 
        Account account = accountService.get("admin1");
        Assert.assertEquals("admin1", account.getLoginId());
        Assert.assertNotNull(account.getCreatedBy());
    }
}
cs

결과를 확인해보기 위해 Test 코드를 돌려보면 잘 적용이 되는것을 확인 할수 있다.

728x90
반응형
반응형

기존 소스에 동시 로그인을 제어하기 위해서 Maxsession 설정을 넣어보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class ResourceSecurityConfiguration extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/""/login/**","/browser/**""/error/**").permitAll()
                .antMatchers("/private/**").authenticated()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .successHandler(new LoginSuccessHandler())
                .failureHandler(new LoginFailureHandler())
                .and()
                .logout().permitAll()
                .and()
                .sessionManagement()
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true);
    }
}
cs


위 설정주에 보면 sessionManagement가 추가되어있다. 그리고 maximunSessions가 1로 설정되어있고 maxSessionPreventsLogin 이 true 로 설정되어있다. 

maximunSessions : Session 허용 개수

maxSessionPreventsLogin : true 일 경우 기존에 동일한 사용자가 로그인한 경우에는 login 이 안된다. false 일경우는 로그인이 되고 기존 접속된 사용자는 Session이 종료된다. false 가 기본이다.

이렇게 설정하고 나서 동시로그인을 해보았다.



처음 로그인 한 경우에는 잘 된다.



두번째 동일한 계정으로 접속을 할 경우에는 이런 메세지가 나온다. 

(물론 이 화면은 내가 LoginFailureHandler에서 페이지를 설정해놓았기 때문에 저런 화면이 나온다.)



실제로 Log를 보면 org.springframework.security.web.authentication.session.SessionAuthenticationException: Maximum sessions of 1 for this principal exceeded 라는 Exception 이 발생한 것을 볼수 있다.




실제로 어디에서 Exception이 발생하는지 궁금해서 Debug를 찍어보았다. 여러면 돌려보면서 확인 한 결과 AbstractAuthenticationProcessingFilter 에서 발생을 했다. 이 Filter에 보면 sessionStrategy.onAuthentication 이라는 메소드를 호출한다. 이 메소드를 따라가 보면 CompositeSessionAuthenticationStrategy 클래스에서 다시 ConcurrentSessionControlAuthenticationStrategy 클래스로 다시 Delegating 된다. ConcurrentSessionControlAuthenticationStrategy 의 onAuthentication 메소드는 아래와 같이 구현되어있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public void onAuthentication(Authentication authentication,
            HttpServletRequest request, HttpServletResponse response) {
 
        final List<SessionInformation> sessions = sessionRegistry.getAllSessions(
                authentication.getPrincipal(), false);
 
        int sessionCount = sessions.size();
        int allowedSessions = getMaximumSessionsForThisUser(authentication);
 
        if (sessionCount < allowedSessions) {
            // They haven't got too many login sessions running at present
            return;
        }
 
        if (allowedSessions == -1) {
            // We permit unlimited logins
            return;
        }
 
        if (sessionCount == allowedSessions) {
            HttpSession session = request.getSession(false);
 
            if (session != null) {
                // Only permit it though if this request is associated with one of the
                // already registered sessions
                for (SessionInformation si : sessions) {
                    if (si.getSessionId().equals(session.getId())) {
                        return;
                    }
                }
            }
            // If the session is null, a new one will be created by the parent class,
            // exceeding the allowed number
        }
 
        allowableSessionsExceeded(sessions, allowedSessions, sessionRegistry);
}
cs


4번 라인 : sessionRegistry 에서 현재 인증된 Principal과 동일한 session을 모두 가져오게 된다.  

7번 라인 : 먼저 로그인했던 session 이 있기 때문에 sessionCount 는 1이 된다.

8번라인 : allowedSessions 는 sessionManagement의 maxSession이 1로 설정했기 때문에 1이다. 


그래서 20번 라인의 if문을 통과해서 for문을 수행하게 되는데 sessionId가 다르기 때문에 그냥 빠져나오고 36 라인의 allowableSessionsExceeded가 실행된다.


allowableSessionsExceeded 메소드 안에는 아래와 같은 구문이 있는데 결론적으로 아래 조건문이 만족하게 되고 Exception을 throw 하게 된다.


1
2
3
4
5
6
if (exceptionIfMaximumExceeded || (sessions == null)) {
    throw new SessionAuthenticationException(messages.getMessage(
            "ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
            new Object[] { Integer.valueOf(allowableSessions) },
            "Maximum sessions of {0} for this principal exceeded"));
}
cs



728x90
반응형
반응형

spring boot 와 연동하면서 삽질을 많이했는데 일단 기록은 해놔야 할것 같아서 메모를 한다.

MacOs에서 RabbitMQ 를 설치하는것은 참~~ 간단하다.


https://www.rabbitmq.com/install-standalone-mac.html


Rabbitmq 홈페이지에 install 가이드를 보면 따라하기 쉽게 되어있다. 설치를 위해서는 brew를 설치 해야한다.


https://brew.sh/index_ko.html


brew 홈페이지에 가보면 설치 방법이 나와있다. 홈페이지에 나온대로


1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
cs


이렇게 실행을 하면 brew가 설치가 된다. brew는 apt-get  같은 패키지 관리/설치를 해주는 툴이라고 이해하면 될것 같다.


그리고 나서


1
brew install rabbitmq
cs


이렇게 하면 설치는 완료된다.  설치 위치는 /usr/local/sbin 위치에 설치가 된다.

저 디렉토리로 이동후에 ./rabbitmq-server 를 실행하면 정상적으로 작동한다.



그리고 나서 http://localhost:15672/ 로 접속을 하면 된다. 처음 계정은 guest/guest 로 접속을 하면 된다.


guest 로만 접속을 하면 이상하니깐 계정을 하나 더 만들었다.


1
2
./rabbitmqctl add_user rabbitmq rabbitmq1!
./rabbitmqctl set_user_tags rabbitmq administrator
cs


첫번째 명령어는 새로운 계정을 생성하는 명령어이고 두번째 명령어는 생성한 계정에 permission을 할당하는 명령어 이다.

실제 화면에 들어가서 확인해 보면 아래와 같이 나온다.




728x90
반응형

+ Recent posts