loginForm.jsp
<form action="/auth/loginProc" method="post" >
<div class="form-group">
<label for="userName">UserName</label>
<input type="text" name="userName" class="form-control" placeholder="userName" id="userName">
</div>
<div class="form-group">
<label for="pwd">Password</label>
<input name="password" type="password" class="form-control" placeholder="password" id="password">
</div>
<button id="btn-login" class="btn btn-primary">로그인</button>
</form>
SecurityConfig.java
// 빈 등록 : 스프링 컨테이너에서 객체를 관리할 수 있게 하는 것
@Configuration // 빈등록 (IoC관리)
// Controller 에 들어가기전에 여기를 탄다
public class SecurityConfig {
@Bean
// 사용자의 자격증명을 검증하는 Bean 생성
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean // IoC가 된다!! 비밀번호를 해싱하는데 사용
public BCryptPasswordEncoder encodePWD() {
return new BCryptPasswordEncoder();
}
@Bean
SecurityFilterChain configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // csrf 토큰 비활성화 (테스트시 걸어두는게 좋음)
.authorizeRequests()
.antMatchers("/", "/auth/**", "/js/**", "/css/**", "/img/**") // "/auth/**", "/js/**", "/css/**", "/img/**" 에 대해서는
.permitAll() // 모두 허용
.anyRequest() // 다른 request에 대해서는
.authenticated() // 인증이 필요해
.and() // 그리고 인증이 필요한 곳에 대해서는 /auth/loginForm로 url 요청
.formLogin()
.loginPage("/auth/loginForm")
.loginProcessingUrl("/auth/loginProc") // 스프링 시큐리티가 해당 주소로 로그인을 가로채서 대신 로그인 해준다.
.defaultSuccessUrl("/") // 성공시 이 페이지로 이동
.usernameParameter("userName") // userDetails 에서는 파라미터명을 username인 것을 받아서 사용하는데 userName을 변경하겠다.
//.failureUrl("/fail") // 실패시 이 페이지로 이동
;
return http.build();
}
}
PrincipalDetail.java
SpringSecurity의 고유세션 저장소에서는 UserDetail 객체를 사용하기 때문에 기존에 만든 User 객체를 스프링에 맞게 타입을 바꿔줘야 한다.
기존의 User객체를 UserDetails 타입으로 만들어야 한다.
PrincipalDetail.java
// 스프링 시큐리티가 로그인 요청을 가로채서 로그인을 진행하고 완료가 되면 UserDetails 타입의 오브젝트를
// 스프링 시큐리티의 고유한 세션저장소에 저장을 해준다.
@Data
public class PrincipalDetail implements UserDetails{
private User user; // 컴포지션(객체를 품고있음)
public PrincipalDetail(User user) {
this.user = user;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
// 계정이 만료되지 않았는지 리턴한다 (true : 만료안됨)
@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;
}
}
PrincipalDetailService.java
@Service // Bean 등록
public class PrincipalDetailService implements UserDetailsService{
@Autowired
private UserRepository userRepository;
// 스프링이 로그인 요청을 가로챌 때, userName, password 변수 2개를 가로채는데
// password 부분처리는 알아서 함.
// userName이 DB에 있는지만 확인해주면 됨.
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User principal = userRepository.findByUserName(userName)
.orElseThrow(()->{
return new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다. : " + userName);
});
return new PrincipalDetail(principal); // 시큐리티의 세션에 유저 정보가 저장이 됨.
}
}
UserRepository.java
public interface UserRepository extends JpaRepository<User, Integer>{
// SELECT * FROM user Where userName = 1?; 비밀번호검증은 SpringSecurity가 한다.
// 우리는 userName이 실제로 DB에 있는지만 확인해서 넘겨주면 된다.
Optional<User> findByUserName(String userName);
}
로그인이 잘된다.
참고 유튜브 (메타코딩님 강의)
https://youtu.be/pHp2LGuukls?si=us6jd9Q2YZCJtPfx