일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 머신러닝
- 깊이우선탐색
- 캐싱
- 해시
- 지도학습
- Merge sort
- 파이썬 오류
- 코테
- 딥러닝
- bineary search
- BOJ
- HTTP
- 오버라이딩
- rest api
- 딕셔너리
- 파이썬
- 파이썬 알고리즘
- 강화학습
- 너비우선탐색
- 알고리즘
- 이진탐색
- 코딩테스트
- 비지도학습
- 백준
- 코딩
- 프로그래머스
- 자바
- post
- 스택과 힙
- 멱등
- Today
- Total
chae._.chae
스프링 시큐리티 로그인 - SecurityConfig 본문
🎈 의존성 추가
Spring Security를 이용하기 위해서는 pom.xml(혹은 build.gradle)에서 dependency를 추가해줘야 한다.
<!-- 시큐리티 태그 라이브러리 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
SecurityConfig.java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private PrincipalDetailService principalDetailService;
@Bean
public BCryptPasswordEncoder encodePWD(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // csrf 토큰 비활성화
.authorizeRequests()
.antMatchers("/auth/**") // 누구나 들어올 수 있음
.permitAll()
.anyRequest() // 이외의 다른 모든 요청은 authentication이 필요하다
.authenticated();
.and()
.formLogin() // antMatchers에 해당하지 않는 모든 요청은, /auth/loginForm으로 간다
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc")
.defaultSuccessUrl("/"); // 로그인 성공적으로 되면 이동할 주소
}
}
- @Configuration : 빈등록 : 스프링 컨테이너에서 객체를 관리할 수 있게 하는 것(IoC관리)
- @EnableWebSecurity : 시큐리티 필터 등록 = 스프링 시큐리티가 활성화되어 있는데, 그 설정을 해당 파일(SecurityConfig)에서 하겠다
- @EnableGlobalMethodSecurity : 특정 주소로 접근하면 권한 및 인증을 미리 체크하겠다는 의미(요청을 수행하기 전에 먼저 체크!)
- csrf().disable() : 요청시 csrf토큰이 없으면 시큐리티가 요청을 막는데(default), 이를 비활성화 해준다.
- /auth/** 로 이동하는 것 말고는 모두 인증이 필요하다.
- loginProcessingUrl("/auth/loginProc") : 스프링 시큐리티가 해당 주소로 요청이 들어오는 로그인을 가로채서 대신 로그인해준다. 해당 Url로 들어오는 모든 요청을 가로챈다.
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD()); // encodePWD를 알려준다
}
- 시큐리티가 대신 로그인을 해주는데, password가 어떤걸로 해쉬되어서 회원가입이 되었는지를 알아야 같은 해쉬방법으로 암호화해서 db에 있는 해쉬된 비밀번호와 비교해준다.
로그인 동작
1. 시큐리티가 계속 지켜보다가, 사용자가 로그인 요청(파라미터로)을 하면 해당 요청을 가로챈다.
2. username, password를 가로챈다. 이를 가로채 시큐리티가 내부적으로 로그인을 진행시킨다.
3. 로그인이 완료되면, 시큐리티 세션에 유저정보를 등록(IoC)한다. (우리가 원할때 꺼내 쓸 수 있음)
4. 시큐리티 세션에 User객체를 넣을 때, 넣을 수 있는 type이 정해져 있다. UserDetails 타입만 가능하다.
(User 객체 extends UserDetails)
=> 사용자가 요청한 username이나 password를 가로채서 로그인을 하는데, 이때 UserDetails를 갖고 있는 User객체가 필요하다.
🎈 비밀번호 해쉬화
SecurityConfig 파일 안에 아래의 비밀번호 해쉬코드를 넣어준다.
@Bean
public BCryptPasswordEncoder encodePWD(){
// String encPassword = new BCryptPasswordEncoder().encode("1234"); // 암호화해서 넣어준다
return new BCryptPasswordEncoder();
}
PrincipalDetail.java
public class PrincipalDetail implements UserDetails {
private User user; // 컴포지션(객체를 품고 있음)
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
// 계정이 만료되지 않았는지 리턴한다(true : 만료X)
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정이 잠겨있는지 리턴 (true: 잠기지 않음)
@Override
public boolean isAccountNonLocked() {
return true;
}
// 비밀번호가 만료되진 않았는지 (true: 만료안됨)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
// 계정이 활성화가 되어있는지 (true: 활성화)
@Override
public boolean isEnabled() {
return true;
}
// 계정이 가진 권한목록을 리턴
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collectors = new ArrayList<>();
collectors.add(() ->{ return "ROLE_" + user.getRole();}); // 람다식표현
return collectors;
}
}
- 스프링 시큐리티가 로그인 요청을 가로채서 로그인을 진행하고 완료하면 UserDetails 타입의 객체를
스프링 시큐리티의 고유한 세션저장소에 저장한다. (PrincipalDetails가 저장됨)
PrincipalDetailService.java
@Service // Bean 등록
public class PrincipalDetailService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User principal = userRepository.findByUsername(username)
.orElseThrow(() -> {
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다 : " + username );
});
return new PrincipalDetail(principal); // 시큐리티 세션에 user정보가 저장된다.(UserDetails타입으로)
}
}
- loadUserByUsername : 해당 username이 db에 있는지 확인해준다.
- 로그인 요청시 username, password의 정보가 넘어오지만, password부분은 스프링이 알아서 처리해주기에, db에 username이 있는지만 확인해주는 작업을 만들면 된다. (password가 맞는지는 처리해줌)
💡 로그인 요청시 순서
1. 로그인 요청이 들어오면 SecurityConfig에서의 /auth/loginProc가 이 요청을 가로챈다.
2. 가로챈 username, password정보를 PrincipalDetailService의 loadUserByUsername으로 던진다.
3. loadUserByUsername에서 db에 있는지 비교한다.
4. 값을 리턴해주기 전에, password비교를 해준다. SecurityConfig에서의 auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD());에서 사용자가 적은 password를 다시 암호화해서 db와 비교해준다.
5. 비교가 끝나면, 스프링시큐리티에 유저 정보가 PrincipalDetail로 감싸서 저장된다.
6. 로그인이 끝나면 defaultSuccessUrl("/")에 따라 이동한다.
@GetMapping({"", "/"}) // 아무것도 안적었을 때랑 /넣었을때
public String index(@Authentication PrincipalDetail principal) { // 컨트롤러에서 세션을 어떻게 찾는지?
System.out.println("로그인 사용자 아이디: " + principal.getUsername());
return "index";
}
- 로그인된 세션에 접근하는 방법 : @Authentication PrincipalDetail principal
'스프링 > 시큐리티' 카테고리의 다른 글
JWT 토큰 서버 구축하기 - (2) (0) | 2022.07.28 |
---|---|
JWT 토큰 서버 구축하기 - (1) (0) | 2022.07.28 |
시큐리티 - (3) 권한 및 구글 로그인 설정 (0) | 2022.07.02 |
시큐리티 - (2) 회원가입과 로그인 (0) | 2022.07.02 |
시큐리티 - (1) 시큐리티 초기 설정 (0) | 2022.06.30 |