Dependency - Spring Security 추가

- 빈 프로젝트 생성

Dependencies:
- Spring Security

Copy:
- dependency - spring-boot-starter-security

Paste:
- dependency - spring-boot-starter-security

- Maven Load

프로젝트 실행
- Using generated security password: 3485b72e-14b1-42bd-a441-1c8ca10dba02

- Security 라이브러리에 따라서 login 절차가 생김

- Username: user
- Password: 3485b72e-14b1-42bd-a441-1c8ca10dba02

- 로그인 이후 메인 페이지 접근 가능

- 일단 로그아웃 처리
SecurityConfiguration 클래스 생성

- 로그인을 관리할 클래스 생성

Override (Ctrl + O):
- configure
메인 페이지에서 '회원정보' 링크 추가

- 메인 페이지에서 '회원정보' 링크 추가

- '회원정보' 링크가 추가된 모습

- 메인페이지의 링크를 맵핑
- 주소는 /member/info
'회원정보' 페이지 생성


- 접속 권한 획득 여부 확인 목적으로 특별한 기능이 없는 페이지
접속 권한 지정

로그인 하지 않은 상태에서 접근 가능한 페이지
- 메인페이지(/)
- 회원가입페이지(/member/register)
- 계정인증페이지(/email-auth)


- 로그인페이지(/member/login)에서 error가 발생한 경우
- 로그인에 실패했다는 에러 메세지 노출


@Bean
UserAuthenticationFailureHandler getFailureHandler() {
return new UserAuthenticationFailureHandler();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService)
.passwordEncoder(getPasswordEncoder());
super.configure(auth);

- UserDetailService를 상속받음

- Override: loadUserByUsername

Override를 구현
- memberRepository.findById(username)을 받아와서 회원 정보 존재 여부 확인
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Member> optionalMember = memberRepository.findById(username);
if (!optionalMember.isPresent()) {
throw new UsernameNotFoundException("회원 정보가 존재하지 않습니다.");
}
Member member = optionalMember.get();
if (!member.isEmailAuthYn()) {
throw new MemberNotEmailAuthException("계정 활성화 이후에 로그인 해주세요.");
}
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return new User(member.getUserId(), member.getPassword(), grantedAuthorities);
}

Bean으로 묶어줌
- getPasswordEncoder()로 받아와서 BCryptPasswordEncorder()로 보냄
@Bean
PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(memberService)
.passwordEncoder(getPasswordEncoder());
super.configure(auth);
}

- 로그인페이지 맵핑
로그인페이지 구현

form을 post방식으로 만들어줌
- 아이디, 비밀번호 입력칸과 로그인 버튼 생성
<form method="post">
<div>
<input type="text" name="username" placeholder="아이디(이메일)을 입력" required/>
</div>
<div>
<input type="password" name="password" placeholder="비밀번호 입력" required/>
</div>
<div>
<button typre="submit"> 로그인 </button>
</div>
</form>

- 생성된 로그인 페이지
로그인페이지 테스트

- 이메일, 비밀번호 입력 - 로그인 실패

- 일단 테스트를 위하여 http.csrf().disable()
http.csrf().disable();

- 이메일, 비밀번호 입력 - 로그인 실패

- Post 방식을 지원하지 않음

- Get과 Post방식을 모두 지원하는
- RequestMapping으로 변경
@RequestMapping("/member/login")
public String login() {
return "member/login";
}

- 로그인 실패 시 에러메시지를 노출할 부분 추가
<div th:text="${errorMessage}"></div>

- 로그인 실패한 경우 에러 메시지 노출
비밀번호 저장 방식 변경

- BCrypt.hashpw를 사용하여 암호화된 비밀번호로 저장
String encPassword = BCrypt.hashpw(parameter.getPassword(), BCrypt.gensalt());
String uuid = UUID.randomUUID().toString();
//builder pattern
Member member = Member.builder()
.userId(parameter.getUserId())
.userName(parameter.getUserName())
.phone(parameter.getPhone())
.password(encPassword)
.regDt(LocalDateTime.now())
.emailAuthYn(false)
.emailAuthKey(uuid)
.build();
memberRepository.save(member);

- 다시 회원가입 (비밀번호 '1111' 입력)

- 일단 회원가입은 성공

- DB에서도 password가 암호회된 모습을 확인할 수 있음
권한에 따른 페이지 접근 테스트

- 가입시킨 계정으로 로그인

- 메인 페이지에서 회원 정보 페이지 이동 : 성공
로그아웃 기능 구현

http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/member/logout"))
.logoutSuccessUrl("/")
.invalidateHttpSession(true);
로그아웃 기능 테스트

- 로그인 상태에서 로그아웃 링크 클릭
- 로그아웃 상태에서 회원정보 링크 클릭

- 로그인 페이지에 접근되나,
- 로그아웃 상태이므로 로그인 페이지 노출
계정 활성화 이전 회원정보 페이지 접근 제한

if (!member.isEmailAuthYn()) {
throw new MemberNotEmailAuthException("계정 활성화 이후에 로그인 해주세요.");
}

- 클래스가 없어서 발생하는 에러

- 클래스 생성

- 에러가 발생되지 않도록 RuntimeException을 상속받음

- 로그인 실패한 경우 에러메시지 노출 구현
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
String msg = "로그인에 실패하였습니다.";
if (exception instanceof InternalAuthenticationServiceException) {
msg = exception.getMessage();
}
setUseForward(true);
setDefaultFailureUrl("/member/login?error=true");
request.setAttribute("errorMessage", msg);
System.out.println("로그인에 실패하였습니다.");
계정 활성화 이전 회원정보 페이지 접근 테스트

- 계정 활성화 이전 회원 정보 페이지에서 로그인하면
- 계정 활성화를 선행하라는 에러 메시지 노출

'cs > java-spring-boot' 카테고리의 다른 글
| [Zero-base] 7-1. 관리자 로그인 (0) | 2022.02.22 |
|---|---|
| [Zero-base] 6-14. 비밀번호 초기화 요청 및 메일 링크를 통한 초기화 (0) | 2022.02.18 |
| [Zero-base] 6-12. 전송된 메일을 통한 계정 활성화 구현 (0) | 2022.02.16 |
| [Zero-base] 6-11. 회원가입 후 메일 전송 (0) | 2022.02.16 |
| [Zero-base] 6-10. JavaMailSender를 통한 메일 전송 설정 및 메일 전송 테스트 (Gmail) (0) | 2022.02.16 |