본문 바로가기
국비학원/수업기록

국비 지원 개발자 과정_Day101

by 루팽 2023. 4. 25.

프로젝트 생성 시에 디폴트로 지정된 패키지명 아래를 사용한다

약속된 basepackage가 정해져 있다

→ com.example.demo

만약 다른 패키지를 사용하려면 추가 설정이 필요한 부분

 

스프링 시큐리티가 기본적으로 제공하는 로그인 화면이 아니라 사용자 정의 로그인 화면으로 처리하려면, 반드시 SecurityCongif.java를 추가할 것

 

<스프링 시큐리티 복습 진행중 - DatabaseConfiguration.java>

package com.example.demo;

import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

/*
 * @Configuration
 * 사용자 정의 클래스도 ApplicationContext나 BeanFactory의 관리를 받을 수 있다 - 의존성 주입(Dependency Injection)
 * 
 * application.properties - maven방식
 * application.yml - gradle방식(반복코드 없음, json포맷이라서)
 * 
 * resource - mapper
 * 	member.xml, board.xml, order.xml
 * 
 * static - CDN
 * 	css, js, images(정적인것들을 static에 넣음)
 */

@Configuration
@PropertySource("classpath:/application.yml") // classpath -> resource -> log4j.properties
public class DatabaseConfiguration {
	private static final Logger logger = LogManager.getLogger(DatabaseConfiguration.class);

	// @Bean - @Configuration으로 선언된 클래스에서만 사용가능한 어노테이션
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.hikari") // application.properties의 접두어
	public HikariConfig hikariConfig() { // 인스턴스화
		return new HikariConfig(); // 생성자호출 - 메모리에 로딩됨 - 변수와 메소드를 누릴 수 있음(주입해줌)
	}

	// 물리적으로 떨어져있는 오라클서버(application.properties에 정의됨)와 연결통로 확보
	// POJO에서는 MyBatisConfig.xml이 hikariConfig()역할을 대신함
	@Bean
	public DataSource dataSource() {
		// 인터페이스=구현체클래스 (new HikariConfig()) -> application.properties
		DataSource dataSource = new HikariDataSource(hikariConfig());
		logger.info("datasource : {}", dataSource);
		return dataSource;
	}

	// @Autowired 의존성 주입
	@Autowired
	private ApplicationContext applicationContext; // 빈관리 - 이른 인스턴스화 - BeanFactory의 자손 클래스임(기능 더 많다)

	/*
	 	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation" value="WEB-INF/mybatis-config.xml"/>
		<property name="dataSource" ref="data-source-target"/> 
		</bean>
		위의 코드를 대체하는 것의 아래의 @Bean
	 */
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
		SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dataSource);
		//classpath는 src/main/resources이고 쿼리가 있는 xml 위치를 설정해주면 된다.
		sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/**/*.xml"));
		return sqlSessionFactoryBean.getObject();
	}

	/*
	 	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory"/>
		</bean>
		위의 코드를 대체하는 것의 아래의 @Bean
	 */
	@Bean
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}

 

<스프링 시큐리티 복습 진행중 - HomeController.java>

package com.example.demo.controller;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
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.example.demo.logic.UserLogic;
import com.example.demo.model.User;

@Controller
public class HomeController {
	Logger logger = LogManager.getLogger(HomeController.class);
	
	@Autowired
	private UserLogic userLogic = null;
	
	@Autowired
	public BCryptPasswordEncoder bCryptPasswordEncoder = null;
	
	@GetMapping("/")
	public String index() {
		logger.info("index");
		return "forward:index.jsp";
	}
	
	// 로그인 화면
	@GetMapping("/loginForm")
	public String loginForm() {
		logger.info("loginForm");
		return "forward:loginForm.jsp";
	}

	// 로그인 에러 화면
	@GetMapping("/login-error")
	public String loginError() {
		logger.info("loginError");
		return "forward:loginError.jsp";
	}
	
	// 회원가입 화면
	@GetMapping("/joinForm")
	public String joinForm() {
		logger.info("joinForm");
		return "forward:joinForm.jsp";
	}
	
	// 회원가입
	@PostMapping("/join")
	public String join(User user) {
		logger.info("join");
		int result = 0; // 등록 성공 유무 담기
		user.setRole("ROLE_USER");
		// 패스워드 암호화처리
		String rawPassword = user.getPassword();
		String encPassword = bCryptPasswordEncoder.encode(rawPassword);
		user.setPassword(encPassword);
		result = userLogic.memberInsert(user);
		return "forward:index.jsp";
	}
	
	// 유저 화면
	@GetMapping("/user")
	public String userPage() {
		logger.info("userPage");
		return "forward:user-index.jsp";
	}
	
	// 관리자 화면
	@GetMapping("/admin")
	public String adminPage() {
		logger.info("adminPage");
		return "forward:admin-index.jsp";
	}
}

 

<스프링 시큐리티 복습 진행중 - SecuriryConfig.java>

package com.example.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

//import com.example.demo.oauth.PrincipalOauth2UserService;

// 1-1에서틑 SecurityConfig 파일을 생성하지 않았다 -> 스프링시큐리티에서 제공하는 인증화면으로 처리 진행됨
// 이 클래스를 정의하지 않으면 스프링 시큐리티가 제공하는 DefaultLoginPageGeneratingFilter가
// 제어권을 가져가서 개발자가 만든 로그인 화면을 만날 수 없다
// 결론: 내가 주도하는 인증처리를 위해서는 반드시 선언해야함
@EnableWebSecurity(debug=true) // 요청이 지나가는 필터정보 확인 가능
@EnableGlobalMethodSecurity(securedEnabled=true ,prePostEnabled=true) // 권한을 체크하겠다는 설정 추가
public class SecurityConfig {
	// 암호화가 안된 비밀번호로는 로그인이 안됨
	// 패스워드를 암호화하기위한 코드 추가
	// Bean어노테이션을 적으면 해당 메소드의 리턴되는 오브젝트를 IoC로 등록해줌
//	@Autowired
//	private PrincipalOauth2UserService principalOauth2UserService;
	
	@Bean
	public BCryptPasswordEncoder encodePwd() {
		return new BCryptPasswordEncoder();
	}

	// 상수로 처리시에 주석을 반드시 풀 것
	/*
	 @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder();
	 }
	 */
	
	// 만약 관리자 계정이면 하위인 유저 계정에 접근할 수 있도록 처리하려면 아래 코드를 추가함
	// 유저는 관리자에 접근이 안되지만 관리자는 유저페이지도, 관리자페이지 접근 가늘하도록 설정
	@Bean
	RoleHierarchy roleHierarchy() {
		RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
		roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
		return roleHierarchy;
	}
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
		AuthenticationManagerBuilder auth = 
				http.getSharedObject(AuthenticationManagerBuilder.class);
		/*		
		auth.inMemoryAuthentication()
		// 아래 메소드가 데프리케이트 대상인 이유는 테스트용도로만 사용하라는 경고의 의미
		// 보안상 안전하지 않으니 쓰지말것을 당부 -  그러나 지원은 끊지 않음
 		.withUser(User.withDefaultPasswordEncoder()
				.username("user")
				.password("123")
				.roles("USER")
		).withUser(User.withDefaultPasswordEncoder()
				.username("admin")
				.password("123")
				.roles("ADMIN")
		);
		*/
		http.csrf().disable(); // csrf filter 비활성화하기
		http.authorizeRequests() // http요청으로 들어오는 모든 것에대해서 매칭하기
		// localhost:5000/user로 요청하면 403발동 - 403접근권한이 없을때
		.antMatchers("/user/**").authenticated() // 인증만 되면 들어갈 수 있는 주소
		// manager나 admin 권한이 있는 사람만 접근가능, securedEnabled=true속성추가해야함
		.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
		// admin 권한이 있는 사람만 접근가능
		.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
		.anyRequest().permitAll() // 위 세가지가 아닌 경우는 모두 허용함
		.and()
		.formLogin()
		.loginPage("/loginForm") // 2차 단위테스트 - 권한에따라서 페이지 제어
		.loginProcessingUrl("/login") // 이 URL이 요청되면 시큐리티가 인터셉트해서 대신 로그인 진행
		.failureUrl("/login-error")
		.defaultSuccessUrl("/");
//		.and()
		// 구글 로그인 기능 구현 추가
//		.oauth2Login()
//		.loginPage("/loginForm")
		// 구글 로그인 후 후처리 필요함
		// 1. 인증받기 -> 2. 엑세스 토큰(권한) -> 3. 사용자 프로필정보 가져오기 -> 4. 그 정보로 회원가입 처리가능(여기선 미적용)
		// 구글 로그인이 성공되면 코드를 받는게 아니라 엑세스 토큰과 사용자에대한 프로필정보를 한번에 받아옴
//		.userInfoEndpoint()
//		.userService(principalOauth2UserService); // 구글 로그인 후처리 클래스 추가
		// 현재 페이지에서 로그아웃을 눌럿을 때 로그인 페이지가 아니라 메인 페이지에 있도록 해줌
		//.logout(logout -> logout.logoutSuccessUrl("/"))
		// 403번 접근제한 예외 발생시 이동할 페이지 요청URL 작성하기
		//.exceptionHandling(exception -> exception.accessDeniedPage("/access-denied"));
		
		return http.build();
	}// end of filterChain
}

/*
	테스트 시나리오
	localhost:5000 요청은 권한과 상관없이 열림
	localhost:5000/user
	localhost:5000/admin
	localhost:5000/manager
	로그인 페이지로 이동함
	
	첫번째 - user롤일때
	localhost:5000/user 출력되고
	localhost:5000/admin 403발동
	localhost:5000/manager 403발동
	
	두번째 - admin 로그인했을때
	lcalhost:5000/user 출력
	localhost:5000/admin 출력
	localhost:5000/manager 출력
	
	두번째 - manager 로그인했을때
	lcalhost:5000/user 403발동
	localhost:5000/admin 403발동
	localhost:5000/manager 출력
*/

 

<스프링 시큐리티 복습 진행중 - UserLogic.java>

package com.example.demo.logic;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dao.UserDao;
import com.example.demo.model.User;

@Service
public class UserLogic {
	Logger logger = LogManager.getLogger(UserLogic.class);
	
	@Autowired
	private UserDao userDao = null;
	
	public int memberInsert(User user) {
		logger.info(user);
		int result = userDao.memberInsert(user);
		return result;
	}
}

 

<스프링 시큐리티 복습 진행중 - UserDao.java>

package com.example.demo.dao;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.example.demo.model.User;

@Repository
public class UserDao {
	Logger logger = LogManager.getLogger(UserDao.class);
	
	@Autowired
	private SqlSessionTemplate sqlSessionTemplate = null;

	public int memberInsert(User user) {
		int result = 0;
		result = sqlSessionTemplate.update("memberInsert", user);
		return 0;
	}
}

 

<스프링 시큐리티 복습 진행중 - User.java>

package com.example.demo.model;

import java.sql.Timestamp;

import lombok.Builder;
import lombok.Data;

@Data // getter, setter생성해줌
public class User {
	private int id;
	private String username;
	private String password;
	private String email;
	private String role; // ROLE_USER, ROLE_MANAGER, ROLE_ADMIN
	private String createDate;
	
	// 회원가입에 사용할 생성자 추가
	// @Builder하면 안됨! - 들고온 정보를 모두 초기화하기에! 문법상 써놓은 생성자임!
	public User() {}
	
	// UserRepository의 findByUsername으로 찾아낸 정보로 초기화가됨
	@Builder
	public User(String username, String password, String email
			, String role, String createDate) {
		this.username = username;
		this.password = password;
		this.email = email;
		this.role = role;
		this.createDate = createDate;
	}
}

 

<스프링 시큐리티 복습 진행중 - member.xml>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo">
	<!-- 회원 정보 입력 -->
	<insert id="memberInsert" parameterType="map">
		INSERT INTO member202210(
						  id
						, username
						, password
						, email
						, role
						, createDate
						)
		VALUES (SEQ_MEMBER202210_ID.nextval
					<if test="username != null">
						,#{username}
					</if>
					<if test="password != null">
						,#{password}
					</if>
					<if test="email != null">
						,#{email}
					</if>
					<if test="role != null">
						,#{role}
					</if>
						, to_char(sysdate, 'YYYY-MM-DD')
					)
	</insert>
</mapper>

댓글