spring boot security(라고 쓰고, spring security 라고 읽을 수도 있다) 에서 동시접속 세션수를 제한하는 기능이 있다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
}
}
위와 같이 @Configuration에 @EnableWebSecurity 어노테이션을 붙인 뒤, WebSecurityConfigurerAdapter를 상속받아 구현하면 된다.
override된 메서드 중 confiugre() 함수 내에 http security를 설정할 수 있는데
maximumSessions(1)을 주게 되면 해당 UserDetails에 해당하는 유저가 "1명" 만 접속이 가능하다.
이론상으론 그런데, "Maximum sessions of 1 for this principal exceeded" 와 같은 에러를 뿜고 작동이 잘 안된다.
스텝
- /login 페이지에서 A유저로 로그인을 진행
- /logout 페이지로 이동해서 로그아웃을 진행
- /login 페이지에서 다시 동일한 A유저로 로그인을 진행
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 최대 세션 수 설정
http.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.expiredUrl("/duplicated-login")
.sessionRegistry(sessionRegistry);
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}// Register HttpSessionEventPublisher
@Bean
public static ServletListenerRegistrationBean httpSessionEventPublisher() {
return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
}
}
위와 같이 SessionRegistry와 ServletListenerRegistrationBean을 Bean으로 만들면 된다.
SessionRegistry는 http의 sessionManagement에 직접 붙여주고, ServletListenerRegistrationBean는 그냥 Bean만 생성을 하면 된다.(static bean)
이렇게 설정을 하니 동일 유저로 로그인/로그아웃을 진행해도 문제없이 잘 된다.
+ 접속한 브라우저 말고 크롬 시크릿 페이지를 열어서 다른 세션으로 접속을 시도해도 한 User가 로그인이 되어 있으면 로그인이 되지 않게 된다.
또한 UserDetails를 구현한 구현체에 반드시 equals, hashcode 메서드를 override해서 구현을 해줘야 한다.
본인은 Lombok을 사용하기 때문에
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = "username")
public class User implements Principal, UserDetails {
@Id
@Column
@JsonIgnore
private String username;
@Column
@JsonIgnore
private String password;
@Column
@JsonIgnore
private String authorities;
@Column
private boolean locked;
@Column
@JsonIgnore
private String ip;
@Column
@JsonIgnore
private String userAgent;
@Column
@Temporal(TemporalType.TIMESTAMP)
private Date creation;
@Column
@Temporal(TemporalType.TIMESTAMP)
private Date lastAccess;
@Override
public String getName() {
return username;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
List authorityList = new ArrayList<>();
if (!StringUtils.isEmpty(authorities)) {
String[] splitedValue = authorities.split(",");
for (String auth : splitedValue) {
authorityList.add(new SimpleGrantedAuthority(auth));
}
}
return authorityList;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
와 같이 사용한다. (username 기준으로 equals, hashcode를 생성)
'Development > Java' 카테고리의 다른 글
[Spring boot] Websocket connect / disconnect 관련 (0) | 2018.06.08 |
---|---|
[Spring Boot] Resource load 관련 (0) | 2018.06.08 |
SpringBoot Jackson(ObjectMapper) Config (0) | 2017.07.07 |
[Spring] @Valid Collection Validation 관련 (3) | 2017.06.22 |
[Framework] Light4J(light-java) (0) | 2017.05.28 |