본문 바로가기
프로젝트/SpringSecurity

[Spring Security] 10. JWT를 이용하기위한 security 설정

by dantriss 2023. 7. 11.

유튜브에서 스프링시큐리티를 심화교육하면서 조금더 레벨업 하고자 한다.

IDE : IntelliJ

언어 : Java 8

스프링부트 버전 : 2.7.13

DB : MySQL

빌드관리 도구 : Maven

OS : iOS

참고유튜브 : 메타코딩


JWT(Json Web Token)

https://jwt.io/introduction

정보를 Json 객체로 안전하게 전송하기 위한 방법

구조는 헤더(Header)/페이로드(Payload)/서명(Signature)

 

  • 헤더(Header)

헤더에는 해시알고리즘과 토큰의 유형이 표시 되어있다.

 

 

  • 페이로드(Payload)

페이로드에는 데이터(정보)들이 들어가 있다.

 

  • 서명(Signature)

서명부분의 구조는 header.payload, 비밀키 로 되어있다.

JWT를 이용해서 로그인을 진행 한 후 개인정보페이지로 이동했을때의 진행과정은

1. 클라이언트 측 로그인요청

2. 서버 JWT생성 후 클라이언트 측으로 응답

3. 클라이언트 측 개인정보페이지 요청, 헤더에 JWT 첨부하여 서버측으로 전달

4. 전달받은 JWT를 이용하여 사용자 검증완료

5. 페이로드에 있는 회원정보로 db에서 검색 후 개인정보페이지 응답

 


 

JWT(Json Web Token)을 활용해보기 위해서 새로운 프로젝트를 생성한다.

 

mvnrepository에서 JWT 디펜더시도 추가

https://mvnrepository.com/artifact/com.auth0/java-jwt

 

pom.xml에 추가한다.

pom.xml

 

application.properties 를 yml으로 변경

 

application.yml에 아래내용 추가

server:
  port: 8089
  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: 'root'
    password: '1234'

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

 

controller패키지에 RestApiController 클래스 추가

 

기존방법인 form로그인방식을 확인하기 위해서 간단한 주소 작성 

@GetMapping("/home")
public String home(){

    return "home";
}

 

서버를 실행하면 콘솔에 비밀번호가 나오게된다.

 

username은 user

password는 콘솔에 나온 비밀번호를 로그인해야 /home으로 이동할 수 있다.

 

로그인 후 home으로 이동한 모습

 

그 후 db에 연결을 해준다.

IntelliJ db에 연결하기

 

스키마이름 Jwt으로 생성 후 db연결 후 IntelliJ에서 jwt스키마로 변경을 해준다.

 

DB로 이동해서 jwt를 생성해준다.

 

IntelliJ DB연결 및 스키마 변경하기

 

model 패키지에 User 모델 클래스 생성

 

아래와 같이 추가해준다.

@Entity
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;
    private String username;
    private String password;
    private String roles;

    public List<String> getRoleList(){
        if (this.roles.length()>0){
            return Arrays.asList(this.roles.split(","));
        }

        return new ArrayList<>();
    }

}

 

서버를 실행해주면 db에 user 테이블이 생성이 된다.

 

config패키지 생성 후 CorsConfig와 SecurityConfig 생성

 

CorsConfig에 규칙을 생성해낸다.

config.setAllowedOrigin("*") -> 모든 ip에 대해서 응답 허용

config.setAllowedHeader("*") -> 모든 header에 응답 허용

config.setAllowedMethod("*") -> 모든 post, get, patch, delete 요청 허용

source.registerCorsConfiguration("/api/**", config) -> /api로 들어오는 주소는 모드 해당 규칙을 따라야한다

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);                                  
        config.addAllowedOrigin("*");                                      
        config.addAllowedHeader("*") ;                                     
        config.addAllowedMethod("*");                                      
        source.registerCorsConfiguration("/api/**", config);       
        return new CorsFilter(source);
    }

}

 

 

SecurityConfig에서 .formLogin().disable과 HttpBasic().disable()로 기본적인 Http 로그인과 form 로그인방식을 사용하지 않게 설정해준다.

addFilter로 CorsConfig에서 만든 필터설정을 넣어준다.

( 여기서 바보같이 return null로 설정해놓고 해당 필터가 작동이 되지 않아서 왜 안되는지 찾아보느냐 시간이 좀 걸렸다... )

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    
    private final CorsConfig corsConfig;


    @Bean
    public SecurityFilterChain configure(HttpSecurity http) throws Exception{

        http.csrf().disable()
                .addFilter(corsConfig.corsFilter())
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .formLogin().disable()
                .httpBasic().disable()
                .authorizeRequests()

                .antMatchers("/api/v1/user/**")
                .access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')or hasRole('ROLE_MANAGER')")

                .antMatchers("/api/v1/manager/**")
                .access("hasRole('ROLE_MANAGER') or hasRole('ROLE_ADMIN')")

                .antMatchers("/api/v1/admin/**")
                .access("hasRole('ROLE_ADMIN')")

                .anyRequest().permitAll();

        return http.build();

    }
}

 

서버를 실행해서 /home으로 이동해보면 로그인을 하라는 창이 나오지 않고 이동이 가능하다.

 

이미 로그인이 되어서 이동이 가능한거 아니야? 

그렇다면 SecurityConfig에서 설정한 권한들이 있어야 특정 주소로 이동할 수 있게 만들었으니 해당 주소로 이동을 해보자

403 에러가 발생하면서 접근이 제한되었다.

제대로 잘 작동하는걸 확인할 수 있다.

댓글