Spring Security 사용하기 · 5화

[SPRING] Spring Security 사용하기 #5

커비3분 분량

source: https://github.com/ziponia/spring-security-example

security 에 대해서 좀 더 알아보자.

로그인 세션 제한하기

시큐리티에서 로그인 세션 제한을 둘 수 있다.

만약, 사용자가 서울에서 로그인 한 후, 인천에서 로그인하면 서울에서 되어있는 로그인 세션이 풀리는것이다.

코드를 보자

WebSecurityConfig.java

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

// ...

@Override
protected void configure(HttpSecurity http) throws Exception {
// other....
http
.authorizeRequests()
.antMatchers("/private/**").hasAnyRole("USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().permitAll()
.and()
.sessionManagement()
.maximumSessions(1) // 최대 접속수를 1개로 제한한다.
.expiredUrl("/login?expire=true") // 세션이 제한 되었을 경우 리다이렉트 할 URL
// other....
;
}

// ...
}

login.html

<fieldset>
<div th:if="${#request.getParameter('expire')}">
새로운 곳에서 로그인 시도되었습니다.
</div>
<legend>로그인</legend>
<form th:action="@{/login}" th:method="post">
<input type="text" name="username" placeholder="아이디를 입력 해 주세요." />
<br />
<input type="password" name="password" placeholder="비밀번호를 입력 해 주세요." />
<br />
<button type="submit">로그인</button>
</form>
<div>
<a th:href="@{/login/facebook}">페이스북 로그인</a>
</div>
<div>
<a th:href="@{/login/github}">깃허브 로그인</a>
</div>
<div>
<a th:href="@{/login/kakao}">카카오 로그인</a>
</div>
</fieldset>

이제 창 두개를 열고, 각각 로그인 해보자

one session login

한쪽이 로그인 하면, 다른 한쪽이 로그인 해제 되는것을 볼 수 있다.

remember-me , 나를 기억해줘 시큐리티!

로그인 기억하기 기능을 알아보자. 이것도 매우 단순하다.

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

// ...

@Override
protected void configure(HttpSecurity http) throws Exception {
// other....
http
.authorizeRequests()
.antMatchers("/private/**").hasAnyRole("USER")
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().permitAll()
.and()
.sessionManagement()
.maximumSessions(1)
.expiredUrl("/login?expire=true")
.and()
.and()
.rememberMe()
.alwaysRemember(false) // 항상 로그인을 기억한다.
.rememberMeParameter("remember-me") // 기본 파라메터
// other....
;
}

// ...
}

.rememberMeParameter() 에서는 form 에서 전달 받을, name 을 넣어주면 된다. default 는 remember-me 이다.

<fieldset>
<div th:if="${#request.getParameter('expire')}">
새로운 곳에서 로그인 시도되었습니다.
</div>
<legend>로그인</legend>
<form th:action="@{/login}" th:method="post">
<input type="text" name="username" placeholder="아이디를 입력 해 주세요." />
<br />
<input type="password" name="password" placeholder="비밀번호를 입력 해 주세요." />
<br />
<button type="submit">로그인</button>

<!-- 자동 로그인 설정 -->
<label for="remember-me">
자동 로그인
<input type="checkbox" name="remember-me" id="remember-me" />
</label>
</form>
<div>
<a th:href="@{/login/facebook}">페이스북 로그인</a>
</div>
<div>
<a th:href="@{/login/github}">깃허브 로그인</a>
</div>
<div>
<a th:href="@{/login/kakao}">카카오 로그인</a>
</div>
</fieldset>

이제 체크박스에 체크 후 로그인 하면 remember-me 라는 쿠키가 생성 되는것을 볼 수 있다.

remember me cookie

브라우저를 닫고 다시 접속 후 개인페이지로 다시 들어 가 보자.

물론, 만료시간, remember-me 기록 방식 등도 지정 할 수 있다.

최종 접속일 기록하기

security 의 AuthenticationSuccessHandler 를 이용하여 최종 로그인을 기록 해보자.

먼저 UserEntity 에 필드를 만들어 주자.

public class UserEntity {

@Id
@GeneratedValue
private Integer idx;

private String username;

private String password;

@Enumerated(EnumType.STRING)
private SocialProvider sns;

private Date lastLogin; // 최종 로그인 기록
}

그리고 userRepository 에서 username 으로 entity 를 찾을 수 있는 쿼리를 만들자.

@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> {

UserEntity findByUsernameAndSns(String id, SocialProvider sns);
UserEntity findByUsername(String username); // here
}

다음으로 LoginSuccessHandler class 를 만들고, SimpleUrlAuthenticationHandler 를 상속 받아 쿼리를 끼워 넣자.

package ziponia.spring.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;

@Component
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

@Autowired
private UserRepository userRepository;

@Override
@Transactional
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
UserEntity userEntity = userRepository.findByUsername(authentication.getName());
userEntity.setLastLogin(new Date());
userRepository.save(userEntity);
super.onAuthenticationSuccess(request, response, authentication);
}
}

마지막으로 WebSecurityConfig 에서 handler 를 주입 해 주면 된다.

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

// ...
@Autowired
private LoginSuccessHandler loginSuccessHandler;

// ...

@Override
protected void configure(HttpSecurity http) throws Exception {
http

// ...

.and()
.formLogin()
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password")
.failureUrl("/login?error=true")
.successHandler(loginSuccessHandler) // <-- 이곳이다.

// ...
;
}
// ...
}

이제 로그인 후 h2 콘솔을 열어보면 last_login 필드에 기록 되는걸 볼 수 있다.

마지막 로그인 기록

오늘은 여기까지

다음엔... 우리가 직접 Social Provider 가 되어 oauth2 인증 시스템을 만들어 보자

To Be continue...

댓글

0

첫 댓글을 남겨보세요

로그인하면 댓글을 남길 수 있어요