20221021 [Spring Boot, JWT]

Yeoonnii·2022년 10월 21일
0

TIL

목록 보기
52/52
post-thumbnail

JWT 토큰발행

jwt/JwtUtil.java

로그인 시 토큰/발행 생성 위해 jwtutil사용하여 토큰 발행

package com.example.jwt;


import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.json.JSONObject;
import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;


// jjwt
// 토큰을 생성하고, 토큰의 정보를 추출 또는 만료시간 검증

@Component
public class JwtUtil {

    // 토큰 생성용 보안키
    private final String SECRETKEY = "gyuf6r567546sd546e56";

    // 정보 추출용 메소드
    private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = Jwts.parser().setSigningKey(SECRETKEY).parseClaimsJws(token).getBody();
        return claimsResolver.apply(claims);
    }

    // 토큰 생성(아이디 정보를 이용한 토큰 생성)
    // 아이디만포함. json -> string -> 토큰 -> string -> json
    // {"uid":"aaa", "role":"CUSTOMER"}
    public String generateToken(String username, String role) {
        // 1. jsonobject로 변환
        JSONObject jobj = new JSONObject();
        jobj.put("username", username);
        jobj.put("role", role);

        // ex) 30분 => 1000 * 60 * 30
        long tokenValidTime = 1000 * 60 * 60 * 4; // 4시간

        // 2. 문자형태로 추가한 후 토큰 생성
        Map<String, Object> claims = new HashMap<>();
        String token = Jwts.builder().setClaims(claims)
                .setSubject(jobj.toString())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + tokenValidTime))
                .signWith(SignatureAlgorithm.HS256, SECRETKEY).compact();

        return token;
    }

    // 토큰 검증
    public Boolean validateToken(String token, String userid) {
        // 토큰에서 아이디 정보 추출
        final String username = this.extractUsername(token);
        if (username.equals(userid) && !isTokenExpired(token)) {
            return true;
        }
        return false;
    }

    // 토큰에서 아이디 정보 추출하기
    public String extractUsername(String token) {
        String sObj = extractClaim(token, Claims::getSubject);
        JSONObject jobj = new JSONObject(sObj);
        return jobj.getString("username");
    }

    // 토큰에서 권한 정보 정보 추출하기
    public String extractRole(String token) {
        String sObj = extractClaim(token, Claims::getSubject);
        JSONObject jobj = new JSONObject(sObj);
        return jobj.getString("role");
    }

    // 토큰에서 만료 시간 추출하기
    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    // 토큰의 만료시간이 유효한지 확인
    public Boolean isTokenExpired(String token) {
        // 만료시간 가져와서 현재시간보다 이전인지 확인
        return this.extractExpiration(token).before(new Date());
    }

}

FilterConfig.java

  • 로그인, 회원가입 ⇒ 토큰이 필요하지 않음
  • 로그아웃, 회원 조회 , 회원정보 수정, 회원 탈퇴 ⇒ 토큰이 필요

토큰을 전송해서 검증 후에 수행되어야 하는 주소들
토큰 검증 완료 = 로그인이 완료되었다는것

로그아웃, 회원 한명 조회 , 회원정보 수정, 회원 탈퇴
⇒ 위 4개는 필터를 통해 토큰 검증이 완료된 후 수행되어야 한다

@component를 붙이면 = 모두 filter를 통과한다
⇒ filter가 필요한 주소만 지정하기 위해 filterConfig 생성하여 filter환경설정을 해준다

filterConfig 에서 filter 무시할 주소 설정

package com.example.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.example.jwt.JwtFilter;

// 필터를 통과할 url설정하기
@Configuration
public class FilterConfig {

    @Bean
    // <JwtFilter> => 사용할 필터 명시
    public FilterRegistrationBean<JwtFilter> 
        filterRegistrationBean(JwtFilter jwtFilter){

        FilterRegistrationBean<JwtFilter> bean 
            = new FilterRegistrationBean<>();            
        // bean.setFilter(jwtFilter); => 아래 설정한 bean.addUrlPatterns에 적용할 필터 설정
            bean.setFilter(jwtFilter);    

        // ex) 필터를 통과시킬 URL설정하기
        // 하위 모든 주소일 경우 * 사용!
        // "/api/board/*"

        bean.addUrlPatterns(
            "/api/member/update.json", 
            "/api/member/logout.json", 
            "/api/member/delete.json", 
            "/api/member/selectone.json"
            );

        return bean;
    }

}

MemberRestController.java

package com.example.restcontroller;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.entity.ExMember;
import com.example.jwt.JwtUtil;
import com.example.repository.ExMemberRepository;

import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping(value = "/api/member")
@RequiredArgsConstructor
public class MemberRestController {

    // 환경설정에서 미리 생성된 bean객체 가져오기 = autowired = 주입
    // SecurityConfig.java 에서 생성한 암호화 bean객체 가져오기
    final PasswordEncoder passwordEncoder;
    final AuthenticationManager authenticationManager;
    final ExMemberRepository exMemberRepository;
    final JwtUtil jwtUtil;

    // 127.0.0.1:8080/ROOT/api/member/join.json
    @PostMapping(value = "/join.json")
    public Map<String, Object> joinPOST(@RequestBody ExMember member){
        Map<String, Object> map = new HashMap<>();
        try {
            System.out.println(member.toString());
            // 회원가입시 사용자가 입력한 암호가져와서 암호화 
            member.setPw(passwordEncoder.encode(member.getPw()));
            exMemberRepository.save(member);
            map.put("status", 200);
            
            
        } catch (Exception e) {
            map.put("status", -1);
            map.put("result", e.getMessage());

        }
        return map;
    }
    
    
    // 127.0.0.1:8080/ROOT/api/member/logout.json
    @PostMapping(value = "/logout.json")
    public Map<String, Object> logoutPOST(){
        Map<String, Object> map = new HashMap<>();
        try {
            
        } catch (Exception e) {
            e.printStackTrace();

        }
        return map;
    }

    // 127.0.0.1:8080/ROOT/api/member/selectone.json
    @GetMapping(value = "/selectone.json")
    public Map<String, Object> selectoneGET(HttpServletRequest request){
        Map<String, Object> map = new HashMap<>();
        try {
            // 토큰 검증 후에 추출하는 사용자의 아이디 정보
            String id = (String) request.getAttribute("username");
            ExMember member = exMemberRepository.findById(id).orElse(null);
            
            map.put("status", 200);
            map.put("result", member);

        } catch (Exception e) {
            e.printStackTrace();

        }
        return map;
    }

    // 127.0.0.1:8080/ROOT/api/member/update.json
    @PostMapping(value = "/update.json")
    public Map<String, Object> updatePUT(){
        Map<String, Object> map = new HashMap<>();
        try{
        }
        catch(Exception e){
        }
        return map;
    }

    // 127.0.0.1:8080/ROOT/api/member/delete.json
    @PostMapping(value = "/delete.json")
    public Map<String, Object> deleteDELETE(){
        Map<String, Object> map = new HashMap<>();
        try{
        }
        catch(Exception e){
        }
        return map;
    }

    // 로그인하기
    // rest사용할거면 DetailUserService 사용할 필요 없움
    // 127.0.0.1:8080/ROOT/api/member/login.json
    // ex) {"id" : "aaa", "pw" : "aaa", "role" : "CUSTOMER"}
    @PostMapping(value = "/login.json")
    public Map<String, Object> loginPOST(@RequestBody ExMember member){
        Map<String, Object> map = new HashMap<>();
        try {
            System.out.println(member.toString());
            
            // 아이디를 이용하여 DB의 기존 회원정보 가져오기
            ExMember retMember = exMemberRepository.findById(member.getId()).orElse(null);
            
            if(retMember != null){
                // Hash 되지 않은 암호와 DB에서 꺼낸 HASH된 암호 비교
                if(passwordEncoder.matches(member.getPw(), retMember.getPw())){
                
                // 카카오 같은 간편로그인은 토큰이 2개 발행된다
                // 일반 토큰, refresh토큰
                map.put("status", 200);
                map.put("result", jwtUtil.generateToken(member.getId(), member.getRole()));
                }
            }
            else{
                map.put("status", -1);
                map.put("result", "password or username not found");
            }
            
        } catch (Exception e) {
            map.put("status", -1);
            map.put("result", e.getMessage());

        }
        return map;
    }

    
}

0개의 댓글