스프링 시큐리티 프레임워크는 스프링 서버에 필요한 인증 및 인가를 위해 많은 기능을 제공해 준다.
프레임워크 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
-build.gradle-
package com.sparta.week04.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
@Configuration
@EnableWebSecurity // 스프링 Security 지원을 가능하게 함
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure (WebSecurity web){
// h2-console 사용에 대한 허용 (CSRF, FrameOptions 무시)
web.ignoring().antMatchers("/h2-console/**");
}
@Bean
public BCryptPasswordEncoder encodePassword() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 회원 관리 처리 API (POST /user/**) 에 대해 CSRF 무시
http.csrf().ignoringAntMatchers("/user/**");
http.authorizeRequests()
// image 폴더를 login 없이 허용
.antMatchers("/images/**").permitAll()
// css 폴더를 login 없이 허용
.antMatchers("/css/**").permitAll()
//회원 관리 처리 API 전부를 login 없이 허용
.antMatchers("/user/**").permitAll()
// 어떤 요청이든 '인증'
.anyRequest().authenticated()
.and()
// 로그인 기능 허용
.formLogin()
//로그인 View 제공(GET/user/login)
.loginPage("/user/login") //로그인 할 때 longin.html 페이지로
// 로그인 처리 (POST/user/login)
.loginProcessingUrl("/user/login") //로그인 처리(보안검색대 가고 그런 과정들)
.defaultSuccessUrl("/") //로그인이 성공할 시 해당 url로 이동
.failureUrl("/user/login?error") //실패했을 때 url
.permitAll()//로그인에 관련된 기능에 대해 허용을 해줘라
.and()
// 로그아웃 기능 허용
.logout()
// 로그아웃 처리 URL
.logoutUrl("/user/logout")
.permitAll(); //로그아웃에 관련된 기능에 대해 허용을 해줘라
}
}
-WebSecurityConfig.java-
허용을 해주는 부분이 중요한 것 같다.
package com.sparta.week04.security;
import com.sparta.week04.domain.Users;
import com.sparta.week04.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService { //Spring Security에서 유저의 정보를 가져오는 인터페이스이다.
private final UserRepository userRepository;
@Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
//loadUserByUsername 는 UserDetailsService가 가지고 있는 함수
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("Can't find " + username));
//조회가 되면 UserDetail로 반환?
return new UserDetailsImpl(user);
}
}
-UserDetailsServiceImpl.java-
UserDetailsService라는 인터페이스를 implements 해준다.
UserDetailsService
이 친구는 loadUserByUsername() 이라는 함수를 가지고 있고, 필수적으로 선언을 해줘야 한다고 한다.
user의 정보를 찾으면 UserDetailsImpl
로 넘겨준다.
package com.sparta.week04.security;
import com.sparta.week04.domain.Users;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
public class UserDetailsImpl implements UserDetails { //Spring Security에서 사용자의 정보를 담는 인터페이스이다.?
private final Users user;
public UserDetailsImpl(Users user) { //userDatialsService에서 조회가 된 회원 정보의 객체
this.user = user;
}
public Users getUser() {
return user;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
//
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
}
-UserDetailsImpl.java-
public UserDetailsImpl(Users user) {
this.user = user;
}
조회가 된 회원 정보를 넣는다.
그 후 사용을 하려면
@Controller
public class HomeController {
@GetMapping("/")
public String home(Model model, @AuthenticationPrincipal UserDetailsImpl userDetails) {
model.addAttribute("username", userDetails.getUsername());
return "index";
}
}
-HomeController.java-
@AuthenticationPrincipal UserDetailsImpl userDetails 에서
userDetails
에 사용자의 정보가 담기고, index.html 이라는 곳에 username 에 로그인 된 사용자의 이름을 전달해준다.
흐름을 좀 잡기위해 크게크게 써보았다. 틀린 부분은 더 공부하면서 고칠 예정이다.
끗!
!!!!!!!!!!!!!
회원가입을 진행할 때 클라에서 POST로 받은 정보들을 controller
안에 있는 SignupRequestDto에 담아 서버로 넘어오는데, 그 정보들을 service
안에 있는 registerUser 함수에 넣어준다.
이 service 단에서 패스워드 암호화, 관리자/사용자 등 정해준 후 repo에 저장한다.