서블릿 컨테이너
1. 사용자 request
→ 2. 필터체인 - 인코딩, 세션, 로그아웃 관련 필터 등등
→ 3. DispatchServlet / 또 다른 서블릿도 존재할 수 있음 → Controller / 업무마다 하나씩 존재
→ 4. 메소드 실행
모든 리퀘스트는 필터체인을 거쳐야 서블릿에 도착한다
최초 경유 필터 Filter0 → FilterProxyFilter(인터셉트, 인증인가에따라 다른 서비스를 적용) → 또 다른 Filter1 → Servlet
필터체인은 한 개 이상, URL패턴에따라 다르게 설정 가능
<스프링 시큐리티 유저와 어드민 테스트 - SecurityConfig.java>
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
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.web.SecurityFilterChain;
@EnableWebSecurity(debug=true) // 요청이 지나가는 필터정보 확인 가능
@EnableGlobalMethodSecurity(securedEnabled=true ,prePostEnabled=true) // 권한을 체크하겠다는 설정 추가
public class SecurityConfig {
@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("1234")
.roles("ADMIN")
);
http.csrf().disable();
http.authorizeRequests()
// localhost:5000/user로 요청하면 403발동 - 403접근권한이 없을때
.antMatchers("/user/**").authenticated() // 인증만 되면 들어갈 수 있는 주소
// manager나 admin 권한이 있는 사람만 접근가능
.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")
.defaultSuccessUrl("/");
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 출력
localhost:5000/admin 403발동
localhost:5000/manager 출력
*/
<스프링 시큐리티 유저와 어드민 테스트 - HomeController.java>
package com.example.demo.controller;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class HomeController {
Logger logger = LogManager.getLogger(HomeController.class);
@GetMapping("/")
public String index(HttpServletRequest req) {
logger.info("index");
logger.info("admin 호출: " + req.isUserInRole("ROLE_ADMIN"));
logger.info("user 호출: " + req.isUserInRole("ROLE_USER"));
logger.info("manager 호출: " + req.isUserInRole("ROLE_MANAGER"));
if(req.isUserInRole("ROLE_ADMIN")) {
return "forward:admin-index.jsp";
}
else if(req.isUserInRole("ROLE_USER")) {
return "forward:user-index.jsp";
}
else {
return "forward:index.jsp";
}
}
@GetMapping("/loginForm")
public String loginForm() {
logger.info("loginForm");
return "redirect:/auth/loginForm.jsp";
}
/*
/login이 요청되면 스프링 시큐리티가 인터셉트해서 대신 로그인 진행해줌
@GetMapping("/login")
public @ResponseBody String login() {
logger.info("login");
return "로그인 후 페이지";
}
*/
@GetMapping("/user")
public @ResponseBody String user() {
logger.info("user");
return "user";
}
@GetMapping("/manager")
public @ResponseBody String manager() {
logger.info("manager");
return "manager";
}
@GetMapping("/admin")
public @ResponseBody String admin() {
logger.info("admin");
return "admin";
}
@GetMapping("/auth")
public @ResponseBody Authentication auth() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
<스프링 시큐리티 유저와 어드민 테스트 - application.yml>
server:
port: 5000
servlet:
context-path: /
encoding:
charset: UTF-8
enabled: true
force: true
spring:
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
username: root
password: 1234
#ddl-auto: create이면 매번 테이블을 만들어준다
#create전략이면 테이블을 삭제하고 새로 만들어 준다
#그래서 맨 처음 테이블이 만들어질 때만 생성하고 다음에 실행할 때는 update로 바꾸어 주어야함
#show-sql을 true로 주면 콘솔창에 쿼리문이 출력됨
#hibernate.format_sql: true로 주면 쿼리문이 정렬이 되어서 출력됨
#physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 이것은
#Entity를 만들때(즉 테이블을 만들때) 변수명 그대로 테이블 컬럼으로 만들어준다는 의미임
#open-in-view:true 로 주면 Lazy로드가 가능해짐. false로 하면 영속성 컨텍스트가 service계층에서 종료됨
#영속성을 프리젠테이션 계층까지 가져간다. 트랜잭션은 Service계층에서 종료된다
#Transaction이 종료된 후에도 Controller의 Session이 close 되지 않았기 때문에 영속 객체는
#Persistence 상태를 유지할 수 있으며, 따라서 프록시 객체에 대한 Lazy Loading을 수행할 수 있게 된다
#버전 2.0부터 스프링 부트는 기본적으로 OSIV가 활성화되어 있을때 경고를 발생하므로 프로덕션 시스템에
#영향을 주기 전에 이 문제를 발견할 수 있다
#서블릿 필터에서 Session을 오픈하고 트랜잭션을 시작하던 전통적인 방식의 OPEN SESSION IN VIEW 패턴과 달리
#SpringMVC에서 제공하는 OpenSessionInViewFilter 는 필터 내에서 Session은 오픈하지만
#트랜잭션은 시작하지 않는다. 따라서 서블릿 필터 안에서는 커넥션 풀로부터 JDBC커넥션을 얻을 필요가 없다
#https://getinthere.tistory.com/27 참고하세요
jpa:
open-in-view: true
hibernate:
ddl-auto: create #create로하면 model추가시 테이블 자동생성됨. 매번생성되니 테이블생성되면 update로 바꿀것
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
use-new-id-generator-mappings: false
show-sql: true
properties:
hibernate.format_sql: true
security:
user:
name: user
password: 123
roles: USER #인증과 인가[권한](role -> ROLE_ADMIN, ROLE_USER, ROLE_MANAGER)
<스프링 시큐리티 유저 MySql 입력 - User.java>
package com.example.demo.model;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.hibernate.annotations.CreationTimestamp;
import lombok.Builder;
import lombok.Data;
@Entity // jpa사용시 테이블 생성해줌 - 확인할것. application.yml에 create모드인지 체크
@Data // getter, setter생성해줌
public class User {
@Id // primary key
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String username;
private String password;
private String email;
private String role; // ROLE_USER, ROLE_MANAGER, ROLE_ADMIN
@CreationTimestamp
private Timestamp createDate;
// 회원가입에 사용할 생성자 추가
@Builder
public User(String username, String password, String email
, String role, Timestamp createDate) {
this.username = username;
this.password = password;
this.email = email;
this.role = role;
this.createDate = createDate;
}
}
<스프링 시큐리티 유저 MySql 입력 - UserRepository.java>
package com.example.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.model.User;
// CRUD메소드는 JpaRepository가 제공함
public interface UserRepository extends JpaRepository<User, Integer> {
public User findByUsername(String username);
}
<스프링 시큐리티 유저 MySql 입력 - HomeController.java>
package com.example.demo.controller;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
@Controller
public class HomeController {
Logger logger = LogManager.getLogger(HomeController.class);
@Autowired
private UserRepository userRepository;
@GetMapping("/")
public String index(HttpServletRequest req) {
logger.info("index");
logger.info("admin 호출: " + req.isUserInRole("ROLE_ADMIN"));
logger.info("user 호출: " + req.isUserInRole("ROLE_USER"));
logger.info("manager 호출: " + req.isUserInRole("ROLE_MANAGER"));
if(req.isUserInRole("ROLE_ADMIN")) {
return "forward:admin-index.jsp";
}
else if(req.isUserInRole("ROLE_USER")) {
return "forward:user-index.jsp";
}
else {
return "forward:index.jsp";
}
}
// 로그인 화면
@GetMapping("/loginForm")
public String loginForm() {
logger.info("loginForm");
return "redirect:/auth/loginForm.jsp";
}
/*
/login이 요청되면 스프링 시큐리티가 인터셉트해서 대신 로그인 진행해줌
@GetMapping("/login")
public @ResponseBody String login() {
logger.info("login");
return "로그인 후 페이지";
}
*/
// 회원가입 화면 부르기
@GetMapping("/joinForm")
public String joinForm() {
logger.info("joinForm");
return "redirect:/auth/joinForm.jsp";
}
// 회원가입
@PostMapping("/join")
public String join(User user) {
logger.info("user");
user.setRole("ROLE_USER");
userRepository.save(user);
return "redirect:/auth/loginForm.jsp";
}
@GetMapping("/user")
public @ResponseBody String user() {
logger.info("user");
return "user";
}
@GetMapping("/manager")
public @ResponseBody String manager() {
logger.info("manager");
return "manager";
}
@GetMapping("/admin")
public @ResponseBody String admin() {
logger.info("admin");
return "admin";
}
@GetMapping("/auth")
public @ResponseBody Authentication auth() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
<스프링 시큐리티 유저 MySql 입력 - application.yml>
server:
port: 5000
servlet:
context-path: /
encoding:
charset: UTF-8
enabled: true
force: true
spring:
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jsp
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
username: root
password: 1234
#ddl-auto: create이면 매번 테이블을 만들어준다
#create전략이면 테이블을 삭제하고 새로 만들어 준다
#그래서 맨 처음 테이블이 만들어질 때만 생성하고 다음에 실행할 때는 update로 바꾸어 주어야함
#show-sql을 true로 주면 콘솔창에 쿼리문이 출력됨
#hibernate.format_sql: true로 주면 쿼리문이 정렬이 되어서 출력됨
#physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl 이것은
#Entity를 만들때(즉 테이블을 만들때) 변수명 그대로 테이블 컬럼으로 만들어준다는 의미임
#open-in-view:true 로 주면 Lazy로드가 가능해짐. false로 하면 영속성 컨텍스트가 service계층에서 종료됨
#영속성을 프리젠테이션 계층까지 가져간다. 트랜잭션은 Service계층에서 종료된다
#Transaction이 종료된 후에도 Controller의 Session이 close 되지 않았기 때문에 영속 객체는
#Persistence 상태를 유지할 수 있으며, 따라서 프록시 객체에 대한 Lazy Loading을 수행할 수 있게 된다
#버전 2.0부터 스프링 부트는 기본적으로 OSIV가 활성화되어 있을때 경고를 발생하므로 프로덕션 시스템에
#영향을 주기 전에 이 문제를 발견할 수 있다
#서블릿 필터에서 Session을 오픈하고 트랜잭션을 시작하던 전통적인 방식의 OPEN SESSION IN VIEW 패턴과 달리
#SpringMVC에서 제공하는 OpenSessionInViewFilter 는 필터 내에서 Session은 오픈하지만
#트랜잭션은 시작하지 않는다. 따라서 서블릿 필터 안에서는 커넥션 풀로부터 JDBC커넥션을 얻을 필요가 없다
#https://getinthere.tistory.com/27 참고하세요
jpa:
open-in-view: true
hibernate:
ddl-auto: create #create로하면 model추가시 테이블 자동생성됨. 매번생성되니 테이블생성되면 update로 바꿀것
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
use-new-id-generator-mappings: false
show-sql: true
properties:
hibernate.format_sql: true
'국비학원 > 수업기록' 카테고리의 다른 글
국비 지원 개발자 과정_Day95 (0) | 2023.04.14 |
---|---|
국비 지원 개발자 과정_Day94 (0) | 2023.04.13 |
국비 지원 개발자 과정_Day92 (0) | 2023.04.11 |
국비 지원 개발자 과정_Day91 (0) | 2023.04.07 |
국비 지원 개발자 과정_Day90 (0) | 2023.04.06 |
댓글