새소식

반응형
250x250
My Study/Java

신입 개발자의 기록 [11/29 ~ 12/04]

  • -
728x90
반응형

JWT 토큰 조사 및 구현하기

 

이번에는 JWT를 조사하고 JWT 토큰의 발급 및 사용, 재발급을 구현해 보았다.

JWT 토큰은 학원에서 프로젝트 때 잠깐 써본 것밖에 없어서 살짝 어려움이 있었지만 나름 재미있던 것 같다.

 

먼저 JWT 사용을 위한 라이브러리를 추가해준다.

		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
            <scope>runtime</scope>
        </dependency>

 

application.yml 에 jwt 설정도 해준다.

jwt:
  secret: nOmi70nNMA6ORMsiQC2035fgrsuguUT0ccrE4mL4HwQyy1abKXUphnEiKkn5Vzg1Bfs9i9k+dq3uO3yFAIE08Q==

 

그 뒤 member 관련 DTO들을 생성해준다.

(MemberDTO, AuthorityDTO, MemberRoleDTO)

 

JWT 관련 클래스들도 생성을 해준다.

public TokenDTO generateTokenDTO(MemberDTO member) {
        List<String> roles = new ArrayList<>();
        for(MemberRoleDTO memberRole : member.getMemberRole()) {
            roles.add(memberRole.getAuthority().getAuthorityName());
        }
        /* 1. 회원 아이디를 "sub"라는 클레임으로 토큰에 추가 */
        Claims claims = Jwts.claims().setSubject(member.getMemberId());

        /* 2. 회원의 권한들을 "auth"라는 클레임으로 토큰에 추가 */
        claims.put(AUTHORITIES_KEY, roles);

        long now = System.currentTimeMillis();      // 현재 시각을 ms 단위로 가져옴

        Date accessTokenExpiresIn = new Date(now + ACCESS_TOKEN_EXPIRE_TIME);
        Date refreshTokenExpiresIn = new Date(now + REFRESH_TOKEN_EXPIRE_TIME);
        String accessToken = Jwts.builder()
                .setClaims(claims)
                .setExpiration(accessTokenExpiresIn)    // 토큰의 만료기간을 DATE형으로 토큰에 추가
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();

        String refreshToken = Jwts.builder()
                .setClaims(claims)
                .setExpiration(refreshTokenExpiresIn)
                .signWith(key, SignatureAlgorithm.HS512)
                .compact();

        return new TokenDTO(BEARER_TYPE, member.getMemberCode(), accessToken, refreshToken, accessTokenExpiresIn.getTime());
    }

TokenProvider -> token 생성 메소드 설정

accessToken과 refreshToken을 둘 다 발급하고 TokenDTO에 담아 반환을 하면

Front 쪽에서 토큰에서 accessToken과 refreshToken을 꺼내 쿠키에 저장한다.

 

public Authentication getAuthentication(String token) {

        Claims claims = parseClaims(token);

        if(claims.get(AUTHORITIES_KEY) == null) {
            throw new RuntimeException("권한 정보가 없는 토큰입니다.");
        }

        UserDetails userDetails = userDetailsService.loadUserByUsername(this.getMemberId(token));

        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

TokenProvider -> accessToken으로 인증 객체 추출 메소드

 

public TokenDTO login(MemberDTO member) {

        MemberDTO mem = memberMapper.findMemberById(member.getMemberId());
        TokenDTO token = new TokenDTO();

        if(mem != null && passwordEncoder.matches(member.getPassword(), mem.getPassword())){
            int memberCode = mem.getMemberCode();
            List<MemberRoleDTO> memberRoleList = memberMapper.getAuthList(memberCode);
            System.out.println(memberRoleList);
            for(MemberRoleDTO memberRole : memberRoleList) {
                AuthorityDTO auth = memberMapper.findAuth(memberRole.getAuthorityCode());
                memberRole.setAuthority(auth);
            }
            member.setMemberRole(memberRoleList);

            token = tokenProvider.generateTokenDTO(member);
            Map<String, Object> map = new HashMap<>();
            map.put("refreshToken", token.getRefreshToken());
            map.put("memberCode", memberCode);
            memberMapper.refreshToken(map);
            token.setMemberCode(mem.getMemberCode());
        }

        System.out.println(token);
        return token;
    }

로그인 시 토큰 생성하는 로직

MemberId로 가져온 MemberDTO 객체가 null이 아니고 비밀번호가 맞으면

MemberRole을 가져와 MemberDTO에 넣어준다. 그 값을 토대로 토큰을 발급하고

refreshToken을 회원 DB에 저장한다.

 

public Object check(String token) {

        /* 토큰의 유효성 검사 (만료되면 false 를 반환) */
        boolean validation = tokenProvider.validateToken(token);

        if(validation == true) {
            return true;
        } else {
            Claims auth = tokenProvider.getAuth(token);
            String refreshToken = memberMapper.findToken(auth.getSubject());
            /* 리프레시 토큰의 만료상태 검사 (만료 시 false반환) */
            boolean refresh = tokenProvider.validateToken(refreshToken);
            if(refresh == true) {
                String accessToken = tokenProvider.accessToken(refreshToken);
                return accessToken;
            }
        }
        return false;
    }

토큰의 유효성 검사를 하는 메소드

accessToken의 유효성을 검사해 만료되었으면 accessToken 값으로 Claims을 추출해

refreshToken을 가져온다. refreshToken을 가져와 유효성 검사를 하고

refreshToken이 만료되지 않았을 땐 다시 accessToken을 발급하고 

만료되었을 때에는 모든 토큰을 다시 재발급한다.

 

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        MemberDTO member = memberMapper.findMemberByMemberId(username);

        List<MemberRoleDTO> memberRoleList = memberMapper.getAuthList(member.getMemberCode());
        System.out.println(memberRoleList);
        for(MemberRoleDTO memberRole : memberRoleList) {
            AuthorityDTO auth = memberMapper.findAuth(memberRole.getAuthorityCode());
            memberRole.setAuthority(auth);
        }
        member.setMemberRole(memberRoleList);

        List<GrantedAuthority> authorities = new ArrayList<>();
        for(MemberRoleDTO memberRole : member.getMemberRole()) {
            String authorityName = memberRole.getAuthority().getAuthorityName();
            authorities.add(new SimpleGrantedAuthority(authorityName));
            member.setAuthorities(authorities);
        }
        return member;
    }

userDetails에서 권한 설정해주는 메소드

 

이런 식으로 구현을 해 토큰의 발급, 사용 및 재발급까지 경험해볼 수 있었다.

728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.