Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- git 오류
- 1주차(1)
- 그리디
- 인스타
- #코린이 #코딩 #할 수 있다
- 스택
- Workbench
- c언어 기본
- 최단거리
- 백준
- 도커
- git오류
- python자료형
- git기초
- 코테
- 4장
- python기초
- 파이썬 알고리즘 인터뷰
- 운체 1주차
- 인텔리제이
- Git
- 5장
- DP
- 참고X
- c언어
- 데베시 1주차
- 자료구조
- python기본
- c언어 제어문
- 코딩테스트
Archives
- Today
- Total
하루살이 개발자
[Instagram 클론코딩] 3. 로그인 본문
로그인 특징
- 로그인은 예외적으로 무조건 POST로 요청(GET으로 요청시 아이디, 비번이 로그로 남기 때문)
- 직접 Controller를 만들지 않고 로그인 시큐리티로 위임함
- 로그아웃은 시큐리티에서 제공하는 기능(/logout 으로 접근 시 세션 끊어줌)
[프론트]
signin 로그인
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photogram</title>
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css"
integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous" />
</head>
<body>
<div class="container">
<main class="loginMain">
<!--로그인섹션-->
<section class="login">
<!--로그인박스-->
<article class="login__form__container">
<!--로그인 폼-->
<div class="login__form">
<h1><img src="/images/logo.jpg" alt=""></h1>
<!--로그인 인풋-->
<form class="login__input" action="/auth/signin" method="POST">
<input type="text" name="username" placeholder="유저네임" required="required" />
<input type="password" name="password" placeholder="비밀번호" required="required" />
<button>로그인</button>
</form>
<!--로그인 인풋end-->
<!-- 또는 -->
<div class="login__horizon">
<div class="br"></div>
<div class="or">또는</div>
<div class="br"></div>
</div>
<!-- 또는end -->
<!-- Oauth 소셜로그인 -->
<div class="login__facebook">
<button onclick="javascript:location.href='/oauth2/authorization/facebook'">
<i class="fab fa-facebook-square"></i>
<span>Facebook으로 로그인</span>
</button>
</div>
<!-- Oauth 소셜로그인end -->
</div>
<!--계정이 없으신가요?-->
<div class="login__register">
<span>계정이 없으신가요?</span>
<a href="/auth/signup">가입하기</a>
</div>
<!--계정이 없으신가요?end-->
</article>
</section>
</main>
</div>
</body>
</html>
[백엔드]
UserRepository
username으로 찾기
package com.cos.photogramstart.domain.user;
import org.springframework.data.jpa.repository.JpaRepository;
// 저장소 필요
// 어노테이션이 없어도 JpaRepository를 상속하면 IoC 등록이 자동으로 된다.
public interface UserRepository extends JpaRepository<User, Integer>{ // <User,프라이마리 키 타입>
// JPA query method
User findByUsername(String username);
}
* 스프링 시큐리티 이용
PrincipalDetailsService
package com.cos.photogramstart.config.auth;
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.cos.photogramstart.domain.user.User;
import com.cos.photogramstart.domain.user.UserRepository;
import lombok.RequiredArgsConstructor;
// 스프링 시큐리티
@RequiredArgsConstructor
@Service // IoC에 등록(UserDetailsService를 새로 만든 PrincipalDetailsService로 대체하여 로그인 진행)
public class PrincipalDetailsService implements UserDetailsService{
private final UserRepository userRepository;
// 1. 패스워드는 알아서 체킹하니까(시큐리티가 확인해줌) 신경쓸 필요 없다.
// 2. 리턴이 잘되면 자동으로 UserDetails 타입을 세션으로 만든다.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// username이 존재하는지 확인
User userEntity = userRepository.findByUsername(username);
if(userEntity == null) { // username 못찾았으면
return null;
}else {
return new PrincipalDetails(userEntity); // UserDetails 타입으로 리턴하기
}
}
}
PrincipalDetails
package com.cos.photogramstart.config.auth;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import com.cos.photogramstart.domain.user.User;
import lombok.Data;
@Data
public class PrincipalDetails implements UserDetails, OAuth2User{
private static final long serialVersionUID = 1L;
private User user;
private Map<String, Object> attributes;
public PrincipalDetails(User user) {
this.user = user;
}
// 생성자 만들기
public PrincipalDetails(User user, Map<String, Object> attributes) {
this.user = user;
}
// 권한 : 한개가 아닐 수 있음. (3개 이상의 권한)
// 하나가 아니므로 Collection으로..
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collector = new ArrayList<>();
collector.add(() -> { return user.getRole();}); // 권한 넣어주기(람다식으로 쓰면 간단해짐)
return collector;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override // 계정 만료? -> false로 하지 말자(직접 서비스 할때 false로 사용하기)
public boolean isAccountNonExpired() {
return true;
}
@Override // 계정 막혔니?
public boolean isAccountNonLocked() {
return true;
}
@Override // 비밀번호 1년 만료?
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public Map<String, Object> getAttributes() {
return attributes; // {id:343434343, name:최주호, email:ssarmango@nate.com}
}
@Override
public String getName() {
// TODO Auto-generated method stub
return (String) attributes.get("name");
}
}
ImageController
package com.cos.photogramstart.web;
import java.util.List;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.domain.image.Image;
import com.cos.photogramstart.handler.ex.CustomValidationException;
import com.cos.photogramstart.service.ImageService;
import com.cos.photogramstart.web.dto.image.ImageUploadDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class ImageController {
private final ImageService imageService;
// 로그인 성공시 story 페이지로 이동(main 페이지)
// 주소 2개 매핑 가능 { , }
@GetMapping({"/", "/image/story"})
public String story() {
return "image/story";
}
@GetMapping("/image/upload")
public String upload() {
return "image/upload";
}
}
UserController
package com.cos.photogramstart.web;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.cos.photogramstart.config.auth.PrincipalDetails;
import com.cos.photogramstart.service.UserService;
import com.cos.photogramstart.web.dto.user.UserProfileDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Controller
public class UserController {
private final UserService userService;
/* 세션정보 찾기를 통해 회원 정보 변경하기!
/auth/login -> username이 존재하면? -> PrincipalDetails에서 세션 저장
-> autnentication에 접근(AuthenticationPrincipal 어노테이션 이용) -> 세션 접근!!
*/
// 회원 정보 변경
@GetMapping("/user/{id}/update")
public String updateForm(@PathVariable int id, @AuthenticationPrincipal PrincipalDetails principalDetails) {
// 1. 추천
System.out.println("세션 정보 : "+principalDetails.getUser());
// 2. 비효율
//Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//PrincipalDetails mPrincipalDetails = (PrincipalDetails) auth.getPrincipal();
//System.out.println("직접 찾은 세션 정보 : "+mPrincipalDetails.getUser());
return "user/update";
}
}
'Project > Instagram 클론코딩' 카테고리의 다른 글
[Instagram 클론코딩] 4. 구독 (0) | 2022.02.17 |
---|---|
[Instagram 클론코딩] 4. 회원정보 수정 (0) | 2022.02.16 |
[Instagram 클론코딩] 2. 회원가입 (0) | 2022.02.14 |
[Instagram 클론코딩] 0. 설계 (0) | 2022.02.14 |