환경
spring boot 3.1.5
JDK 17
이번 프로젝트에서 인증 방식은 session으로 해보기!
이전 프로젝트에서는 Jwt를 활용해 인증과 인가를 구축했다.
즉, 토큰 인증만을 진행해봤다. 그래서 이번에는 세션 인증을 해보려고 한다. 그래도 둘의 차이는 알고 이용해야겠다.
Redis?
Redis는 다양한 방식으로 쓰일 수 있는 미들웨어라고 생각한다. 물론 데이터베이스, 캐시 , 메세지 브로커 등으로 쓰일 수 있다. 쓰기 나름이다!
레디스의 특징은
이런 대표적인 특징을 가지고 있다.
Redis를 사용해보려는 이유는
1. 처음 써보는 기술이기 때문에 해보고 싶음
2. 레디스의 특성 상 빠른 읽기와 쓰기 제공, 간편한 확장성,높은 가용성 때문
3. Spring security는 Authentication 인증 객체를 세션에 보관하여 사용하는 것이 기본값인데, 이는 분산 환경이 될시에 로그인 상태를 확인하는 기능에 장애를 유발할 수 있기 때문, 따라서 in memory 방식인 레디스를 이용하여 세션을 대체
단 Redis를 사용시 단점도 있다.
1. 레디스는 메모르 기반이기 때문에 서버가 재시작되면 데이터 손실될 수 있음
2. 레디스에 데이터 저장 시 암호화를 적용하지 않으면 세션ID 노출 될 수 있음
이러한 단점들을 생각하면서 개발을 진행해야한다.
Redis 세션 절차
절차
// redis 연결을 위한 라이브러리
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// 세션 데이터를 redis로 연결하는 라이브러리
implementation 'org.springframework.session:spring-session-data-redis'
Github에 올라갈 것이므로 환경 변수로 셋팅
@Configuration
@EnableRedisHttpSession // 레디스를 세션 저장소로 사용할 수 있게 해줌
public class RedisConfig {
@Value("${REDIS_HOST}")
private String host;
@Value("${REDIS_PORT}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory(){
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
}
@EnableRedisHttpSession 을 통해 세션 데이터를 Redis를 사용하여 관리하도록 하는 기능을 활성화
public class UserAuthenticationFilter extends OncePerRequestFilter{
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
User user = (User) request.getSession().getAttribute("user");
if(!isNull(user)) {
GrantedAuthority authority = new SimpleGrantedAuthority("USER"); // 사용자 권한
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, Collections.singleton(authority)); // 현재 사용자의 인증 정보
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request,response);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private static final String[] PERMIT_URL_ARRAY = {
"/swagger-resources/**","/v3/api-docs/**","/swagger-ui/**",
"/api/login","/api/signup"
};
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(PERMIT_URL_ARRAY).permitAll()
.anyRequest().authenticated())
.csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
.addFilterBefore(new UserAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
로그인 로직
@Transactional(readOnly = true)
public String login(Login login, HttpSession session) {
User user = userRepository.findByEmail(login.getEmail());
if(user == null || !user.isPasswordMatch(passwordEncoder,login.getPassword()))
throw new UserException(UserErrorCode.FAIL_TO_LOGIN);
session.setAttribute("user",user);
return session.getId();
}
session.setAttribute("user", user) 를 통해서
Redis에는
-> key 로 세션Id spring:session:sessions:{세션ID} 이런형식
-> value 로 "user"(user정보)
으로 저장 됨
테스트를 해보면


로그인한 세션Id가 Redis에도 저장 된 것을 볼 수 있다.
그리고 인가에 대한 부분도
로그인을 하고 로그아웃을 한 경우(인가됨)

로그인을 하지 않고 로그아웃을 한다면(인가 되지 않음)

이것만 셋팅해도 시간이 꽤 걸렸기에 다음에는 Redis 비밀번호 설정 및 세션 시간도 늘리면 될 것 같당