프로젝트 생성 시에 디폴트로 지정된 패키지명 아래를 사용한다
약속된 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>
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day103 (0) | 2023.04.26 |
---|---|
국비 지원 개발자 과정_Day102 (0) | 2023.04.25 |
국비 지원 개발자 과정_Day100 (0) | 2023.04.21 |
국비 지원 개발자 과정_Day99 (0) | 2023.04.20 |
국비 지원 개발자 과정_Day98 (0) | 2023.04.19 |
댓글