이번 포스트에서는 로그인 기능을 구현하겠습니다.
시작
스프링 시큐리티 dependency 를 적용시키고 브라우저를 통해 접속을 하면 아래와 같은 화면이 출력되었습니다.
이전에 만들었던 SecurityConfig 파일에서 설정을 해서 모든 url 을 인증없이 접속할 수 있도록 했으며,
다음으로 위의 로그인 페이지는 직접 만든 로그인 페이지로 바꾸도록 설정 파일을 작성하겠습니다.
package com.crud;
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.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().requestMatchers(
new AntPathRequestMatcher("/**")).permitAll()
.and()
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/");
return http.build();
}
}
formLogin() 과 loginPage("접속할 url ") 를 통해 로그인 페이지를 직접 만든 url 로 연결시킬 수 있으며,
로그인이 성공했을 경우 defaultSuccessUrl("로그인 성공했을 경우 이동할 url") 을 통해 url 를 설정할 수 있습니다.
로그인 폼
다음으로 로그인 폼인 login_form.html 을 작성합니다.
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
<form th:action="@{/user/login}" method="post">
<div th:if="${param.error}">
<div class="alert alert-danger">
아이디 또는 비밀번호를 확인해주세요.
</div>
</div>
<div class="mb-3">
<label for="username" class="form-label">아이디</label>
<input type="text" name="username" id="username" class="form-control">
</div>
<div class="mb-3">
<label for="password" class="form-label">비밀번호</label>
<input type="password" name="password" id="password" class="form-control">
</div>
<button type="submit" class="btn btn-primary">로그인</button>
</form>
</div>
</html>
네비게이션 메뉴에서 로그인페이지에 접속할 수 있도록 layout.html 의 로그인 <a> 태그에 url 을 연결합니다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
...
</head>
<body>
...
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/user/login">로그인</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/signup">회원가입</a>
</li>
</ul>
...
</body>
</html>
UserRepository
다음으로 User 를 username 을 통해 조회할 수 있는 메서드를 UserRepository 에 추가합니다.
package com.crud.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import com.crud.model.User;
public interface UserRepository extends JpaRepository<User, Long>{
Optional<User> findByusername(String username);
}
UserSecurityService
스프링 시큐리티 설정에 사용할 UserSecurityService 를 생성하고 작성합니다.
package com.crud.service;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class UserSecurityService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
return null;
}
}
UserSecurityService 는 스프링 시큐리티의 UserDetailsService 인터페이스를 상속받는 클래스로
loadUserByUsername 메서드를 구현해야 하므로 다음과 같이 작성합니다.
package com.crud.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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;
import com.crud.model.User;
import com.crud.model.UserRole;
import com.crud.repository.UserRepository;
@Service
public class UserSecurityService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> _user = userRepository.findByusername(username);
if(_user.isEmpty()) {
throw new UsernameNotFoundException("사용자를 찾을 수 없습니다.");
}
User user = _user.get();
List<GrantedAuthority> authorities = new ArrayList<>();
if("admin".equals(username)) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
}
loadUserByUsername 메서드는 UserRepository 의 findByusername 으로 user 를 조회하고
가입된 유저가 없을 경우 UsernameNotFoundException 을 발생합니다.
또한 아이디가 "admin" 인 경우 ADMIN 권한을 부여하고 그 외의 경우 USER 권한을 부여합니다.
SecurityConfig 추가
다음으로 스프링 시큐리티 설정파일인 SecurityConfig 에 AuthenticationManager 빈을 추가합니다.
package com.crud;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
...
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
결과
Springboot 를 실행시키고 로그인을 해보면 아래와 같은 화면이 나옵니다.
이것은 PasswordEncoder 가 연결이 되어있지 않아서 발생하는 문제인데
앞의 회원가입 단계에서 UserService 클래스에 BCryptPasswordEncoder 를 생성해서 사용했습니다.
이것을 스프링 시큐리티 설정파일에 빈으로 등록해서 사용하는것으로 수정하겠습니다.
package com.crud;
...
@Configuration
@EnableWebSecurity
public class SecurityConfig {
...
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
위와 같이 BCryptPasswordEncoder 를 빈으로 등록합니다.
회원가입 단계에서 만든 UserService 클래스 또한 빈으로 등록한 PasswordEncoder 를 사용하는것으로 수정합니다.
package com.crud.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import com.crud.model.User;
import com.crud.repository.UserRepository;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
public User create(String username, String email, String password) {
User user = new User();
user.setUsername(username);
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password));
userRepository.save(user);
return user;
}
}
로그인을 다시 해보면 제대로 접속이 되는것을 확인할 수 있습니다.
'Spring > Springboot 실습' 카테고리의 다른 글
게시판 만들기 - 글쓴이 표시(1) (0) | 2023.01.26 |
---|---|
게시판 만들기 - 로그아웃 (0) | 2023.01.25 |
게시판 만들기 - 회원가입(2) (0) | 2023.01.24 |
게시판 만들기 - 회원가입(1) (0) | 2023.01.24 |
게시판 만들기 - 페이징 처리(2) (0) | 2023.01.22 |