Spring Framework/[인프런] Spring Security & OAuth 2.0 & JWT

Chapter 01. 스프링 시큐리티(Spring Security) 기본

계란💕 2022. 10. 14. 13:41

1.1 환경 설정

 

 

  • yml 파일
    • mvc: view: ... 부분은 삭제한다.
    • 디펜던시에 mustache를 등록하면 디폴트로 mvc에 경로가 잡힌다. 따라서 삭제 가능
<hide/>
server:
  port: 8080
  servlet:
    context-path: /
    encoding:
      charset: UTF-8
      enabled: true
      force: true
      
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?serverTimezone=Asia/Seoul
    username: cos
    password: cos1234
    
  mvc:
    view:
      prefix: /templates/
      suffix: .mustache

  jpa:
    hibernate:
      ddl-auto: update #create update none
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    show-sql: true

 

  • IndexController
    • @컨트롤러 파일에 @ResponseBody를 붙이고 String을 반환하면 @RestController 처럼 동작한다.
      • 따라서 join 페이지에 접속하면 "join"이라고 뜬다.
<hide/>
@Controller
public class IndexController {

    @GetMapping("/")
    public String index(){
        return "index";
    }

    @GetMapping("/user")
    public @ResponseBody String user(){
        return "user";
    }

    @GetMapping("/admin")
    public @ResponseBody String admin(){
        return "admin";
    }

    @GetMapping("/manager")
    public @ResponseBody String manager(){
        return "manager";
    }

    @GetMapping("/login")
    public @ResponseBody String login(){
        return "login";
    }

    @GetMapping("/join")
    public @ResponseBody String join(){
        return "join";
    }

    @GetMapping("/joinProc")
    public  @ResponseBody String joinProc(){
        return "회원가입 완료!";
    }
   
}

 

  •  index
    • preffix는 html로 간다.
    • mustache로 하는 경우?
      • mustache는  스프링 부트에서 공식으로 지원하는 템플릿 엔진이다. Java를 포함한 여러 가지 언어를 지원한다. 
      • 다른 서버 템플릿 엔진은 JSP, Velocity, Thymeleaf 등이 있다. 
      • 로직 코드가 없어서 view 역할에 충실하다. (서버와 클라이언트의 코드가 섞이는 문제가있는 JSP와 다르다.)

 

  • Webconfig 
    • Webconfig는 WebMvcConfigurer 인터페이스를 implement
      • WebMvcConfigurer을 이용하면 ViewResolver를 커스텀할 수 있다.
      •  
    • 메소드 "configureViewResolvers"를 오버라이드해서 내가 만든 뷰에 대해 설정한다.
    • "classpath:"는 내 프로젝트 경로
    • setSuffix("html"): html로 만들어도 mustache라고 인식한다.

 

 

 

1.2 시큐리티 설정

  • securityConfig
    • @EnableWebSecurity를 붙이면 스프링 시큐리티 필터가 스프링 필터 체인에 등록된다.
      • 웹 보안을 활성화 시킨다.
    • configure 오버라이드
    • csrf(사이트 간 위조 요청)는 비활성화한다.
    • "manager"로 시작하는 페이지는 admin, manager만 허용하고 "admin" 페이지는 admin만 허용한다.
    • "/user"로 들어로면 인증이 필요한 페이지다.
    • 403 에러는 접근 권한이 없는 경우에 나는 에러이다.
<hide/>
@EnableWebSecurity  // 스프링 시큐리티 필터가 스프링
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests()
            .antMatchers("/user/**").authenticated()
            .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
            .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
            .anyRequest().permitAll();  // 다른 요청은 전부 허용한다.
       
    }
}

 

  • 위와 같이 설정하고 나면 로그인 페이지에 접속했을 때 아래와 같이 뜬다.
    • 아까 처럼 스프링 시큐리티가 이 페이지를 낚아채지 않아서 컨트롤러에서 매핑한대로 페이지가 뜨는 것이다.

 

 

 

   Ex) 권한 없는 페이지에 접속한 경우 login 페이지로 이동시키는 방법

 

  • configure() 메서드의 뒤쪽에 아래 코드를 추가한다.
    • 추가하고 나면 권한이 없는 페이지 user, admin, manager 같은 페이지에 접속할 때 login 페이지로 이동한다.
.formLogin()
.loginPage("/login");

 

  • 추가 후
<hide/>
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.authorizeRequests()
        .antMatchers("/user/**").authenticated()
        .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
        .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
        .anyRequest().permitAll()  // 다른 요청은 전부 허용한다.
        .and()
        .formLogin()
        .loginPage("/login");
}

 

 

 

1.3 시큐리티 회원가입

  • login()
<hide/>
@GetMapping("/login")
public  String login(){
    return "loginForm";
}
<hide/>
<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>로그인 페이지</title>
</head>
<body>
  <h1>로그인 페이지입니다.</h1>
  <hr/>
<form>
  <input type="text" name="username" placeholder="Username"/></br>
  <input type="text" name="password" placeholder="Password"/></br>
  <button>로그인</button>
</form>
</body>
</html>

 

  Note) 실행 결과

 

  • DESC USER

  

 

 

  Ex) 회원 가입

<hide/>
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http.authorizeRequests()
        .antMatchers("/user/**").authenticated()
        .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
        .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
        .anyRequest().permitAll()  // 다른 요청은 전부 허용한다.
        .and()
        .formLogin()
        .loginPage("/loginForm");
}

 

  Note) 실행 결과 - localhost: 8080/manager

  • 로그인 창이 나온다.

 

  • 로그인에 하이퍼링크 추가
<a href="/joinForm">회원 가입을 아직 하지 않으셨나요?</a>

 

  • 회원 가입 창
    • 가입 폼을 전송할 거니까 method ="post"
    • "/join"으로 이동한다.

 

  • User
    • @CreationTimeStamp: 객체가 생성될 때 자동으로 시간이 채워진다.
<hide/>
@Entity
@Setter
@Getter
public class User {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String username;
    private String password;
    private String email;
    private String role;

    @CreationTimestamp
    private Timestamp createDate;
}

 

  • 컨트롤러 
    • 매개변수로 User을 넣으니까 입력한 회원 가입 창에 입력한 정보가 자동으로 저장된다?
    • view 페이지에서 사용자가 입력하는 값은 @PostMapping이 붙은 메서드의 매개변수로 통해 전달된다.
      • 아래의 user에 해당한다.
<hide/>
@GetMapping("/joinForm")
public  String joinForm(){
    return "joinForm";
}

// 가입 시킨다.
@PostMapping("/join")
public @ResponseBody String join(User user){
    System.out.println(user);
    return "join";
}

 

 

  Note) 실행 결과

    • 다음과 같이 출력된다.
    • com.example.security.model.User@2bfdbb41

 

  • repository
    • CRUD 함수를 JPA repo가 가지고 있다.
    • JpaRepo를 상속받았기 때문에 UserRepo에 @Repository를 붙이지 않아도 자동으로  IoC가 적용된다. 
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {

}

 

  • 시큐리티로 로그인하려면 패스워드가 암호화 되어 있어야한다. => BCryptPasswordEncoder을 반환하는 encodePwd() 빈을 만든다.
  • 보안 설정 파일에 추가한다.
    • @Bean을 추가하면 해당 메서드의 반환 객체를 IoC로 등록한다. 이 빈을 어디서든 쓸 수 있다.
    • BCryptPasswordEncoder는 시큐리티에서 제공하는 클래스로 비밀번호를 암호화(해시)하는데 이용한다.
@Bean
public BCryptPasswordEncoder encodePwd(){
    return new BCryptPasswordEncoder();
}

 

  • 컨트롤러
    • join()에 암호화를 적용해서 다음과 같이 바꾼다.
<hide/>
@PostMapping("/join")
public String join(User user){
    System.out.println(user);
    user.setRole("ROLE_USER");
    String rawPassword = user.getPassword();
    String encPassword = bCryptPasswordEncoder.encode(rawPassword);
    user.setPassword(encPassword);
    userRepository.save(user);
    return "redirect:/loginForm";
}

 

  • 회원 가입하면 아래와 같이 정보가 잘 들어간다.

 

 

1.4 시큐리티 로그인

 

1.5 시큐리티 권한 처리

 

 

확인 사항

  • implement와 extend 차이는?
    • extend를 통해 abstract class를 상속하면 부모 클래스의 멤버 변수까지 이용 가능하다. 부모 클래스의 특징을  연장해서 사용한다.
      • 자식 객체는 오버라이딩할 필요없이 부모의 변수, 메서드를 그대로 사용 가능
    • implement는 부모의 특징을 도구로 사용해서 새로운 특징을 만들어 사용한다.
      • 부모 객체(인터페이스)는 메서드 선언만 할 수 있고 구현을 자식 객체에서 구현이 가능하다.
      • 부모 객체에 선언된 메서드를 반드시 자식 객체에서 구현해야한다.
  • prefix(접두사), suffix(접미사)

 

 

출처 - https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0/dashboard

 

[무료] 스프링부트 시큐리티 & JWT 강의 - 인프런 | 강의

스프링부트 시큐리티에 대한 개념이 잡힙니다., - 강의 소개 | 인프런...

www.inflearn.com