스프링 시큐리티를 통해서 회원가입부터 시작해서 로그인 & 로그아웃을 진행할 것이다.
1. 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
기본적으로 시큐리티를 사용하기 위해서는 'org.springframework.boot:spring-boot-starter-security' 를 추가시켜야 하며, 필자 같은 경우에는 시큐리티 관련해서 Thymeleaf를 쓸 것이기 때문에 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'를 추가하였다.
2. 환경설정 추가
package com.example.login.config;
import com.example.member.service.MemberService;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private MemberService memberService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception
{
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 페이지 권한
.antMatchers("/**").permitAll()
// 로그인
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.permitAll()
.and()
// 로그아웃
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService).passwordEncoder(passwordEncoder());
}
}
로그인 & 로그아웃 구현하는데 있어서 첫번째 핵심 코드이다. 메소드 기준으로 해서 차근차근 설명할 것이다.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
이 메소드는 비밀번호 암호화할 때 쓰이는 메소드이다. 나중에 로그인을 할 때 비밀번호가 들어갈 텐데 인코딩 된 비밀번호를 비교해주는 역할을 해준다.
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/img/**", "/lib/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 페이지 권한
.antMatchers("/**").permitAll()
// 로그인
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.permitAll()
.and()
// 로그아웃
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
}
이 부분은 한꺼번에 설명을 할까 하다.
web.ignoring().antMatchers 부분은 자원에 대한 접근을 푼 것이고,
antMatchers.permitAll() 부분은 간단한 예제를 보여주는 것이기 떄문에 모든 경로에 대해 권한을 줬다. 이 설정을 안하게 되면 사이트 전체가 잠겨서, 비밀번호를 쳐야 하는 불편함이 있기 때문이다.
로그인 부분은
formLogin()
ㄴ loginPage - 로그인 페이지 경로 설정
ㄴ loginProcessingUrl - POST로 로그인 정보를 보낼 시 경로
ㄴ defaultSuccessUrl - 로그인이 성공할 시 경로
logout()
ㄴlogoutRequestMatcher - 로그아웃을 호출할 경우 해당 경로
ㄴlogoutSuccessUrl - 로그아웃이 성공했을 경우 해당 경로
ㄴinvalidateHttpSession - 로그아웃시 인증정보를 지우고 세션을 무효화 시키는 설정
3. 회원가입 저장 및 인증 세팅
package com.example.member.service;
import com.example.member.domain.Role;
import com.example.member.domain.entity.MemberEntity;
import com.example.member.domain.repository.MemberRepository;
import com.example.member.dto.MemberDto;
import lombok.AllArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@AllArgsConstructor
public class MemberService implements UserDetailsService {
private MemberRepository memberRepository;
@Transactional
public Long joinUser(MemberDto memberDto) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
memberDto.setPassword(passwordEncoder.encode(memberDto.getPassword()));
return memberRepository.save(memberDto.toEntity()).getNo();
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<MemberEntity> userEntityWrapper = memberRepository.findById(username);
MemberEntity userEntity = userEntityWrapper.get();
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Role.MEMBER.getValue()));
return new User(userEntity.getId(), userEntity.getPassword(), authorities);
}
}
로그인 및 회원가입에 있어서 2번째 핵심코드이다. 이번에도 각 메소드 기준으로 설명을 하겠다.
@Transactional
public Long joinUser(MemberDto memberDto) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
memberDto.setPassword(passwordEncoder.encode(memberDto.getPassword()));
return memberRepository.save(memberDto.toEntity()).getNo();
}
해당 메소드는 회원가입을 할 때 쓰는 메소드이다. 회원가입 할 때 화면에서 비밀번호를 입력할 때, 단순히 문자 그대로를 저장하지 않고 암호화를 하고나서 저장을 한다. 이 과정이 없으면 나중에 로그인을 할 때 복호화 과정을 거치는데, 로그인을 할 수 없는 문제가 발생한다.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<MemberEntity> userEntityWrapper = memberRepository.findById(username);
MemberEntity userEntity = userEntityWrapper.get();
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(Role.MEMBER.getValue()));
return new User(userEntity.getId(), userEntity.getPassword(), authorities);
}
로그인을 하는 데 있어 DB에 접근해서 사용자 정보를 가져오는 메소드이다. 이 때 스프링 시큐리티에서 제공하는 User 클래스가 있다. UserDetails 인터페이스를 직접 구현하거나 User 클래스를 확장해서 사용하면 된다.
'spring' 카테고리의 다른 글
Junit 개념 및 활용 (0) | 2021.07.24 |
---|---|
Springboot aop 기본개념 및 예제 (0) | 2021.07.17 |
[Springboot] Logback 사용하기 (0) | 2021.03.21 |
[Gradle] build.gradle 기본 구조(dependencies) (0) | 2021.03.15 |
Spring aop와 로그 예제 (0) | 2018.04.25 |