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기초
 - 그리디
 - 백준
 - Git
 - 1주차(1)
 - Workbench
 - 파이썬 알고리즘 인터뷰
 - 스택
 - 인텔리제이
 - python기본
 - DP
 - 최단거리
 - python기초
 - 참고X
 - 인스타
 - c언어 기본
 - python자료형
 - git오류
 - 4장
 - git 오류
 - 5장
 - #코린이 #코딩 #할 수 있다
 - 도커
 - 운체 1주차
 - c언어 제어문
 - 코딩테스트
 - 코테
 - 데베시 1주차
 - 자료구조
 - 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 |