728x90
이전에 작성한 포스팅은 단순히 책에 있는 내용을 정리한 것이었는데,
이번엔 직접 코드를 짜보면서 복습해보려고 한다.
1. 라이브러리 설치
implementation 'org.springframework.boot:spring-boot-starter-security'
2. IndexController 생성
- controller 패키지 생성 후 IndexController 생성
@GetMapping({"", "/"})
public String index() {
return "index";
}
- resources/template/index.html 생성
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>인덱스페이지</title>
</head>
<body>
<h1>인덱스페이지입니다.</h1>
</body>
</html>
3. user, admin 메소드 생성
- IndexController에 두 개의 메소드를 만들어 준다.
@GetMapping("/user")
@ResponseBody
public String user() {
return "user";
}
@GetMapping("/admin")
@ResponseBody
public String admin() {
return "admin";
}
4. 필요한 화면 생성
- IndexController에 메소드 추가
@GetMapping("/loginForm")
public String loginForm() {
return "loginForm";
}
@GetMapping("/joinForm")
public String joinForm() {
return "joinForm";
}
- resources/template/joinForm.html,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>회원가입 페이지</title>
</head>
<body>
<h1>회원가입 페이지</h1>
<hr>
<form action="/join" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<input type="text" name="email" placeholder="Email"><br>
<button>회원가입</button>
</form>
</body>
</html>
loginForm 생성
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>로그인 페이지</title>
</head>
<body>
<h1>로그인 페이지</h1>
<hr>
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<button>로그인</button>
</form>
<a href="/joinForm">회원가입을 아직 하지 않으셨나요?</a>
</body>
</html>
5. 스프링 시큐리티 설정
@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록된다.
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // secured 어노테이션 활성화, preAuthorize 어노테이션 활성화
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // 인증만 되면 접근 가능
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login") // /login 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 진행해준다.
.defaultSuccessUrl("/")
;
return http.build();
}
}
- 메소드의 설명은 기존 포스팅을 참고하면 좋을 것 같다.
https://devhooney.tistory.com/17?category=1052331
https://devhooney.tistory.com/19?category=1052331
6. Model, Repository 생성
- Jpa를 사용할 것이기 때문에 라이브러리를 설치한다.
- 편리하게 Lombok도 설치.
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compileOnly 'org.projectlombok:lombok'
- model 패키지 안에 User 클래스 생성
@Entity
@Getter
@Setter
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String email;
private String role; // ROLE_ADMIN, ROLE_USER
@CreationTimestamp
private Timestamp createDate;
}
- repository 패키지 안에 UserRepository 인터페이스 생성
// CRUD 함수를 JpaRepository가 들고 있음.
// @Repository라는 어노테이션이 없어도 IoC 된다.
// 이유는 JpaRepository를 상속했기 때문에.
public interface UserRepository extends JpaRepository<User, Integer> {
User findByUsername(String username);
}
- 회원 가입 시 비밀번호 암호화를 위해 설정 (SecurityConfig)
@Configuration
@EnableWebSecurity // 스프링 시큐리티 필터가 스프링 필터체인에 등록된다.
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) // secured 어노테이션 활성화, preAuthorize 어노테이션 활성화
public class SecurityConfig {
// 해당 메소드의 리턴되는 오브젝트를 IoC로 등록해준다.
@Bean
public BCryptPasswordEncoder encodePwd() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated() // 인증만 도면 접근 가능
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/loginForm")
.loginProcessingUrl("/login") // /login 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 진행해준다.
.defaultSuccessUrl("/")
;
return http.build();
}
}
- IndexController에 join 메소드 추가
@PostMapping("/join")
public String join(User user) {
user.setRole("ROLE_USER");
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user);
return "redirect:/loginForm";
}
7. 로그인
- auth 패키지에 PrincipalDetails 파일 생성
- UserDetails 인터페이스를 상속받아 메소드 오버라이드
- 기본 생성자 만들어주기
package com.cos.security1.auth;
// 시큐리티가 /login 주소 요청이 오면 낚아채서 로그인을 진행시킨다.
// 로그인을 진행이 완료가 되면 session에 만들어준다. (Security ContextHolder)
// 오브젝트 타입 => Authentication 타입 객체
// Authentication 안에 User 정보가 있어야 된다.
// User 오브젝트 타입 => UserDetails 타입 객체
// Security Session => Authentication => UserDetails(PrincipalDetails)
import com.cos.security1.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class PrincipalDetails implements UserDetails {
private User user; // 컴포지션
public PrincipalDetails(User user) {
this.user = user;
}
// 해당 User의 권한을 리턴하는 곳!
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collect = new ArrayList<>();
collect.add((GrantedAuthority) () -> user.getRole());
return collect;
}
@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() {
// 사이트에서 1년동안 회원이 로그인을 안했을 경우 휴면 계정으로 하기로 함.
// 현재 시간 - 로그인 시간 => 1년을 초과하면 return false;
return true;
}
}
- PrincipalDetailsService 생성
- /login 요청 시 실행된다.
@Service
public class PrincipalDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
// 시큐리티 session = Authentication = UserDetails
// 실행되면 ->
// 시큐리티 session(내부 Authentication(내부 UserDetails))
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userEntity = userRepository.findByUsername(username);
if (userEntity != null) {
return new PrincipalDetails(userEntity);
}
return null;
}
}
- 여기까지하면 로그인이 잘 된다.
728x90
'개발 > Java & Kotlin' 카테고리의 다른 글
[Spring] 스프링시큐리티 OAuth2.0 적용해보기(Facebook) (0) | 2022.09.01 |
---|---|
[Spring] 스프링시큐리티 OAuth2.0 적용해보기(Google) (0) | 2022.08.31 |
[JPA] LIKE 정리 (0) | 2022.08.24 |
[Java] 예외처리 (0) | 2022.08.23 |
[Spring] Message 적용해보기 (0) | 2022.08.21 |