[TIL] 22.12.15 JWT 회원가입, 로그인 구현

hyewon jeong·2022년 12월 16일
0

TIL

목록 보기
2/138
post-thumbnail

출처 :https://www.tutorialspoint.com/jsp/jsp_architecture.htm

🎈위의 그림설명

  1. 클라이언트가 어떤 동작을 함으로써 hello.jsp 를 요청하였다.

  2. JSP 컨테이너가 JSP 파일을 읽는다.

  3. JSP 컨테이너가 Generete (변환) 작업을 통해 Servlet ( .java ) 파일을 생성한다.

  4. .java 파일은 다시 .class 파일로 컴파일된다.

  5. Execute (실행) 을통해 HTML 파일을 생성하여 JSP 컨테이너 에게 전달한다.

  6. JSP 는 HTTP 프로토콜을 통해 HTML 페이지를 클라이언트 에게 전달한다.

JSP (JavaServer Pages ) 란 무엇인가?

JSP 란 JavaServer Pages 의 약자이며

HTML 코드에 JAVA 코드를 넣어 동적웹페이지를 생성하는 웹어플리케이션 도구이다.

JSP 가 실행되면 자바 서블릿(Servlet) 으로 변환되며 웹 어플리케이션 서버에서 동작되면서 필요한 기능을 수행하고

그렇게 생성된 데이터를 웹페이지와 함께 클라이언트로 응답한다.

자바 서블릿(Java Servlet)

서블릿이란 웹페이지를 동적으로 생성하기 위해 서버측 프로그램을 말한다.

이는 자바 언어를 기반으로 만들지며 웹 어플리케이션 서버 ( Web Application Sever ) 위에서 컴파일 되고 동작한다.

JSP 와 서블릿

JSP 와 서블릿의 차이점은 결과적으로 하는일은 동일하지만

  • JSP 는 HTML 내부에 JAVA 소스코드가 들어감으로 인해 HTML 코드를 작성하기 간편하다는 장점이있으며

  • 서블릿은 자바코드내에 HTML 코드가 있어서 읽고 쓰기가 굉장히 불편하기 때문에 작업의 효율성이 떨어진다.

하지만 웹을 공부할때 JSP 와 서블릿은 함께 배운다 그건 왜 때문일까..

JSP 로 작성된 프로그램은 서버로 요청시 서블릿(Servlet) 파일로 변환되어 JSP 태그를 분해하고 추출하여 다시 순수한 HTML 를 변환한다.


JSP 기본 내장 객체 중 request 객체는 JSP에서 가장 많이 사용되는 객체입니다.

웹브라우저 사용자인 클라이언트로부터 서버로 요청이 들어오면

서버에서는 HttpServletRequest를 생성하며, 요청정보에 있는 패스로 매핑된 서블릿에게 전달합니다.

이렇게 전달받은 내용들을 파라미터로 Get과 Post 형식으로 클라이언트에게 전달하게 됩니다.

HttpServletRequest

HttpServletRequest를 사용하면, 값을 받아올 수가 있는데

만약 회원 정보를 컨트롤러로 보냈을 때 HttpServletRequest 객체 안에 모든 데이터들이 들어가게 됩니다!

원하는 데이터를 꺼낼때는 HttpServletRequest의 객체 안의 메소드인 getParameter()를 이용하면 됩니다.

(getParameter의 반환타입은 String입니다.)

Request 객체

  • 사용자의 요청에 관련된 정보를 얻기 위해 사용하는객체
  • 클라이언트에서 서버로 보내느 요청을 담고 있는객체
  • 요청범위를 가지며 javas.servlet.http.HttpServletRequest클래스의 한 인스턴스
  • 요청 파라미터와 관련된 메소드들

출처

@RequiredArgsConstructor란?

  • Lombok으로 스프링에서 DI(의존성 주입)의 방법 중에 생성자 주입을 임의의 코드없이 자동으로 설정해주는 어노테이션이다.
  • @RequiredArgsConstructor는 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 줍니다.
  • 새로운 필드를 추가할 때 다시 생성자를 만들어서 관리해야하는 번거로움을 없애준다. (@Autowired를 사용하지 않고 의존성 주입)

@Slf4j 어노테이션

로깅에 대한 추상 레이어를 제공하는 인터페이스의 모음이다. (로깅 Facade)

인터페이스를 사용하여 로깅을 구현하게 되면 좋은 점은 추후에 필요로 의해 로깅 라이브러리를 변경할 때 코드의 변경 없이 가능하다는 점이다.

@Value 어노테이션

import 할경우 Alt+Insert 누를 시 두 개가 나오는데 아래 것으로 해야 에러가 나지
않고 @Value("${jwt.secret.key}") 값을 가지고 온다.

import org.springframework.beans.factory.annotation.Value;

@Component 어노테이션

의존성 주입을 위해 Bean 등록 할때 사용 , 클래스나 메서드 위에 선언 함으로
객체 생성시 빈이 자동생성되어 스프링IoC컨테이너에 저장됨



 if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
            return bearerToken.substring(7);
        } 

- 가지고 온 코드가 있는지 , BEARER_PREFIX로 시작 하는지를 확인한 다음에 substring(7)사용해서 앞에 7글자 를 지워줍니다. (BEARER 이 여섯 글자이고 한칸이 띄어져 있기 때문에 그것은 토큰에 연관이 되지않는 그냥 스트링 값임)
// 토큰 생성
    public String createToken(String username, UserRoleEnum role) {
        Date date = new Date();

        return BEARER_PREFIX +
                Jwts.builder()   //여기서부터 아래까지 실제로 토큰이 만들어지는 부분 
                .setSubject(username) // 유저네임을 넣어줄 공간
                .claim(AUTHORIZATION_KEY, role)
// 사용자권한을 넣어줄 것이고 , 우리가 지정해놓은 OAuth key를 사용해서 넣어 놓을 거임 
             //AUTHORIZATION_KEY : 사용자 권한 값의 key 
                .setExpiration(new Date(date.getTime() + TOKEN_TIME))
//이 토큰을 언제까지 유효하게 가져갈 건지 지정하는 부분
// date.getTime() 현재시간을 가져와서 ..토큰타임을 더하면 
//지금 기준으로 언제까지 가능한지를 만들어 부분 // 지금부터 1시간까지 토큰이 유효함 
                .setIssuedAt(date) //언제 만들어졌는지를 넣어주는 부분 
                .signWith(key, signatureAlgorithm)
//시크릿키를 사용해서 만든 key객체와 그 키 객체를 어떤 알고리즘을 사용해서 암호화할건지를 지정해주는 부분
                .compact();
/// 이게 스트링형식의 JWT 토큰으로 반환이 되어짐 
    }


private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

SignatureAlgorithm Enum이 있는데 여러가지 암호화 알고리즘들이 있음.

그중 HS256을 사용함

  • JWT 검증
// 토큰 검증
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
//사용했던Jwts에 들어있는 parserBuilder()를 통해서우리가 검증을 할건데  
//setSigningKey()에다가 우리가 토큰을 만들 때 사용한 키를 넣어주고 어떠한 토큰을 검증 할건지를 
//이렇게 .parseClaimsJws부분에 토큰을 넣어주면 이 내부적으로 토큰을 검증해줌 
            return true;
        } catch (SecurityException | MalformedJwtException e) {
            log.info("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
        } catch (ExpiredJwtException e) {
            log.info("Expired JWT token, 만료된 JWT token 입니다.");
        } catch (UnsupportedJwtException e) {
            log.info("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
        } catch (IllegalArgumentException e) {
            log.info("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
        }
        return false;
    }

  • JWT 에서 사용자 정보 가져오기
// 토큰에서 사용자 정보 가져오기
    public Claims getUserInfoFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
    }   //getbody 로 그안에 들어있는 정보들을 가지고 올 수 있음

이렇게 사용자 정보를 가지고 오는 것은 이미 앞에서 validateToken() 으로 검증을 했다고 가정을 했기 때문에 이것은 유효한 토큰이다라고 가정해서 트라이 캐치가 없습니다.

//    @PostMapping("/login")
//    public String login(LoginRequestDto loginRequestDto) {
//        userService.login(loginRequestDto);
//        return "redirect:/api/shop";
//    }
    //여기 위에서는 Form태그로 넘어왔기 때문에 저게 모델어트리뮤트 형식으로 받아와져서 리퀘스트 바디를 넣어주지 않았는데
    @ResponseBody
    @PostMapping("/login")
    public String login(@RequestBody LoginRequestDto loginRequestDto, HttpServletResponse response) {
        userService.login(loginRequestDto, response);
        return "success";

        //이제는 ajax에서 body에 값이 넘어오기 때문에 이렇게 @RequestBody를 써주셔야 함

        //HttpServletResponse 는 우리가 HttpRequest에서 헤더가 넘어와 받아오는 것처럼
        //우리도 클라이언트쪾으로 반환 할 때는  이렇게 Response객체를 반환함
        //그래서 이것도 미리 가지고 와서 반환할 response Header에다가 우리가 만들어준 토큰을 넣이주기 위해서
        //이렇게 받아오고 있음


누구든지 토큰 값을 알면 분해 해볼 수 있기 때문에

페이로드에는 패스워드와 같은 중요한 값은 넣으면 안됨

암호화가 됐다고 할수 있는 것은

하단의 시크릿 키를 알아야만이 변조가 가능하기 때문에 암호화가 유효하다라고 볼 수 있음

혹시라도 시크릿 키가 탈취를 당하더라도 우리가 만료시간을 줬기 때문에

그 이후에는 다시 들어오지 못해서 보안상 어느정도 유지가 된다

profile
개발자꿈나무

0개의 댓글