Java

[Java] 3. JWT와 bcrypt 적용하기

hyun_ji 2021. 8. 22. 00:48
반응형
SMALL

JWT와 bcrypt를 사용하기 위해 pom.xml에 

// bcrypt관련
<dependency>
 <groupId>org.springframework.security</groupId>
 <artifactId>spring-security-web</artifactId>
 <version>5.5.2</version>
</dependency>
    
 // JWT관련   
<dependency> 
 <groupId>io.jsonwebtoken</groupId> 
 <artifactId>jjwt</artifactId> 
 <version>0.9.1</version> 
</dependency>
<dependency>
 <groupId>com.googlecode.json-simple</groupId>
 <artifactId>json-simple</artifactId>
 <version>1.1.1</version>
</dependency>

이 내용을 추가합니다.

 

자바 새 버전에서는 이것도 추가해야 jwt오류가 안난다네요.

	<dependency>
		<groupId>org.glassfish.jaxb</groupId>
		<artifactId>jaxb-runtime</artifactId>
		<version>2.3.2</version>
	</dependency>

 

JWT와 bcrypt를 사용하기 위해 service 폴더에 JWTManager.java와 Bcrypt.java 파일을 만들어줍니다.

 

Bcrypt부터 진행하겠습니다.

Bcrypt.java

package daily.coding.service;

import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class Bcrypt {

  public String HashPassword(String password) {

    BCryptPasswordEncoder ecnoder = new BCryptPasswordEncoder();
    String hash = ecnoder.encode(password);
    return hash;
  }

  public Boolean CompareHash(String password, String DBpassword) {

    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

    if (encoder.matches(password, DBpassword)) {
      return true;
    } else {
      return false;
    }
  }


}

ExController.java

package daily.coding.controller; // 자기 위치를 명시

import daily.coding.models.*;
import daily.coding.mapper.*;
import daily.coding.service.*;

// 프레임워크에 탑재된 기능 가져오기
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

// 자바 가지고 있는 내장 모듈
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.Iterator;

@RestController // restApi를 작성할 수 있는 컨트롤러
@RequestMapping("/api") // url을 api로 지정
@CrossOrigin(origins="*", allowedHeaders="*") // cors허용
public class ExController {

  private UserMapper userMapper;
  private Bcrypt bcrypt;

  public ExController(UserMapper userMapper, Bcrypt bcrypt) {
    this.userMapper = userMapper;
    this.bcrypt = bcrypt;
  }

  // create
  @PostMapping("/user") 
  public ResponseEntity<Map<String,String>> CreateUser(@RequestBody User req) {
    String hashpassword = bcrypt.HashPassword(req.getPassword());
    req.setPassword(hashpassword);
    userMapper.Create(req);
    Map<String,String> map = new HashMap<>();
    map.put("result", "success");

    return new ResponseEntity<>(map, HttpStatus.OK);
  }

  // read
  @GetMapping("/users")
  public List<User> AllUser() {
    return userMapper.findAll();
  }

  // update
  @PostMapping("/update") 
  public ResponseEntity<Map<String,String>> UpdateUser(@RequestBody User req) {
    userMapper.Update(req);
    
    Map<String,String> map = new HashMap<>();
    map.put("result", "success");

    return new ResponseEntity<>(map, HttpStatus.OK);
  }

  // delete
  @PostMapping("/delete") 
  public ResponseEntity<Map<String,String>> DeleteUser(@RequestBody User req) {
    userMapper.Delete(req);
    
    Map<String,String> map = new HashMap<>();
    map.put("result", "success");

    return new ResponseEntity<>(map, HttpStatus.OK);
  }


  @GetMapping("/hello") // get /api/hello
  public ResponseEntity<Map<String,String>> Hello() { // ResponseEntity 리턴타입 Map 키와 값을 하나의 쌍으로 저장
    Map<String,String> map = new HashMap<>(); // map 선언, HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션
    map.put("result", "hello world"); // map에 값 넣기

    return new ResponseEntity<>(map, HttpStatus.OK); // 생성자로 ResponseEntity를 만들어서 map이랑 status클라이언트에 보내줌
  }

}

DemoApplication.java

package daily.coding.demo;

import daily.coding.models.*;
import daily.coding.service.*;
import daily.coding.controller.ExController;

import org.apache.ibatis.type.MappedTypes;
import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan; //컴포넌트 스캔 안하면 컨트롤러 못 읽음

@MappedTypes(User.class)
@MapperScan("daily.coding.mapper")
@SpringBootApplication
@ComponentScan(basePackageClasses={ExController.class, Bcrypt.class}) //컨트롤러 읽기
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

위의 내용을 추가해주시면 됩니다.

성공적으로 서버가 켜졌고 이제 postman으로 확인해보겠습니다.

db에 제대로 암호화가 적용되서 저장되었네요.

로그인 할 때, 해쉬 패스워드와 입력받은 패스워드를 비교하여 로그인을 성공시켜주는 로직도 작성하겠습니다.

그러기 위해선 디비에서 가져온 패스워드를 담을 모델도 필요하겠죠.

models안에 ReqUser.java 파일을 생성하여 아래와 같이 작성합니다.

package daily.coding.models;

public class ReqUser {

  private String id;
  private String password;

  public ReqUser() {
    super();
  }

  public ReqUser(String id, String password) {
    super();
    this.id = id;
    this.password = password;
  }


  public String getId() {
    return id;
  }

  public String getPassword() {
    return password;
  }

  public void setId(String id) {
    this.id = id;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  

}

이제 ExController.java 안에 아래와 같은 로직을 추가하고 포스트맨으로 확인해보겠습니다.

 @PostMapping("/login")
  public ResponseEntity<Map<String, String>> Login(@RequestBody ReqUser req) {
    User user = new User();
    user = userMapper.findOne(req.getId());
    Boolean result = bcrypt.CompareHash(req.getPassword(), user.getPassword());
    Map<String,String> map = new HashMap<>();

    if (result) {
      map.put("result", "success");
      return new ResponseEntity<>(map, HttpStatus.OK);
      } else {
          map.put("result", "password is not correct");
          return new ResponseEntity<>(map, HttpStatus.OK);        
      }
  }

그럼 비밀번호가 일치하면 success가 나올것이고 일치하지 않으면 password is not correct가 나올겁니다.

 

성공적이네요.

 

jwt도 적용방식은 똑같습니다.

JWTManager.java

package daily.coding.service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts; 
import io.jsonwebtoken.SignatureAlgorithm; 
import org.springframework.stereotype.Service; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.Map;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.DefaultJwtSignatureValidator;

import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

import static io.jsonwebtoken.SignatureAlgorithm.HS256;


@Service
public class JWTManager {
 
    private final String AccessKey = "AccessKey_SECRET";
    private final Long expiredTime = 1000 * 60L * 60L * 24L * 365L; // 1년

    ///////////////create////////////////////////////

    public String CreateToken(String username) {
        Date now = new Date();

        return Jwts.builder()
            .setSubject(username)
            .setHeader(createHeader())
            .setClaims(createClaims(username))
            .setExpiration(new Date(now.getTime() + expiredTime))
            .signWith(SignatureAlgorithm.HS256, AccessKey)
            .compact();
    }

    private Map<String, Object> createHeader() {
        Map<String, Object> header = new HashMap<>();
        header.put("typ", "JWT");
        header.put("alg", "HS256");
        header.put("regDate", System.currentTimeMillis());
        return header;
    }

    private Map<String, Object> createClaims(String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", username);
        return claims;
    }

    /////////////////decoded////////////////////////

    private Claims getClaims(String token) {
        //return Jwts.parser().setSigningKey(Base64.getEncoder().encodeToString(AccessKey.getBytes())).parseClaimsJws(token).getBody();
        return Jwts.parser().setSigningKey(AccessKey).parseClaimsJws(token).getBody();
    }

    public String VerifyToken(String token) {
        return (String) getClaims(token).get("username");
    }
}

DemoApplication.java

package daily.coding.demo;

import daily.coding.models.*;
import daily.coding.service.*;
import daily.coding.controller.ExController;

import org.apache.ibatis.type.MappedTypes;
import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan; //컴포넌트 스캔 안하면 컨트롤러 못 읽음

@MappedTypes(User.class)
@MapperScan("daily.coding.mapper")
@SpringBootApplication
@ComponentScan(basePackageClasses={ExController.class, Bcrypt.class, JWTManager.class}) //컨트롤러 읽기
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

이제 ExController.java의 로그인 로직에 jwt를 추가합니다.

로그인이 성공할 시, 토큰을 발급해주는 로직만 추가해주면 됩니다.

결과값으로 토큰을 받도록 하겠습니다.

로그인 로직은 아래와 같이 될 것이고,

@PostMapping("/login")
  public ResponseEntity<Map<String, String>> Login(@RequestBody ReqUser req) {
    User user = new User();
    user = userMapper.findOne(req.getId());
    Boolean result = bcrypt.CompareHash(req.getPassword(), user.getPassword());
    Map<String,String> map = new HashMap<>();

    if (result) {
      String token = jwt.CreateToken(user.getId());

      map.put("result", token);
      return new ResponseEntity<>(map, HttpStatus.OK);
      } else {
          map.put("result", "password is not correct");
          return new ResponseEntity<>(map, HttpStatus.OK);        
      }
  }

전체코드로 보면 이렇습니다.

package daily.coding.controller; // 자기 위치를 명시

import daily.coding.models.*;
import daily.coding.mapper.*;
import daily.coding.service.*;

// 프레임워크에 탑재된 기능 가져오기
import org.springframework.web.bind.annotation.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

// 자바 가지고 있는 내장 모듈
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.Iterator;

@RestController // restApi를 작성할 수 있는 컨트롤러
@RequestMapping("/api") // url을 api로 지정
@CrossOrigin(origins="*", allowedHeaders="*") // cors허용
public class ExController {

  private UserMapper userMapper;
  private Bcrypt bcrypt;
  private JWTManager jwt;

  public ExController(UserMapper userMapper, JWTManager jwt, Bcrypt bcrypt) {
    this.userMapper = userMapper;
    this.bcrypt = bcrypt;
    this.jwt = jwt;
  }

  // create
  @PostMapping("/user") 
  public ResponseEntity<Map<String,String>> CreateUser(@RequestBody User req) {
    String hashpassword = bcrypt.HashPassword(req.getPassword());
    req.setPassword(hashpassword);
    userMapper.Create(req);
    Map<String,String> map = new HashMap<>();
    map.put("result", "success");

    return new ResponseEntity<>(map, HttpStatus.OK);
  }

  // read
  @GetMapping("/users")
  public List<User> AllUser() {
    return userMapper.findAll();
  }

  @PostMapping("/login")
  public ResponseEntity<Map<String, String>> Login(@RequestBody ReqUser req) {
    User user = new User();
    user = userMapper.findOne(req.getId());
    Boolean result = bcrypt.CompareHash(req.getPassword(), user.getPassword());
    Map<String,String> map = new HashMap<>();

    if (result) {
      String token = jwt.CreateToken(user.getId());

      map.put("result", token);
      return new ResponseEntity<>(map, HttpStatus.OK);
      } else {
          map.put("result", "password is not correct");
          return new ResponseEntity<>(map, HttpStatus.OK);        
      }
  }

  // update
  @PostMapping("/update") 
  public ResponseEntity<Map<String,String>> UpdateUser(@RequestBody User req) {
    userMapper.Update(req);
    
    Map<String,String> map = new HashMap<>();
    map.put("result", "success");

    return new ResponseEntity<>(map, HttpStatus.OK);
  }

  // delete
  @PostMapping("/delete") 
  public ResponseEntity<Map<String,String>> DeleteUser(@RequestBody User req) {
    userMapper.Delete(req);
    
    Map<String,String> map = new HashMap<>();
    map.put("result", "success");

    return new ResponseEntity<>(map, HttpStatus.OK);
  }


  @GetMapping("/hello") // get /api/hello
  public ResponseEntity<Map<String,String>> Hello() { // ResponseEntity 리턴타입 Map 키와 값을 하나의 쌍으로 저장
    Map<String,String> map = new HashMap<>(); // map 선언, HashMap은 Map 인터페이스를 구현한 대표적인 Map 컬렉션
    map.put("result", "hello world"); // map에 값 넣기

    return new ResponseEntity<>(map, HttpStatus.OK); // 생성자로 ResponseEntity를 만들어서 map이랑 status클라이언트에 보내줌
  }

}

포스트맨으로 확인해보면

성공적이네요~!^_^

반응형
LIST