20221103-70 Spring Boot(6)Security 회원가입,로그인 구현

공현지·2022년 11월 3일
0

spring

목록 보기
14/30
post-thumbnail

성능향상

  • @ManytoOne 디폴드 값은 EAGER
    끝이 One인것은 모두다 eager ⭐
    조회하는 순간 sql다 끌고와서 관련된 테이블 한꺼번에 가지고옴

  • @OnetoMany는 디폴드값 lAZY (신경x)
  • @ManytoOne(fetch=fetchType.lAZY)
    필요한 값만 조회하기위에서 lazy꼭 걸어주기⭐
  • 동시에 조회해야하는것만 EAGER로 걸어줌 .

Spring boot Security

▶Maven 방식 (실무에선 거의 사용x)

🔽 환경셋팅 해주기

SecurityController01

package com.oracle.oBootSecurity01.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class SecurityController01 {

	@GetMapping("/user")
	public  String user() {
		System.out.println("SecurityController01 user start . . . . . ");
		
		return "user";
	}
	
	@GetMapping("/manager")
	public  String manager() {
		System.out.println("SecurityController01 user start . . . . . ");
		
		return "manager";
	}
	
	@GetMapping("/admin")
	public  String admin() {
		System.out.println("SecurityController01 user start . . . . . ");
		
		return "admin";
	}
	
	
}

user / manager /admin . html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>manager 인증 성공</h1>
</body>
</html>

----------------------
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>admin 인증 성공</h1>
</body>
</html> 
--------------------------  
 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>user 인증 성공</h1>
</body>
</html> 

🔽실행시

SecurityConfig (암호화) 요청 가로채기

  package com.oracle.oBootSecurity01.configuration;

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;
//IoC 빈(bean)을 등록
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity //필터 체인 관리 시작 어노테이션
public class SecurityConfig {

			@Bean 
			        //해쉬암호화 
			public BCryptPasswordEncoder encoderPwd() {
				return new BCryptPasswordEncoder();
				
			}
			@Bean
	protected SecurityFilterChain securityFilterChain (HttpSecurity http) throws Exception {
				
				http.csrf().disable(); //비활성화
				http.authorizeHttpRequests()
						.anyRequest() 
						.permitAll();
				
				return http.build();
				
			}
			
}

  
  

🔽로그인페이지 안뜨고 바로 들어와짐

SecurityController01

@Autowired
           //암호화 
	private BCryptPasswordEncoder bCryptPasswordEncoder;	
	@GetMapping("/user")![](https://velog.velcdn.com/images/rhdguswlx/post/dc16337e-82a8-4d05-bbf1-ef40b2a5ecd1/image.gif)

	public  String user(String passwd) {
		System.out.println("SecurityController01 user start . . . . . ");
		System.out.println("SecurityController01 user passwd->"+passwd);
   	//해쉬 암호화 
		String encPasswd = bCryptPasswordEncoder.encode(passwd);
		System.out.println("SecurityController01 user encPasswd->"+encPasswd);
		return "user";
	}


oBootSecurity02

▶실무에선 Gradle방식 사용

▶security 사용할땐 configuration 도 생성해주기

▶보통의미
dto 라고 정의하면 --> mybatis
dmmain/entity라고 정의하면 --> jpa

User

package com.oracle.oBootSecurity02.entity;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import org.hibernate.annotations.CreationTimestamp;

import lombok.Data;

@Entity
@Data
@SequenceGenerator(
             name = "user_seq_gen",
             sequenceName = "user_seq_generator",
             initialValue = 1,
             allocationSize = 1
            )
@Table(name = "user01")
public class User {
 @Id   // primary Key
 @GeneratedValue(
             strategy = GenerationType.SEQUENCE,
             generator = "user_seq_gen"
             )
 private int id;
 private String username;
 private String password;
 private String email;
 private String role; // ROLE_USER, ROLE_ADMIN, ROLE_MANAGER
 @CreationTimestamp  //INSERT 시 자동으로 값을 채워줌
 private Timestamp createDate;
}

SecurityRepository

인터페이스생성

package com.oracle.oBootSecurity02.repository;

import org.springframework.stereotype.Repository;

import com.oracle.oBootSecurity02.entity.User;

@Repository
public interface SecurityRepository {
	void save(User user); 
	User findByUsername(String username);
	//  Username을 where 조건절에 넣어 데이터를 가져올 수 있도록 findByUsername 정의

}

.`

JpaSecurityRepository

SecurityRepository 상속받아 class생성

package com.oracle.oBootSecurity02.repository;

import javax.persistence.EntityManager;

import org.springframework.stereotype.Repository;

import com.oracle.oBootSecurity02.entity.User;

import lombok.RequiredArgsConstructor;
import lombok.ToString;

@Repository
@RequiredArgsConstructor
public class JpaSecurityRepository implements SecurityRepository {
	private final EntityManager em;
	
	
	@Override
	public void save(User user) {
		  em.persist(user);

	}
		//username -->unique 라 가정
	@Override
	public User findByUsername(String username) {
		User user = em.createQuery("select u from User u where username = :username", User.class)
							.setParameter("username", username)
							.getSingleResult();
		return user;
	}

}

SecurityService

인터페이스생성

package com.oracle.oBootSecurity02.service;

import org.springframework.stereotype.Service;

import com.oracle.oBootSecurity02.entity.User;

@Service
public interface SecurityService {
User findByUsername(String username);
void save(User user);
}

SecurityServiceImpl

SecurityService 상속받아 class생성

package com.oracle.oBootSecurity02.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.oracle.oBootSecurity02.entity.User;
import com.oracle.oBootSecurity02.repository.JpaSecurityRepository;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional
public class SecurityServiceImpl implements SecurityService {
		private final JpaSecurityRepository jpaSecurityRepository;
	
	@Override
	public User findByUsername(String username) {
		User user = jpaSecurityRepository.findByUsername(username);
		return user;
	}

	@Override
	public void save(User user) {
		System.out.println("SecurityServiceImpl save start  . . .  ");
		jpaSecurityRepository.save(user);
	}

}

SecurityController02


package com.oracle.oBootSecurity02.controller;

import java.security.Principal;
import java.util.Iterator;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.oracle.oBootSecurity02.configuration.auth.PrincipalDetails;
import com.oracle.oBootSecurity02.entity.User;
import com.oracle.oBootSecurity02.service.SecurityService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
@RequiredArgsConstructor

public class SecurityController02 {
private  final SecurityService securityServiceImpl;   //bean으로 등록되어있음 -> @service걸려있기때문에
private final BCryptPasswordEncoder bCryptPasswordEncoder;  //암호화 사용하기위해서 
                         // BCryptPasswordEncoder 는 빈 설정해줘야함 

	@GetMapping("/user")
	public String user(@AuthenticationPrincipal PrincipalDetails Principal ) {
		System.out.println("SecurityController02 user start . . . . . ");
		System.out.println("SecurityController02 user principar-> " +Principal);
		Iterator<? extends GrantedAuthority> iter = Principal.getAuthorities().iterator();
		while (iter.hasNext()) {
			GrantedAuthority authority = iter.next();
			System.out.println("authority.getauthority()->"+authority.getAuthority());
			
		}                                                                                                                                                                                                                               ![](https://velog.velcdn.com/images/rhdguswlx/post/19446cf8-ed08-4b63-bc39-c6c844192139/image.png)
                                                                                                                                                     
				return "user";
	}
	
							/*
							 * public String user(String passwd) {
							 * System.out.println("SecurityController01 user start . . . . . ");
							 * System.out.println("SecurityController01 user passwd->"+passwd); //해쉬 암호화
							 * String encPasswd = bCryptPasswordEncoder.encode(passwd);
							 * System.out.println("SecurityController01 user encPasswd->"+encPasswd); return
							 * "user"; }
							 */
	@GetMapping("/user/2")
	public  String user2(String passwd) {
		System.out.println("SecurityController02 user start . . . . . ");
				return "user";	
	}
	
	@GetMapping("/manager")
	public  String manager() {
		System.out.println("SecurityController02 manager start . . . . . ");
		
		return "manager";
	}
	@GetMapping("/manager/2")
	public  String manager2() {
		System.out.println("SecurityController02 manager2 start . . . . . ");
		
		return "manager";
	}
	
	
	@GetMapping("/admin")
	public  String admin() {
		System.out.println("SecurityController02 user start . . . . . ");
		
		return "admin";
	}
	@GetMapping("/admin/2")
	public  String admin2() {
		System.out.println("SecurityController02 admin2 start . . . . . ");
		
		return "admin";
	}
//로그인실패시
	@GetMapping("/loginFail")
	public String loginFail() {
		System.out.println("loginFail start. . . ");
		return "loginFail";
		
	}
	//로그인시
	@GetMapping("/loginForm")
	public String loginForm() {
		System.out.println("SecurityController02  loginForm진행 . . . ");
		
		return "loginForm";
		
	}
	//user등록 form
	@GetMapping("/joinForm")
	public String joinForm() {
		System.out.println("SecurityController02  joinForm진행 . . . ");
		
		return "joinForm";
		
	}
	//진짜 user등록
	@PostMapping("/joinProc")
	public String joinProc(User user) {
		System.out.println("회원가입 진행:" + user);
		//spring security 권장-- >passwd암호화
		String encPasswd = bCryptPasswordEncoder.encode(user.getPassword()); //사용자가 적은 패스워드
		user.setPassword(encPasswd);
		securityServiceImpl.save(user);
		
		return "redirect:/loginForm ";


}

}

SecurityConfig

package com.oracle.oBootSecurity02.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration 			//Ioc 빈(Bean) 을 등록
@EnableWebSecurity //Spring Security 설정 클래스임을 알려줌 
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) //특정 주소로 접근하면 권한 및 인증을 미리 체크하기 위해 사용
public class SecurityConfig {
     // 비밀번호를 암호화해서 사용할 수 있도록 Bean으로 등록함
		@Bean   //비밀번호 암호화 객체
		public BCryptPasswordEncoder encodePwd() {
			
			return new BCryptPasswordEncoder();
		
		}
		//
		@Bean
		   protected SecurityFilterChain 
   //HttpSecurity를 통해 HTTP 요청에 대한 보안을 구성할 수 있다.
filterChain(HttpSecurity http) throws Exception {
		      http.csrf().disable();
     //csrf 토큰 없이 요청하면 해당 요청을 막기 때문에 잠깐 비활성화해주었다.
		      http.authorizeRequests()
            //HttpServletRequest에 따라 접근을 제한한다
		         .antMatchers("/user/**")
               //user로 시작하는 경로에 접근가능
		         .authenticated() // /user/** 은 인증 필요 --> 인증만 되면 들어갈 수 있다.
		      .and()
		         .formLogin()
//form 기반으로 인증한다. /login 경로로 접근하면, Spring Security에서 제공하는 로그인 Form을 사용할 수 있다
		         .loginPage("/loginForm")
//기본으로 제공되는 form 말고, 커스텀 로그인 폼을 사용하기 위해 loginPage() 메소드를 사용했다.
		         .loginProcessingUrl("/login")
//Security에서 해당 주소로 오는 요청을 낚아채서 수행한다.
		         .failureUrl("/loginFail")
//로그인 실패시 이동하는 페이지 
		         .defaultSuccessUrl("/");
//로그인성공시 이동되는 페이지 
		      return http.build();
		   }

}

PrincipalDetailsService

package com.oracle.oBootSecurity02.configuration.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 org.springframework.transaction.annotation.Transactional;

import com.oracle.oBootSecurity02.entity.User;
import com.oracle.oBootSecurity02.repository.JpaSecurityRepository;
import com.oracle.oBootSecurity02.repository.SecurityRepository;

import lombok.RequiredArgsConstructor;

@Service
@Transactional
@RequiredArgsConstructor
//DB에 담긴 사용자 인증정보와 비교하기 위해 UserDetailsService에 사용자 정보를 넘겨줌
//Security 설정에서 loginProcessingUrl("/login") 
//login 요청이 오면 자동으로 UserDetailsService Type으로 
//IOC되어 있는  loadUserByUsername Method가 실행(약속)
//Security session( 내부-> Authentication(내부->userDetails) )
public class PrincipalDetailsService implements UserDetailsService {

	 private final SecurityRepository jpaSecurityRepository;
 /* username이 DB에 있는지 확인 */	
@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
	 System.out.println("PrincipalDetailsService username : "+username);
	 User user = jpaSecurityRepository.findByUsername(username);
	if(user== null) return null;
	else          return new PrincipalDetails(user);
	}             /* 시큐리티 세션에 유저 정보 저장 */

}

PrincipalDetails

package com.oracle.oBootSecurity02.configuration.auth;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.oracle.oBootSecurity02.entity.User;


import lombok.Data;
//Authentication 객체에 저장할 수 있는 유일한 타입



@Data
public class PrincipalDetails implements UserDetails {

	private User user;
	
	public PrincipalDetails (User user) {
		this.user = user;
		System.out.println("PrincipalDetails 생성자 getUsername-->  "+user.getUsername());
		
	}
	 
	@Override
	//해당 user의 권한을 리턴하는곳.
    /* 유저의 권한 목록 */
	public Collection<? extends GrantedAuthority> getAuthorities() {
		Collection<GrantedAuthority> collect = new ArrayList<>();
		collect.add(new GrantedAuthority() {
	
			
			@Override
			public String getAuthority() {
				System.out.println("getAuthorities GrantedAuthority user.getRole()--> "+user.getRole());
				return user.getRole();
			}
		});
		
		return collect;
	}

	@Override
	public String getPassword() {
		// TODO Auto-generated method stub
		return user.getPassword();
	}

	@Override
	public String getUsername() {
		// TODO Auto-generated method stub
		return user.getUsername();
	}

   /* 계정 만료 여부
     *  true : 만료 안됨
     *  false : 만료
     */
	@Override  
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

 /* 계정 잠김 여부
     *  true : 잠기지 않음
     *  false : 잠김
     */

	@Override   
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}
     /* 비밀번호 만료 여부
     *  true : 만료 안됨
     *  false : 만료
     */
	@Override 
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}
  /* 사용자 활성화 여부
    *  true : 만료 안됨
    *  false : 만료
    */
	@Override  
	// 예시 ->1년동안 Login 안한 사람 휴먼 계정 ->return false
	public boolean isEnabled() {
		// TODO Auto-generated method stub
		return true;
	}

}


html

index.html
Insert title here

JungChoong SHOP

user 권한

user 회원목록

manager 권한

/manager manager2

admin 권한

admin admin2



loginFail.html
Insert title here

로그인 실패 되었네요

로그인 권한을 살펴보세요

loginForm.html
Insert title here

로그인 페이지

이름

password

등록

User 신규 생성


joinForm.html
Insert title here

jpa security 회원등록(id 자동생성)

이름(회원코드) :

비번 :

email :

권한 : 일반유저 관리자 admin 회원가입

user.html
Insert title here

User 인증 성공





id는 자동생성됨
🔽암호화되어서 비밀번호 들어감
암호화의 목적 -->확산과 혼돈

0개의 댓글